]> Pileus Git - ~andy/gtk/blob - gtk/gtkassistant.c
f43193e68c564b1189d5ec5d1fb434fc476cda00
[~andy/gtk] / gtk / gtkassistant.c
1 /* 
2  * GTK - The GIMP Toolkit
3  * Copyright (C) 1999  Red Hat, Inc.
4  * Copyright (C) 2002  Anders Carlsson <andersca@gnu.org>
5  * Copyright (C) 2003  Matthias Clasen <mclasen@redhat.com>
6  * Copyright (C) 2005  Carlos Garnacho Parro <carlosg@gnome.org>
7  *
8  * All rights reserved.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 #include "config.h"
27
28 #include <atk/atk.h>
29
30 #include "gtkassistant.h"
31
32 #include "gtkaccessible.h"
33 #include "gtkbutton.h"
34 #include "gtkhbox.h"
35 #include "gtkhbbox.h"
36 #include "gtkimage.h"
37 #include "gtklabel.h"
38 #include "gtksizegroup.h"
39 #include "gtkstock.h"
40
41 #include "gtkintl.h"
42 #include "gtkprivate.h"
43 #include "gtkbuildable.h"
44
45 #include "gtkalias.h"
46
47 #define GTK_ASSISTANT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_ASSISTANT, GtkAssistantPrivate))
48
49 #define HEADER_SPACING 12
50 #define ACTION_AREA_SPACING 12
51
52 typedef struct _GtkAssistantPage GtkAssistantPage;
53
54 struct _GtkAssistantPage
55 {
56   GtkWidget *page;
57   GtkAssistantPageType type;
58   guint      complete : 1;
59   guint      complete_set : 1;
60
61   GtkWidget *title;
62   GdkPixbuf *header_image;
63   GdkPixbuf *sidebar_image;
64 };
65
66 struct _GtkAssistantPrivate
67 {
68   GtkWidget *header_image;
69   GtkWidget *sidebar_image;
70
71   GtkWidget *action_area;
72
73   GList     *pages;
74
75   GtkAssistantPage *current_page;
76
77   GSList    *visited_pages;
78
79   GtkSizeGroup *size_group;
80
81   GtkAssistantPageFunc forward_function;
82   gpointer forward_function_data;
83   GDestroyNotify forward_data_destroy;
84 };
85
86 static void     gtk_assistant_class_init         (GtkAssistantClass *class);
87 static void     gtk_assistant_init               (GtkAssistant      *assistant);
88 static void     gtk_assistant_destroy            (GtkObject         *object);
89 static void     gtk_assistant_style_set          (GtkWidget         *widget,
90                                                   GtkStyle          *old_style);
91 static void     gtk_assistant_size_request       (GtkWidget         *widget,
92                                                   GtkRequisition    *requisition);
93 static void     gtk_assistant_size_allocate      (GtkWidget         *widget,
94                                                   GtkAllocation     *allocation);
95 static void     gtk_assistant_map                (GtkWidget         *widget);
96 static void     gtk_assistant_unmap              (GtkWidget         *widget);
97 static gboolean gtk_assistant_delete_event       (GtkWidget         *widget,
98                                                   GdkEventAny       *event);
99 static gboolean gtk_assistant_expose             (GtkWidget         *widget,
100                                                   GdkEventExpose    *event);
101 static gboolean gtk_assistant_focus              (GtkWidget         *widget,
102                                                   GtkDirectionType   direction);
103 static void     gtk_assistant_add                (GtkContainer      *container,
104                                                   GtkWidget         *page);
105 static void     gtk_assistant_remove             (GtkContainer      *container,
106                                                   GtkWidget         *page);
107 static void     gtk_assistant_forall             (GtkContainer      *container,
108                                                   gboolean           include_internals,
109                                                   GtkCallback        callback,
110                                                   gpointer           callback_data);
111 static void     gtk_assistant_set_child_property (GtkContainer      *container,
112                                                   GtkWidget         *child,
113                                                   guint              property_id,
114                                                   const GValue      *value,
115                                                   GParamSpec        *pspec);
116 static void     gtk_assistant_get_child_property (GtkContainer      *container,
117                                                   GtkWidget         *child,
118                                                   guint              property_id,
119                                                   GValue            *value,
120                                                   GParamSpec        *pspec);
121
122 static AtkObject *gtk_assistant_get_accessible   (GtkWidget         *widget);
123
124 static void       gtk_assistant_buildable_interface_init     (GtkBuildableIface *iface);
125 static GObject   *gtk_assistant_buildable_get_internal_child (GtkBuildable  *buildable,
126                                                               GtkBuilder    *builder,
127                                                               const gchar   *childname);
128 static gboolean   gtk_assistant_buildable_custom_tag_start   (GtkBuildable  *buildable,
129                                                               GtkBuilder    *builder,
130                                                               GObject       *child,
131                                                               const gchar   *tagname,
132                                                               GMarkupParser *parser,
133                                                               gpointer      *data);
134 static void       gtk_assistant_buildable_custom_finished    (GtkBuildable  *buildable,
135                                                               GtkBuilder    *builder,
136                                                               GObject       *child,
137                                                               const gchar   *tagname,
138                                                               gpointer       user_data);
139
140 static GList*     find_page                                  (GtkAssistant  *assistant,
141                                                               GtkWidget     *page);
142
143 enum
144 {
145   CHILD_PROP_0,
146   CHILD_PROP_PAGE_TYPE,
147   CHILD_PROP_PAGE_TITLE,
148   CHILD_PROP_PAGE_HEADER_IMAGE,
149   CHILD_PROP_PAGE_SIDEBAR_IMAGE,
150   CHILD_PROP_PAGE_COMPLETE
151 };
152
153 enum
154 {
155   CANCEL,
156   PREPARE,
157   APPLY,
158   CLOSE,
159   LAST_SIGNAL
160 };
161
162 static guint signals [LAST_SIGNAL] = { 0 };
163
164
165 G_DEFINE_TYPE_WITH_CODE (GtkAssistant, gtk_assistant, GTK_TYPE_WINDOW,
166                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
167                                                 gtk_assistant_buildable_interface_init))
168
169
170 static void
171 gtk_assistant_class_init (GtkAssistantClass *class)
172 {
173   GObjectClass *gobject_class;
174   GtkObjectClass *object_class;
175   GtkWidgetClass *widget_class;
176   GtkContainerClass *container_class;
177
178   gobject_class   = (GObjectClass *) class;
179   object_class    = (GtkObjectClass *) class;
180   widget_class    = (GtkWidgetClass *) class;
181   container_class = (GtkContainerClass *) class;
182
183   object_class->destroy = gtk_assistant_destroy;
184
185   widget_class->style_set = gtk_assistant_style_set;
186   widget_class->size_request = gtk_assistant_size_request;
187   widget_class->size_allocate = gtk_assistant_size_allocate;
188   widget_class->map = gtk_assistant_map;
189   widget_class->unmap = gtk_assistant_unmap;
190   widget_class->delete_event = gtk_assistant_delete_event;
191   widget_class->expose_event = gtk_assistant_expose;
192   widget_class->focus = gtk_assistant_focus;
193   widget_class->get_accessible = gtk_assistant_get_accessible;
194
195   container_class->add = gtk_assistant_add;
196   container_class->remove = gtk_assistant_remove;
197   container_class->forall = gtk_assistant_forall;
198   container_class->set_child_property = gtk_assistant_set_child_property;
199   container_class->get_child_property = gtk_assistant_get_child_property;
200
201   /**
202    * GtkAssistant::cancel:
203    * @assistant: the #GtkAssistant
204    *
205    * The ::cancel signal is emitted when then the cancel button is clicked.
206    *
207    * Since: 2.10
208    */
209   signals[CANCEL] =
210     g_signal_new (I_("cancel"),
211                   G_TYPE_FROM_CLASS (gobject_class),
212                   G_SIGNAL_RUN_LAST,
213                   G_STRUCT_OFFSET (GtkAssistantClass, cancel),
214                   NULL, NULL,
215                   g_cclosure_marshal_VOID__VOID,
216                   G_TYPE_NONE, 0);
217     
218   /**
219    * GtkAssistant::prepare:
220    * @assistant: the #GtkAssistant
221    * @page: the current page
222    *
223    * The ::prepare signal is emitted when a new page is set as the assistant's
224    * current page, before making the new page visible. A handler for this signal
225    * can do any preparation which are necessary before showing @page.
226    *
227    * Since: 2.10
228    */
229   signals[PREPARE] =
230     g_signal_new (I_("prepare"),
231                   G_TYPE_FROM_CLASS (gobject_class),
232                   G_SIGNAL_RUN_LAST,
233                   G_STRUCT_OFFSET (GtkAssistantClass, prepare),
234                   NULL, NULL,
235                   g_cclosure_marshal_VOID__OBJECT,
236                   G_TYPE_NONE, 1, GTK_TYPE_WIDGET);
237
238   /**
239    * GtkAssistant::apply:
240    * @assistant: the @GtkAssistant
241    *
242    * The ::apply signal is emitted when the apply button is clicked. The default
243    * behavior of the #GtkAssistant is to switch to the page after the current
244    * page, unless the current page is the last one.
245    *
246    * A handler for the ::apply signal should carry out the actions for which
247    * the wizard has collected data. If the action takes a long time to complete,
248    * you might consider to put a page of type %GTK_ASSISTANT_PAGE_PROGRESS
249    * after the confirmation page and handle this operation within the
250    * #GtkAssistant::prepare signal of the progress page.
251    *
252    * Since: 2.10
253    */
254   signals[APPLY] =
255     g_signal_new (I_("apply"),
256                   G_TYPE_FROM_CLASS (gobject_class),
257                   G_SIGNAL_RUN_LAST,
258                   G_STRUCT_OFFSET (GtkAssistantClass, apply),
259                   NULL, NULL,
260                   g_cclosure_marshal_VOID__VOID,
261                   G_TYPE_NONE, 0);
262
263   /**
264    * GtkAssistant::close:
265    * @assistant: the #GtkAssistant
266    *
267    * The ::close signal is emitted either when the close button of
268    * a summary page is clicked, or when the apply button in the last
269    * page in the flow (of type %GTK_ASSISTANT_PAGE_CONFIRM) is clicked.
270    *
271    * Since: 2.10
272    */
273   signals[CLOSE] =
274     g_signal_new (I_("close"),
275                   G_TYPE_FROM_CLASS (gobject_class),
276                   G_SIGNAL_RUN_LAST,
277                   G_STRUCT_OFFSET (GtkAssistantClass, close),
278                   NULL, NULL,
279                   g_cclosure_marshal_VOID__VOID,
280                   G_TYPE_NONE, 0);
281
282   gtk_widget_class_install_style_property (widget_class,
283                                            g_param_spec_int ("header-padding",
284                                                              P_("Header Padding"),
285                                                              P_("Number of pixels around the header."),
286                                                              0,
287                                                              G_MAXINT,
288                                                              6,
289                                                              GTK_PARAM_READABLE));
290   gtk_widget_class_install_style_property (widget_class,
291                                            g_param_spec_int ("content-padding",
292                                                              P_("Content Padding"),
293                                                              P_("Number of pixels around the content pages."),
294                                                              0,
295                                                              G_MAXINT,
296                                                              1,
297                                                              GTK_PARAM_READABLE));
298
299   /**
300    * GtkAssistant:page-type:
301    *
302    * The type of the assistant page.
303    *
304    * Since: 2.10
305    */
306   gtk_container_class_install_child_property (container_class,
307                                               CHILD_PROP_PAGE_TYPE,
308                                               g_param_spec_enum ("page-type", 
309                                                                  P_("Page type"),
310                                                                  P_("The type of the assistant page"),
311                                                                  GTK_TYPE_ASSISTANT_PAGE_TYPE,
312                                                                  GTK_ASSISTANT_PAGE_CONTENT,
313                                                                  GTK_PARAM_READWRITE));
314
315   /**
316    * GtkAssistant:title:
317    *
318    * The title that is displayed in the page header.
319    *
320    * If title and header-image are both %NULL, no header is displayed.
321    *
322    * Since: 2.10
323    */
324   gtk_container_class_install_child_property (container_class,
325                                               CHILD_PROP_PAGE_TITLE,
326                                               g_param_spec_string ("title", 
327                                                                    P_("Page title"),
328                                                                    P_("The title of the assistant page"),
329                                                                    NULL,
330                                                                    GTK_PARAM_READWRITE));
331
332   /**
333    * GtkAssistant:header-image:
334    *
335    * The image that is displayed next to the title in the page header.
336    *
337    * If title and header-image are both %NULL, no header is displayed.
338    *
339    * Since: 2.10
340    */
341   gtk_container_class_install_child_property (container_class,
342                                               CHILD_PROP_PAGE_HEADER_IMAGE,
343                                               g_param_spec_object ("header-image", 
344                                                                    P_("Header image"),
345                                                                    P_("Header image for the assistant page"),
346                                                                    GDK_TYPE_PIXBUF,
347                                                                    GTK_PARAM_READWRITE));
348
349   /**
350    * GtkAssistant:header-image:
351    *
352    * The image that is displayed next to the page.
353    *
354    * Set this to %NULL to make the sidebar disappear.
355    *
356    * Since: 2.10
357    */
358   gtk_container_class_install_child_property (container_class,
359                                               CHILD_PROP_PAGE_SIDEBAR_IMAGE,
360                                               g_param_spec_object ("sidebar-image", 
361                                                                    P_("Sidebar image"),
362                                                                    P_("Sidebar image for the assistant page"),
363                                                                    GDK_TYPE_PIXBUF,
364                                                                    GTK_PARAM_READWRITE));
365   /**
366    * GtkAssistant:complete:
367    *
368    * Setting the "complete" child property to %TRUE marks a page as complete
369    * (i.e.: all the required fields are filled out). GTK+ uses this information
370    * to control the sensitivity of the navigation buttons.
371    *
372    * Since: 2.10
373    **/
374   gtk_container_class_install_child_property (container_class,
375                                               CHILD_PROP_PAGE_COMPLETE,
376                                               g_param_spec_boolean ("complete", 
377                                                                     P_("Page complete"),
378                                                                     P_("Whether all required fields on the page have been filled out"),
379                                                                     FALSE,
380                                                                     G_PARAM_READWRITE));
381
382   g_type_class_add_private (gobject_class, sizeof (GtkAssistantPrivate));
383 }
384
385 static gint
386 default_forward_function (gint current_page, gpointer data)
387 {
388   GtkAssistant *assistant;
389   GtkAssistantPrivate *priv;
390   GtkAssistantPage *page_info;
391   GList *page_node;
392
393   assistant = GTK_ASSISTANT (data);
394   priv = assistant->priv;
395
396   page_node = g_list_nth (priv->pages, ++current_page);
397
398   if (!page_node)
399     return -1;
400
401   page_info = (GtkAssistantPage *) page_node->data;
402
403   while (page_node && !GTK_WIDGET_VISIBLE (page_info->page))
404     {
405       page_node = page_node->next;
406       current_page++;
407
408       if (page_node)
409         page_info = (GtkAssistantPage *) page_node->data;
410     }
411
412   return current_page;
413 }
414
415 static void
416 compute_last_button_state (GtkAssistant *assistant)
417 {
418   GtkAssistantPrivate *priv = assistant->priv;
419   GtkAssistantPage *page_info, *current_page_info;
420   gint count, page_num, n_pages;
421
422   count = 0;
423   page_num = gtk_assistant_get_current_page (assistant);
424   n_pages  = gtk_assistant_get_n_pages (assistant);
425   current_page_info = page_info = g_list_nth_data (priv->pages, page_num);
426
427   while (page_num >= 0 && page_num < n_pages &&
428          page_info->type == GTK_ASSISTANT_PAGE_CONTENT &&
429          (count == 0 || page_info->complete) &&
430          count < n_pages)
431     {
432       page_num = (priv->forward_function) (page_num, priv->forward_function_data);
433       page_info = g_list_nth_data (priv->pages, page_num);
434
435       count++;
436     }
437
438   /* make the last button visible if we can skip multiple
439    * pages and end on a confirmation or summary page
440    */
441   if (count > 1 && page_info &&
442       (page_info->type == GTK_ASSISTANT_PAGE_CONFIRM ||
443        page_info->type == GTK_ASSISTANT_PAGE_SUMMARY))
444     {
445       gtk_widget_show (assistant->last);
446       gtk_widget_set_sensitive (assistant->last,
447                                 current_page_info->complete);
448     }
449   else
450     gtk_widget_hide (assistant->last);
451 }
452
453 static void
454 set_assistant_header_image (GtkAssistant *assistant)
455 {
456   GtkAssistantPrivate *priv = assistant->priv;
457
458   gtk_image_set_from_pixbuf (GTK_IMAGE (priv->header_image),
459                              priv->current_page->header_image);
460 }
461
462 static void
463 set_assistant_sidebar_image (GtkAssistant *assistant)
464 {
465   GtkAssistantPrivate *priv = assistant->priv;
466
467   gtk_image_set_from_pixbuf (GTK_IMAGE (priv->sidebar_image),
468                              priv->current_page->sidebar_image);
469
470   if (priv->current_page->sidebar_image)
471     gtk_widget_show (priv->sidebar_image);
472   else
473     gtk_widget_hide (priv->sidebar_image);
474 }
475
476 static void
477 set_assistant_buttons_state (GtkAssistant *assistant)
478 {
479   GtkAssistantPrivate *priv = assistant->priv;
480
481   if (!priv->current_page)
482     return;
483   
484   switch (priv->current_page->type)
485     {
486     case GTK_ASSISTANT_PAGE_INTRO:
487       gtk_widget_set_sensitive (assistant->cancel, TRUE);
488       gtk_widget_set_sensitive (assistant->forward, priv->current_page->complete);
489       gtk_widget_grab_default (assistant->forward);
490       gtk_widget_show (assistant->cancel);
491       gtk_widget_show (assistant->forward);
492       gtk_widget_hide (assistant->back);
493       gtk_widget_hide (assistant->apply);
494       gtk_widget_hide (assistant->close);
495       compute_last_button_state (assistant);
496       break;
497     case GTK_ASSISTANT_PAGE_CONFIRM:
498       gtk_widget_set_sensitive (assistant->cancel, TRUE);
499       gtk_widget_set_sensitive (assistant->back, TRUE);
500       gtk_widget_set_sensitive (assistant->apply, priv->current_page->complete);
501       gtk_widget_grab_default (assistant->apply);
502       gtk_widget_show (assistant->cancel);
503       gtk_widget_show (assistant->back);
504       gtk_widget_show (assistant->apply);
505       gtk_widget_hide (assistant->forward);
506       gtk_widget_hide (assistant->close);
507       gtk_widget_hide (assistant->last);
508       break;
509     case GTK_ASSISTANT_PAGE_CONTENT:
510       gtk_widget_set_sensitive (assistant->cancel, TRUE);
511       gtk_widget_set_sensitive (assistant->back, TRUE);
512       gtk_widget_set_sensitive (assistant->forward, priv->current_page->complete);
513       gtk_widget_grab_default (assistant->forward);
514       gtk_widget_show (assistant->cancel);
515       gtk_widget_show (assistant->back);
516       gtk_widget_show (assistant->forward);
517       gtk_widget_hide (assistant->apply);
518       gtk_widget_hide (assistant->close);
519       compute_last_button_state (assistant);
520       break;
521     case GTK_ASSISTANT_PAGE_SUMMARY:
522       gtk_widget_set_sensitive (assistant->close, priv->current_page->complete);
523       gtk_widget_grab_default (assistant->close);
524       gtk_widget_show (assistant->close);
525       gtk_widget_hide (assistant->cancel);
526       gtk_widget_hide (assistant->back);
527       gtk_widget_hide (assistant->forward);
528       gtk_widget_hide (assistant->apply);
529       gtk_widget_hide (assistant->last);
530       break;
531     case GTK_ASSISTANT_PAGE_PROGRESS:
532       gtk_widget_set_sensitive (assistant->cancel, priv->current_page->complete);
533       gtk_widget_set_sensitive (assistant->back, priv->current_page->complete);
534       gtk_widget_set_sensitive (assistant->forward, priv->current_page->complete);
535       gtk_widget_grab_default (assistant->forward);
536       gtk_widget_show (assistant->cancel);
537       gtk_widget_show (assistant->back);
538       gtk_widget_show (assistant->forward);
539       gtk_widget_hide (assistant->apply);
540       gtk_widget_hide (assistant->close);
541       gtk_widget_hide (assistant->last);
542       break;
543     default:
544       g_assert_not_reached ();
545     }
546
547   /* this is quite general, we don't want to
548    * go back if it's the first page */
549   if (!priv->visited_pages)
550     gtk_widget_hide (assistant->back);
551 }
552
553 static void
554 set_current_page (GtkAssistant     *assistant,
555                   GtkAssistantPage *page)
556 {
557   GtkAssistantPrivate *priv = assistant->priv;
558   GtkAssistantPage *old_page;
559
560   if (priv->current_page &&
561       GTK_WIDGET_DRAWABLE (priv->current_page->page))
562     old_page = priv->current_page;
563   else
564     old_page = NULL;
565
566   priv->current_page = page;
567
568   set_assistant_buttons_state (assistant);
569   set_assistant_header_image (assistant);
570   set_assistant_sidebar_image (assistant);
571
572   g_signal_emit (assistant, signals [PREPARE], 0, priv->current_page->page);
573
574   if (GTK_WIDGET_VISIBLE (priv->current_page->page) && GTK_WIDGET_MAPPED (assistant))
575     {
576       gtk_widget_set_child_visible (priv->current_page->page, TRUE);
577       gtk_widget_map (priv->current_page->page);
578       gtk_widget_map (priv->current_page->title);
579     }
580   
581   if (old_page && GTK_WIDGET_MAPPED (old_page->page))
582     {
583       gtk_widget_set_child_visible (old_page->page, FALSE);
584       gtk_widget_unmap (old_page->page);
585       gtk_widget_unmap (old_page->title);
586     }
587
588   if (!gtk_widget_child_focus (priv->current_page->page, GTK_DIR_TAB_FORWARD))
589     {
590       GtkWidget *button[6];
591       gint i;
592
593       /* find the best button to focus */
594       button[0] = assistant->apply;
595       button[1] = assistant->close;
596       button[2] = assistant->forward;
597       button[3] = assistant->back;
598       button[4] = assistant->cancel;
599       button[5] = assistant->last;
600       for (i = 0; i < 6; i++)
601         {
602           if (GTK_WIDGET_VISIBLE (button[i]) && GTK_WIDGET_SENSITIVE (button[i]))
603             {
604               gtk_widget_grab_focus (button[i]);
605               break;
606             }
607         }
608     }
609
610   gtk_widget_queue_resize (GTK_WIDGET (assistant));
611 }
612
613 static gint
614 compute_next_step (GtkAssistant *assistant)
615 {
616   GtkAssistantPrivate *priv = assistant->priv;
617   GtkAssistantPage *page_info;
618   gint current_page, n_pages, next_page;
619
620   current_page = gtk_assistant_get_current_page (assistant);
621   page_info = priv->current_page;
622   n_pages = gtk_assistant_get_n_pages (assistant);
623
624   next_page = (priv->forward_function) (current_page,
625                                         priv->forward_function_data);
626
627   if (next_page >= 0 && next_page < n_pages)
628     {
629       priv->visited_pages = g_slist_prepend (priv->visited_pages, page_info);
630       set_current_page (assistant, g_list_nth_data (priv->pages, next_page));
631
632       return TRUE;
633     }
634
635   return FALSE;
636 }
637
638 static void
639 on_assistant_close (GtkWidget    *widget,
640                     GtkAssistant *assistant)
641 {
642   g_signal_emit (assistant, signals [CLOSE], 0, NULL);
643 }
644
645 static void
646 on_assistant_apply (GtkWidget    *widget,
647                     GtkAssistant *assistant)
648 {
649   gboolean success;
650
651   g_signal_emit (assistant, signals [APPLY], 0);
652
653   success = compute_next_step (assistant);
654
655   /* if the assistant hasn't switched to another page, just emit
656    * the CLOSE signal, it't the last page in the assistant flow
657    */
658   if (!success)
659     g_signal_emit (assistant, signals [CLOSE], 0);
660 }
661
662 static void
663 on_assistant_forward (GtkWidget    *widget,
664                       GtkAssistant *assistant)
665 {
666   if (!compute_next_step (assistant))
667     g_critical ("Page flow is broken, you may want to end it with a page of "
668                 "type GTK_ASSISTANT_PAGE_CONFIRM or GTK_ASSISTANT_PAGE_SUMMARY");
669 }
670
671 static void
672 on_assistant_back (GtkWidget    *widget,
673                    GtkAssistant *assistant)
674 {
675   GtkAssistantPrivate *priv = assistant->priv;
676   GtkAssistantPage *page_info;
677   GSList *page_node;
678
679   /* skip the progress pages when going back */
680   do
681     {
682       page_node = priv->visited_pages;
683
684       g_return_if_fail (page_node != NULL);
685
686       priv->visited_pages = priv->visited_pages->next;
687       page_info = (GtkAssistantPage *) page_node->data;
688       g_slist_free_1 (page_node);
689     }
690   while (page_info->type == GTK_ASSISTANT_PAGE_PROGRESS ||
691          !GTK_WIDGET_VISIBLE (page_info->page));
692
693   set_current_page (assistant, page_info);
694 }
695
696 static void
697 on_assistant_cancel (GtkWidget    *widget,
698                      GtkAssistant *assistant)
699 {
700   g_signal_emit (assistant, signals [CANCEL], 0, NULL);
701 }
702
703 static void
704 on_assistant_last (GtkWidget    *widget,
705                    GtkAssistant *assistant)
706 {
707   GtkAssistantPrivate *priv = assistant->priv;
708
709   while (priv->current_page->type == GTK_ASSISTANT_PAGE_CONTENT &&
710          priv->current_page->complete)
711     compute_next_step (assistant);
712 }
713
714 static gboolean
715 alternative_button_order (GtkAssistant *assistant)
716 {
717   GtkSettings *settings;
718   GdkScreen *screen;
719   gboolean result;
720
721   screen   = gtk_widget_get_screen (GTK_WIDGET (assistant));
722   settings = gtk_settings_get_for_screen (screen);
723
724   g_object_get (settings,
725                 "gtk-alternative-button-order", &result,
726                 NULL);
727   return result;
728 }
729
730 static void
731 gtk_assistant_init (GtkAssistant *assistant)
732 {
733   GtkAssistantPrivate *priv;
734
735   priv = assistant->priv = GTK_ASSISTANT_GET_PRIVATE (assistant);
736
737   gtk_container_set_reallocate_redraws (GTK_CONTAINER (assistant), TRUE);
738   gtk_container_set_border_width (GTK_CONTAINER (assistant), 12);
739
740   gtk_widget_push_composite_child ();
741
742   /* Header */
743   priv->header_image = gtk_image_new ();
744   gtk_misc_set_alignment (GTK_MISC (priv->header_image), 1., 0.5);
745   gtk_widget_set_parent (priv->header_image, GTK_WIDGET (assistant));
746   gtk_widget_show (priv->header_image);
747
748   /* Sidebar */
749   priv->sidebar_image = gtk_image_new ();
750   gtk_misc_set_alignment (GTK_MISC (priv->sidebar_image), 0., 0.);
751   gtk_widget_set_parent (priv->sidebar_image, GTK_WIDGET (assistant));
752   gtk_widget_show (priv->sidebar_image);
753
754   /* Action area  */
755   priv->action_area  = gtk_hbox_new (FALSE, 6);
756   
757   assistant->close   = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
758   assistant->apply   = gtk_button_new_from_stock (GTK_STOCK_APPLY);
759   assistant->forward = gtk_button_new_from_stock (GTK_STOCK_GO_FORWARD);
760   assistant->back    = gtk_button_new_from_stock (GTK_STOCK_GO_BACK);
761   assistant->cancel  = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
762   assistant->last    = gtk_button_new_from_stock (GTK_STOCK_GOTO_LAST);
763   GTK_WIDGET_SET_FLAGS (assistant->close, GTK_CAN_DEFAULT);
764   GTK_WIDGET_SET_FLAGS (assistant->apply, GTK_CAN_DEFAULT);
765   GTK_WIDGET_SET_FLAGS (assistant->forward, GTK_CAN_DEFAULT);
766
767   priv->size_group   = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
768   gtk_size_group_add_widget (priv->size_group, assistant->close);
769   gtk_size_group_add_widget (priv->size_group, assistant->apply);
770   gtk_size_group_add_widget (priv->size_group, assistant->forward);
771   gtk_size_group_add_widget (priv->size_group, assistant->back);
772   gtk_size_group_add_widget (priv->size_group, assistant->cancel);
773   gtk_size_group_add_widget (priv->size_group, assistant->last);
774
775   if (!alternative_button_order (assistant))
776     {
777       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->apply, FALSE, FALSE, 0);
778       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->forward, FALSE, FALSE, 0);
779       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->back, FALSE, FALSE, 0);
780       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->last, FALSE, FALSE, 0);
781       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->cancel, FALSE, FALSE, 0);
782       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->close, FALSE, FALSE, 0);
783     }
784   else
785     {
786       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->close, FALSE, FALSE, 0);
787       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->cancel, FALSE, FALSE, 0);
788       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->apply, FALSE, FALSE, 0);
789       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->forward, FALSE, FALSE, 0);
790       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->back, FALSE, FALSE, 0);
791       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->last, FALSE, FALSE, 0);
792     }
793
794   gtk_widget_set_parent (priv->action_area, GTK_WIDGET (assistant));
795   gtk_widget_show (assistant->forward);
796   gtk_widget_show (assistant->back);
797   gtk_widget_show (assistant->cancel);
798   gtk_widget_show (priv->action_area);
799
800   gtk_widget_pop_composite_child ();
801
802   priv->pages = NULL;
803   priv->current_page = NULL;
804   priv->visited_pages = NULL;
805
806   priv->forward_function = default_forward_function;
807   priv->forward_function_data = assistant;
808   priv->forward_data_destroy = NULL;
809
810   g_signal_connect (G_OBJECT (assistant->close), "clicked",
811                     G_CALLBACK (on_assistant_close), assistant);
812   g_signal_connect (G_OBJECT (assistant->apply), "clicked",
813                     G_CALLBACK (on_assistant_apply), assistant);
814   g_signal_connect (G_OBJECT (assistant->forward), "clicked",
815                     G_CALLBACK (on_assistant_forward), assistant);
816   g_signal_connect (G_OBJECT (assistant->back), "clicked",
817                     G_CALLBACK (on_assistant_back), assistant);
818   g_signal_connect (G_OBJECT (assistant->cancel), "clicked",
819                     G_CALLBACK (on_assistant_cancel), assistant);
820   g_signal_connect (G_OBJECT (assistant->last), "clicked",
821                     G_CALLBACK (on_assistant_last), assistant);
822 }
823
824 static void
825 gtk_assistant_set_child_property (GtkContainer    *container,
826                                   GtkWidget       *child,
827                                   guint            property_id,
828                                   const GValue    *value,
829                                   GParamSpec      *pspec)
830 {
831   switch (property_id)
832     {
833     case CHILD_PROP_PAGE_TYPE:
834       gtk_assistant_set_page_type (GTK_ASSISTANT (container), child,
835                                    g_value_get_enum (value));
836       break;
837     case CHILD_PROP_PAGE_TITLE:
838       gtk_assistant_set_page_title (GTK_ASSISTANT (container), child,
839                                     g_value_get_string (value));
840       break;
841     case CHILD_PROP_PAGE_HEADER_IMAGE:
842       gtk_assistant_set_page_header_image (GTK_ASSISTANT (container), child,
843                                            g_value_get_object (value));
844       break;
845     case CHILD_PROP_PAGE_SIDEBAR_IMAGE:
846       gtk_assistant_set_page_side_image (GTK_ASSISTANT (container), child,
847                                          g_value_get_object (value));
848       break;
849     case CHILD_PROP_PAGE_COMPLETE:
850       gtk_assistant_set_page_complete (GTK_ASSISTANT (container), child,
851                                        g_value_get_boolean (value));
852       break;
853     default:
854       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
855       break;
856     }
857 }
858
859 static void
860 gtk_assistant_get_child_property (GtkContainer *container,
861                                   GtkWidget    *child,
862                                   guint         property_id,
863                                   GValue       *value,
864                                   GParamSpec   *pspec)
865 {
866   switch (property_id)
867     {
868     case CHILD_PROP_PAGE_TYPE:
869       g_value_set_enum (value,
870                         gtk_assistant_get_page_type (GTK_ASSISTANT (container), child));
871       break;
872     case CHILD_PROP_PAGE_TITLE:
873       g_value_set_string (value,
874                           gtk_assistant_get_page_title (GTK_ASSISTANT (container), child));
875       break;
876     case CHILD_PROP_PAGE_HEADER_IMAGE:
877       g_value_set_object (value,
878                           gtk_assistant_get_page_header_image (GTK_ASSISTANT (container), child));
879       break;
880     case CHILD_PROP_PAGE_SIDEBAR_IMAGE:
881       g_value_set_object (value,
882                           gtk_assistant_get_page_side_image (GTK_ASSISTANT (container), child));
883       break;
884     case CHILD_PROP_PAGE_COMPLETE:
885       g_value_set_boolean (value,
886                            gtk_assistant_get_page_complete (GTK_ASSISTANT (container), child));
887       break;
888     default:
889       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
890       break;
891     }
892 }
893
894 static void
895 on_page_notify_visibility (GtkWidget  *widget,
896                            GParamSpec *arg,
897                            gpointer    data)
898 {
899   GtkAssistant *assistant = GTK_ASSISTANT (data);
900
901   /* update buttons state, flow may have changed */
902   if (GTK_WIDGET_MAPPED (assistant))
903     set_assistant_buttons_state (assistant);
904 }
905
906 static void
907 remove_page (GtkAssistant *assistant, 
908              GList        *element)
909 {
910   GtkAssistantPrivate *priv = assistant->priv;
911   GtkAssistantPage *page_info;
912   GList *page_node;
913
914   page_info = element->data;
915
916   /* If this is the current page, we need to switch away. */
917   if (page_info == priv->current_page)
918     {
919       if (!compute_next_step (assistant))
920         {
921           /* The best we can do at this point is probably to pick the first
922            * visible page.
923            */
924           page_node = priv->pages;
925
926           while (page_node && !GTK_WIDGET_VISIBLE (((GtkAssistantPage *) page_node->data)->page))
927             page_node = page_node->next;
928
929           if (page_node == element)
930             page_node = page_node->next;
931
932           if (page_node)
933             priv->current_page = page_node->data;
934           else
935             priv->current_page = NULL;
936         }
937     }
938
939   priv->pages = g_list_remove_link (priv->pages, element);
940   priv->visited_pages = g_slist_remove_all (priv->visited_pages, page_info);
941
942   g_signal_handlers_disconnect_by_func (page_info->page, on_page_notify_visibility, assistant);
943   gtk_widget_unparent (page_info->page);
944
945   if (page_info->header_image)
946     g_object_unref (page_info->header_image);
947
948   if (page_info->sidebar_image)
949     g_object_unref (page_info->sidebar_image);
950
951   gtk_widget_destroy (page_info->title);
952   g_slice_free (GtkAssistantPage, page_info);
953   g_list_free_1 (element);
954 }
955
956 static void
957 gtk_assistant_destroy (GtkObject *object)
958 {
959   GtkAssistant *assistant = GTK_ASSISTANT (object);
960   GtkAssistantPrivate *priv = assistant->priv;
961
962   if (priv->header_image)
963     {
964       gtk_widget_destroy (priv->header_image);
965       priv->header_image = NULL;
966     }
967
968   if (priv->sidebar_image)
969     {
970       gtk_widget_destroy (priv->sidebar_image);
971       priv->sidebar_image = NULL;
972     }
973
974   if (priv->action_area)
975     {
976       gtk_widget_destroy (priv->action_area);
977       priv->action_area = NULL;
978     }
979
980   if (priv->size_group)
981     {
982       g_object_unref (priv->size_group);
983       priv->size_group = NULL;
984     }
985
986   if (priv->forward_function)
987     {
988       if (priv->forward_function_data &&
989           priv->forward_data_destroy)
990         priv->forward_data_destroy (priv->forward_function_data);
991
992       priv->forward_function = NULL;
993       priv->forward_function_data = NULL;
994       priv->forward_data_destroy = NULL;
995     }
996
997   if (priv->visited_pages)
998     {
999       g_slist_free (priv->visited_pages);
1000       priv->visited_pages = NULL;
1001     }
1002
1003   /* We set current to NULL so that the remove code doesn't try
1004    * to do anything funny */
1005   priv->current_page = NULL;
1006
1007   while (priv->pages)
1008     remove_page (GTK_ASSISTANT (object), priv->pages);
1009       
1010   GTK_OBJECT_CLASS (gtk_assistant_parent_class)->destroy (object);
1011 }
1012
1013 static GList*
1014 find_page (GtkAssistant  *assistant,
1015            GtkWidget     *page)
1016 {
1017   GtkAssistantPrivate *priv = assistant->priv;
1018   GList *child = priv->pages;
1019   
1020   while (child)
1021     {
1022       GtkAssistantPage *page_info = child->data;
1023       if (page_info->page == page)
1024         return child;
1025
1026       child = child->next;
1027     }
1028   
1029   return NULL;
1030 }
1031
1032 static void
1033 set_title_colors (GtkWidget *assistant,
1034                   GtkWidget *title_label)
1035 {
1036   GtkStyle *style;
1037
1038   gtk_widget_ensure_style (assistant);
1039   style = gtk_widget_get_style (assistant);
1040
1041   /* change colors schema, for making the header text visible */
1042   gtk_widget_modify_bg (title_label, GTK_STATE_NORMAL, &style->bg[GTK_STATE_SELECTED]);
1043   gtk_widget_modify_fg (title_label, GTK_STATE_NORMAL, &style->fg[GTK_STATE_SELECTED]);
1044 }
1045
1046 static void
1047 set_title_font (GtkWidget *assistant,
1048                 GtkWidget *title_label)
1049 {
1050   PangoFontDescription *desc;
1051   gint size;
1052
1053   desc = pango_font_description_new ();
1054   size = pango_font_description_get_size (assistant->style->font_desc);
1055
1056   pango_font_description_set_weight (desc, PANGO_WEIGHT_ULTRABOLD);
1057   pango_font_description_set_size   (desc, size * PANGO_SCALE_XX_LARGE);
1058
1059   gtk_widget_modify_font (title_label, desc);
1060   pango_font_description_free (desc);
1061 }
1062
1063 static void
1064 gtk_assistant_style_set (GtkWidget *widget,
1065                          GtkStyle  *old_style)
1066 {
1067   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1068   GtkAssistantPrivate *priv = assistant->priv;
1069   GList *list;
1070
1071   list = priv->pages;
1072
1073   while (list)
1074     {
1075       GtkAssistantPage *page = list->data;
1076
1077       set_title_colors (widget, page->title);
1078       set_title_font (widget, page->title);
1079
1080       list = list->next;
1081     }
1082 }
1083
1084 static void
1085 gtk_assistant_size_request (GtkWidget      *widget,
1086                             GtkRequisition *requisition)
1087 {
1088   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1089   GtkAssistantPrivate *priv = assistant->priv;
1090   GtkRequisition child_requisition;
1091   gint header_padding, content_padding;
1092   gint width, height, header_width, header_height;
1093   GList *list;
1094
1095   gtk_widget_style_get (widget,
1096                         "header-padding", &header_padding,
1097                         "content-padding", &content_padding,
1098                         NULL);
1099   width = height = 0;
1100   header_width = header_height = 0;
1101   list  = priv->pages;
1102
1103   while (list)
1104     {
1105       GtkAssistantPage *page = list->data;
1106       gint w, h;
1107
1108       gtk_widget_size_request (page->page, &child_requisition);
1109       width  = MAX (width,  child_requisition.width);
1110       height = MAX (height, child_requisition.height);
1111
1112       gtk_widget_size_request (page->title, &child_requisition);
1113       w = child_requisition.width;
1114       h = child_requisition.height;
1115
1116       if (page->header_image)
1117         {
1118           w += gdk_pixbuf_get_width (page->header_image) + HEADER_SPACING;
1119           h  = MAX (h, gdk_pixbuf_get_height (page->header_image));
1120         }
1121
1122       header_width  = MAX (header_width, w);
1123       header_height = MAX (header_height, h);
1124
1125       list = list->next;
1126     }
1127
1128   gtk_widget_size_request (priv->sidebar_image, &child_requisition);
1129   width  += child_requisition.width;
1130   height  = MAX (height, child_requisition.height);
1131
1132   gtk_widget_set_size_request (priv->header_image, header_width, header_height);
1133   gtk_widget_size_request (priv->header_image, &child_requisition);
1134   width   = MAX (width, header_width) + 2 * header_padding;
1135   height += header_height + 2 * header_padding;
1136
1137   gtk_widget_size_request (priv->action_area, &child_requisition);
1138   width   = MAX (width, child_requisition.width);
1139   height += child_requisition.height + ACTION_AREA_SPACING;
1140
1141   width += GTK_CONTAINER (widget)->border_width * 2 + content_padding * 2;
1142   height += GTK_CONTAINER (widget)->border_width * 2 + content_padding * 2;
1143
1144   requisition->width = width;
1145   requisition->height = height;
1146 }
1147
1148 static void
1149 gtk_assistant_size_allocate (GtkWidget      *widget,
1150                              GtkAllocation  *allocation)
1151 {
1152   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1153   GtkAssistantPrivate *priv = assistant->priv;
1154   GtkRequisition header_requisition;
1155   GtkAllocation child_allocation, header_allocation;
1156   gint header_padding, content_padding;
1157   gboolean rtl;
1158   GList *pages;
1159
1160   rtl   = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1161   pages = priv->pages;
1162
1163   gtk_widget_style_get (widget,
1164                         "header-padding", &header_padding,
1165                         "content-padding", &content_padding,
1166                         NULL);
1167
1168   widget->allocation = *allocation;
1169
1170   /* Header */
1171   gtk_widget_get_child_requisition (priv->header_image, &header_requisition);
1172
1173   header_allocation.x = GTK_CONTAINER (widget)->border_width + header_padding;
1174   header_allocation.y = GTK_CONTAINER (widget)->border_width + header_padding;
1175   header_allocation.width  = allocation->width - 2 * GTK_CONTAINER (widget)->border_width - 2 * header_padding;
1176   header_allocation.height = header_requisition.height;
1177
1178   gtk_widget_size_allocate (priv->header_image, &header_allocation);
1179
1180   /* Action area */
1181   child_allocation.x = GTK_CONTAINER (widget)->border_width;
1182   child_allocation.y = allocation->height -
1183     GTK_CONTAINER (widget)->border_width - priv->action_area->requisition.height;
1184   child_allocation.width  = allocation->width - 2 * GTK_CONTAINER (widget)->border_width;
1185   child_allocation.height = priv->action_area->requisition.height;
1186
1187   gtk_widget_size_allocate (priv->action_area, &child_allocation);
1188
1189   /* Sidebar */
1190   if (rtl)
1191     child_allocation.x = allocation->width -
1192       GTK_CONTAINER (widget)->border_width - priv->sidebar_image->requisition.width;
1193   else
1194     child_allocation.x = GTK_CONTAINER (widget)->border_width;
1195
1196   child_allocation.y = GTK_CONTAINER (widget)->border_width +
1197     priv->header_image->allocation.height + 2 * header_padding;
1198   child_allocation.width = priv->sidebar_image->requisition.width;
1199   child_allocation.height = allocation->height - 2 * GTK_CONTAINER (widget)->border_width -
1200     priv->header_image->allocation.height - 2 * header_padding - priv->action_area->allocation.height;
1201
1202   gtk_widget_size_allocate (priv->sidebar_image, &child_allocation);
1203
1204   /* Pages */
1205   child_allocation.x = GTK_CONTAINER (widget)->border_width + content_padding;
1206   child_allocation.y = GTK_CONTAINER (widget)->border_width +
1207     priv->header_image->allocation.height + 2 * header_padding + content_padding;
1208   child_allocation.width  = allocation->width - 2 * GTK_CONTAINER (widget)->border_width - 2 * content_padding;
1209   child_allocation.height = allocation->height - 2 * GTK_CONTAINER (widget)->border_width -
1210     priv->header_image->allocation.height - 2 * header_padding - ACTION_AREA_SPACING - priv->action_area->allocation.height - 2 * content_padding;
1211
1212   if (GTK_WIDGET_VISIBLE (priv->sidebar_image))
1213     {
1214       if (!rtl)
1215         child_allocation.x += priv->sidebar_image->allocation.width;
1216
1217       child_allocation.width -= priv->sidebar_image->allocation.width;
1218     }
1219
1220   while (pages)
1221     {
1222       GtkAssistantPage *page = pages->data;
1223
1224       gtk_widget_size_allocate (page->page, &child_allocation);
1225       gtk_widget_size_allocate (page->title, &header_allocation);
1226       pages = pages->next;
1227     }
1228 }
1229
1230 static void
1231 gtk_assistant_map (GtkWidget *widget)
1232 {
1233   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1234   GtkAssistantPrivate *priv = assistant->priv;
1235   GList *page_node;
1236   GtkAssistantPage *page;
1237
1238   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
1239
1240   gtk_widget_map (priv->header_image);
1241   gtk_widget_map (priv->action_area);
1242
1243   if (GTK_WIDGET_VISIBLE (priv->sidebar_image) &&
1244       !GTK_WIDGET_MAPPED (priv->sidebar_image))
1245     gtk_widget_map (priv->sidebar_image);
1246
1247   /* if there's no default page, pick the first one */
1248   page = NULL;
1249   if (!priv->current_page)
1250     {
1251       page_node = priv->pages;
1252
1253       while (page_node && !GTK_WIDGET_VISIBLE (((GtkAssistantPage *) page_node->data)->page))
1254         page_node = page_node->next;
1255
1256       if (page_node)
1257         page = page_node->data;
1258     }
1259
1260   if (page &&
1261       GTK_WIDGET_VISIBLE (page->page) &&
1262       !GTK_WIDGET_MAPPED (page->page))
1263     set_current_page (assistant, page);
1264
1265   GTK_WIDGET_CLASS (gtk_assistant_parent_class)->map (widget);
1266 }
1267
1268 static void
1269 gtk_assistant_unmap (GtkWidget *widget)
1270 {
1271   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1272   GtkAssistantPrivate *priv = assistant->priv;
1273
1274   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
1275
1276   gtk_widget_unmap (priv->header_image);
1277   gtk_widget_unmap (priv->action_area);
1278
1279   if (GTK_WIDGET_DRAWABLE (priv->sidebar_image))
1280     gtk_widget_unmap (priv->sidebar_image);
1281
1282   if (priv->current_page &&
1283       GTK_WIDGET_DRAWABLE (priv->current_page->page))
1284     gtk_widget_unmap (priv->current_page->page);
1285
1286   g_slist_free (priv->visited_pages);
1287   priv->visited_pages = NULL;
1288   priv->current_page  = NULL;
1289
1290   GTK_WIDGET_CLASS (gtk_assistant_parent_class)->unmap (widget);
1291 }
1292
1293 static gboolean
1294 gtk_assistant_delete_event (GtkWidget   *widget,
1295                             GdkEventAny *event)
1296 {
1297   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1298   GtkAssistantPrivate *priv = assistant->priv;
1299
1300   /* Do not allow cancelling in the middle of a progress page */
1301   if (priv->current_page &&
1302       (priv->current_page->type != GTK_ASSISTANT_PAGE_PROGRESS ||
1303        priv->current_page->complete))
1304     g_signal_emit (widget, signals [CANCEL], 0, NULL);
1305
1306   return TRUE;
1307 }
1308
1309 static void
1310 assistant_paint_colored_box (GtkWidget *widget)
1311 {
1312   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1313   GtkAssistantPrivate *priv = assistant->priv;
1314   gint border_width, header_padding, content_padding;
1315   cairo_t *cr;
1316   gint content_x, content_width;
1317   gboolean rtl;
1318
1319   cr   = gdk_cairo_create (widget->window);
1320   rtl  = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1321   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1322
1323   gtk_widget_style_get (widget,
1324                         "header-padding",  &header_padding,
1325                         "content-padding", &content_padding,
1326                         NULL);
1327
1328   /* colored box */
1329   gdk_cairo_set_source_color (cr, &widget->style->bg[GTK_STATE_SELECTED]);
1330   cairo_rectangle (cr,
1331                    border_width,
1332                    border_width,
1333                    widget->allocation.width - 2 * border_width,
1334                    widget->allocation.height - priv->action_area->allocation.height - 2 * border_width - ACTION_AREA_SPACING);
1335   cairo_fill (cr);
1336
1337   /* content box */
1338   content_x = content_padding + border_width;
1339   content_width = widget->allocation.width - 2 * content_padding - 2 * border_width;
1340
1341   if (GTK_WIDGET_VISIBLE (priv->sidebar_image))
1342     {
1343       if (!rtl)
1344         content_x += priv->sidebar_image->allocation.width;
1345       content_width -= priv->sidebar_image->allocation.width;
1346     }
1347   
1348   gdk_cairo_set_source_color (cr, &widget->style->bg[GTK_STATE_NORMAL]);
1349
1350   cairo_rectangle (cr,
1351                    content_x,
1352                    priv->header_image->allocation.height + content_padding + 2 * header_padding + border_width,
1353                    content_width,
1354                    widget->allocation.height - 2 * border_width - priv->action_area->allocation.height -
1355                    priv->header_image->allocation.height - 2 * content_padding - 2 * header_padding - ACTION_AREA_SPACING);
1356   cairo_fill (cr);
1357
1358   cairo_destroy (cr);
1359 }
1360
1361 static gboolean
1362 gtk_assistant_expose (GtkWidget      *widget,
1363                       GdkEventExpose *event)
1364 {
1365   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1366   GtkAssistantPrivate *priv = assistant->priv;
1367   GtkContainer *container;
1368
1369   if (GTK_WIDGET_DRAWABLE (widget))
1370     {
1371       container = GTK_CONTAINER (widget);
1372
1373       assistant_paint_colored_box (widget);
1374
1375       gtk_container_propagate_expose (container, priv->header_image, event);
1376       gtk_container_propagate_expose (container, priv->sidebar_image, event);
1377       gtk_container_propagate_expose (container, priv->action_area, event);
1378
1379       if (priv->current_page)
1380         {
1381           gtk_container_propagate_expose (container, priv->current_page->page, event);
1382           gtk_container_propagate_expose (container, priv->current_page->title, event);
1383         }
1384     }
1385
1386   return FALSE;
1387 }
1388
1389 static gboolean
1390 gtk_assistant_focus (GtkWidget        *widget,
1391                      GtkDirectionType  direction)
1392 {
1393   GtkAssistantPrivate *priv;
1394   GtkContainer *container;
1395
1396   container = GTK_CONTAINER (widget);
1397   priv = GTK_ASSISTANT (widget)->priv;
1398
1399   /* we only have to care about 2 widgets, action area and the current page */
1400   if (container->focus_child == priv->action_area)
1401     {
1402       if (!gtk_widget_child_focus (priv->action_area, direction) &&
1403           (priv->current_page == NULL ||
1404            !gtk_widget_child_focus (priv->current_page->page, direction)))
1405         {
1406           /* if we're leaving the action area and the current page hasn't
1407              any focusable widget, clear focus and go back to the action area */
1408           gtk_container_set_focus_child (GTK_CONTAINER (priv->action_area), NULL);
1409           gtk_widget_child_focus (priv->action_area, direction);
1410         }
1411     }
1412   else
1413     {
1414       if ((priv->current_page ==  NULL ||
1415            !gtk_widget_child_focus (priv->current_page->page, direction)) &&
1416           !gtk_widget_child_focus (priv->action_area, direction))
1417         {
1418           /* if we're leaving the current page and there isn't nothing focusable
1419              in the action area, try to clear focus and go back to the page */
1420           gtk_window_set_focus (GTK_WINDOW (widget), NULL);
1421           if (priv->current_page != NULL)
1422             gtk_widget_child_focus (priv->current_page->page, direction);
1423         }
1424     }
1425
1426   return TRUE;
1427 }
1428
1429 static void
1430 gtk_assistant_add (GtkContainer *container,
1431                    GtkWidget    *page)
1432 {
1433   gtk_assistant_append_page (GTK_ASSISTANT (container), page);
1434 }
1435
1436 static void
1437 gtk_assistant_remove (GtkContainer *container,
1438                       GtkWidget    *page)
1439 {
1440   GtkAssistant *assistant = (GtkAssistant*) container;
1441   GList *element;
1442
1443   element = find_page (assistant, page);
1444
1445   if (element)
1446     {
1447       remove_page (assistant, element);
1448       gtk_widget_queue_resize ((GtkWidget *) container);
1449     }
1450 }
1451
1452 static void
1453 gtk_assistant_forall (GtkContainer *container,
1454                       gboolean      include_internals,
1455                       GtkCallback   callback,
1456                       gpointer      callback_data)
1457 {
1458   GtkAssistant *assistant = (GtkAssistant*) container;
1459   GtkAssistantPrivate *priv = assistant->priv;
1460   GList *pages;
1461
1462   if (include_internals)
1463     {
1464       (*callback) (priv->header_image, callback_data);
1465       (*callback) (priv->sidebar_image, callback_data);
1466       (*callback) (priv->action_area, callback_data);
1467     }
1468
1469   pages = priv->pages;
1470
1471   while (pages)
1472     {
1473       GtkAssistantPage *page = (GtkAssistantPage *) pages->data;
1474
1475       (*callback) (page->page, callback_data);
1476
1477       if (include_internals)
1478         (*callback) (page->title, callback_data);
1479
1480       pages = pages->next;
1481     }
1482 }
1483
1484 /**
1485  * gtk_assistant_new:
1486  * 
1487  * Creates a new #GtkAssistant.
1488  *
1489  * Return value: a newly created #GtkAssistant
1490  *
1491  * Since: 2.10
1492  **/
1493 GtkWidget*
1494 gtk_assistant_new (void)
1495 {
1496   GtkWidget *assistant;
1497
1498   assistant = g_object_new (GTK_TYPE_ASSISTANT, NULL);
1499
1500   return assistant;
1501 }
1502
1503 /**
1504  * gtk_assistant_get_current_page:
1505  * @assistant: a #GtkAssistant
1506  *
1507  * Returns the page number of the current page
1508  *
1509  * Return value: The index (starting from 0) of the current page in
1510  * the @assistant, if the @assistant has no pages, -1 will be returned
1511  *
1512  * Since: 2.10
1513  **/
1514 gint
1515 gtk_assistant_get_current_page (GtkAssistant *assistant)
1516 {
1517   GtkAssistantPrivate *priv;
1518
1519   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), -1);
1520
1521   priv = assistant->priv;
1522
1523   if (!priv->pages || !priv->current_page)
1524     return -1;
1525
1526   return g_list_index (priv->pages, priv->current_page);
1527 }
1528
1529 /**
1530  * gtk_assistant_set_current_page:
1531  * @assistant: a #GtkAssistant
1532  * @page_num: index of the page to switch to, starting from 0.
1533  *            If negative, the last page will be used. If greater
1534  *            than the number of pages in the @assistant, nothing
1535  *            will be done.
1536  *
1537  * Switches the page to @page_num. Note that this will only be necessary
1538  * in custom buttons, as the @assistant flow can be set with
1539  * gtk_assistant_set_forward_page_func().
1540  *
1541  * Since: 2.10
1542  **/
1543 void
1544 gtk_assistant_set_current_page (GtkAssistant *assistant,
1545                                 gint          page_num)
1546 {
1547   GtkAssistantPrivate *priv;
1548   GtkAssistantPage *page;
1549
1550   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1551
1552   priv = assistant->priv;
1553
1554   if (page_num >= 0)
1555     page = (GtkAssistantPage *) g_list_nth_data (priv->pages, page_num);
1556   else
1557     page = (GtkAssistantPage *) g_list_last (priv->pages);
1558
1559   g_return_if_fail (page != NULL);
1560
1561   if (priv->current_page == page)
1562     return;
1563
1564   /* only add the page to the visited list if the
1565    * assistant is mapped, if not, just use it as an
1566    * initial page setting, for the cases where the
1567    * initial page is != to 0
1568    */
1569   if (GTK_WIDGET_MAPPED (assistant))
1570     priv->visited_pages = g_slist_prepend (priv->visited_pages,
1571                                            priv->current_page);
1572
1573   set_current_page (assistant, page);
1574 }
1575
1576 /**
1577  * gtk_assistant_get_n_pages:
1578  * @assistant: a #GtkAssistant
1579  *
1580  * Returns the number of pages in the @assistant
1581  *
1582  * Return value: The number of pages in the @assistant.
1583  *
1584  * Since: 2.10
1585  **/
1586 gint
1587 gtk_assistant_get_n_pages (GtkAssistant *assistant)
1588 {
1589   GtkAssistantPrivate *priv;
1590
1591   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1592
1593   priv = assistant->priv;
1594
1595   return g_list_length (priv->pages);
1596 }
1597
1598 /**
1599  * gtk_assistant_get_nth_page:
1600  * @assistant: a #GtkAssistant
1601  * @page_num: The index of a page in the @assistant, or -1 to get the last page;
1602  *
1603  * Returns the child widget contained in page number @page_num.
1604  *
1605  * Return value: The child widget, or %NULL if @page_num is out of bounds.
1606  *
1607  * Since: 2.10
1608  **/
1609 GtkWidget*
1610 gtk_assistant_get_nth_page (GtkAssistant *assistant,
1611                             gint          page_num)
1612 {
1613   GtkAssistantPrivate *priv;
1614   GtkAssistantPage *page;
1615   GList *elem;
1616
1617   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1618
1619   priv = assistant->priv;
1620
1621   elem = g_list_nth (priv->pages, page_num);
1622
1623   if (!elem)
1624     return NULL;
1625
1626   page = (GtkAssistantPage *) elem->data;
1627
1628   return page->page;
1629 }
1630
1631 /**
1632  * gtk_assistant_prepend_page:
1633  * @assistant: a #GtkAssistant
1634  * @page: a #GtkWidget
1635  *
1636  * Prepends a page to the @assistant.
1637  *
1638  * Return value: the index (starting at 0) of the inserted page
1639  *
1640  * Since: 2.10
1641  **/
1642 gint
1643 gtk_assistant_prepend_page (GtkAssistant *assistant,
1644                             GtkWidget    *page)
1645 {
1646   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1647   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1648
1649   return gtk_assistant_insert_page (assistant, page, 0);
1650 }
1651
1652 /**
1653  * gtk_assistant_append_page:
1654  * @assistant: a #GtkAssistant
1655  * @page: a #GtkWidget
1656  *
1657  * Appends a page to the @assistant.
1658  *
1659  * Return value: the index (starting at 0) of the inserted page
1660  *
1661  * Since: 2.10
1662  **/
1663 gint
1664 gtk_assistant_append_page (GtkAssistant *assistant,
1665                            GtkWidget    *page)
1666 {
1667   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1668   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1669
1670   return gtk_assistant_insert_page (assistant, page, -1);
1671 }
1672
1673 /**
1674  * gtk_assistant_insert_page:
1675  * @assistant: a #GtkAssistant
1676  * @page: a #GtkWidget
1677  * @position: the index (starting at 0) at which to insert the page,
1678  *            or -1 to append the page to the @assistant
1679  *
1680  * Inserts a page in the @assistant at a given position.
1681  *
1682  * Return value: the index (starting from 0) of the inserted page
1683  *
1684  * Since: 2.10
1685  **/
1686 gint
1687 gtk_assistant_insert_page (GtkAssistant *assistant,
1688                            GtkWidget    *page,
1689                            gint          position)
1690 {
1691   GtkAssistantPrivate *priv;
1692   GtkAssistantPage *page_info;
1693   gint n_pages;
1694
1695   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1696   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1697   g_return_val_if_fail (page->parent == NULL, 0);
1698   g_return_val_if_fail (!GTK_WIDGET_TOPLEVEL (page), 0);
1699
1700   priv = assistant->priv;
1701
1702   page_info = g_slice_new0 (GtkAssistantPage);
1703   page_info->page  = page;
1704   page_info->title = gtk_label_new (NULL);
1705
1706   g_signal_connect (G_OBJECT (page), "notify::visible",
1707                     G_CALLBACK (on_page_notify_visibility), assistant);
1708
1709   gtk_misc_set_alignment (GTK_MISC (page_info->title), 0.,0.5);
1710   set_title_colors (GTK_WIDGET (assistant), page_info->title);
1711   set_title_font   (GTK_WIDGET (assistant), page_info->title);
1712   gtk_widget_show  (page_info->title);
1713
1714   n_pages = g_list_length (priv->pages);
1715
1716   if (position < 0 || position > n_pages)
1717     position = n_pages;
1718
1719   priv->pages = g_list_insert (priv->pages, page_info, position);
1720
1721   gtk_widget_set_child_visible (page_info->page, FALSE);
1722   gtk_widget_set_parent (page_info->page,  GTK_WIDGET (assistant));
1723   gtk_widget_set_parent (page_info->title, GTK_WIDGET (assistant));
1724
1725   if (GTK_WIDGET_REALIZED (GTK_WIDGET (assistant)))
1726     {
1727       gtk_widget_realize (page_info->page);
1728       gtk_widget_realize (page_info->title);
1729     }
1730
1731   gtk_widget_queue_resize (GTK_WIDGET (assistant));
1732
1733   return position;
1734 }
1735
1736 /**
1737  * gtk_assistant_set_forward_page_func:
1738  * @assistant: a #GtkAssistant
1739  * @page_func: the #GtkAssistantPageFunc, or %NULL to use the default one
1740  * @data: user data for @page_func
1741  * @destroy: destroy notifier for @data
1742  *
1743  * Sets the page forwarding function to be @page_func, this function will
1744  * be used to determine what will be the next page when the user presses
1745  * the forward button. Setting @page_func to %NULL will make the assistant
1746  * to use the default forward function, which just goes to the next visible 
1747  * page.
1748  *
1749  * Since: 2.10
1750  **/
1751 void
1752 gtk_assistant_set_forward_page_func (GtkAssistant         *assistant,
1753                                      GtkAssistantPageFunc  page_func,
1754                                      gpointer              data,
1755                                      GDestroyNotify        destroy)
1756 {
1757   GtkAssistantPrivate *priv;
1758
1759   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1760
1761   priv = assistant->priv;
1762
1763   if (priv->forward_data_destroy &&
1764       priv->forward_function_data)
1765     (*priv->forward_data_destroy) (priv->forward_function_data);
1766
1767   if (page_func)
1768     {
1769       priv->forward_function = page_func;
1770       priv->forward_function_data = data;
1771       priv->forward_data_destroy = destroy;
1772     }
1773   else
1774     {
1775       priv->forward_function = default_forward_function;
1776       priv->forward_function_data = assistant;
1777       priv->forward_data_destroy = NULL;
1778     }
1779
1780   /* Page flow has possibly changed, so the
1781      buttons state might need to change too */
1782   set_assistant_buttons_state (assistant);
1783 }
1784
1785 /**
1786  * gtk_assistant_add_action_widget:
1787  * @assistant: a #GtkAssistant
1788  * @child: a #GtkWidget
1789  * 
1790  * Adds a widget to the action area of a #GtkAssistant.
1791  *
1792  * Since: 2.10
1793  **/
1794 void
1795 gtk_assistant_add_action_widget (GtkAssistant *assistant,
1796                                  GtkWidget    *child)
1797 {
1798   GtkAssistantPrivate *priv;
1799
1800   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1801   g_return_if_fail (GTK_IS_WIDGET (child));
1802
1803   priv = assistant->priv;
1804
1805   if (GTK_IS_BUTTON (child))
1806     gtk_size_group_add_widget (priv->size_group, child);
1807
1808   gtk_box_pack_end (GTK_BOX (priv->action_area), child, FALSE, FALSE, 0);
1809 }
1810
1811 /**
1812  * gtk_assistant_remove_action_widget:
1813  * @assistant: a #GtkAssistant
1814  * @child: a #GtkWidget
1815  *
1816  * Removes a widget from the action area of a #GtkAssistant.
1817  *
1818  * Since: 2.10
1819  **/
1820 void
1821 gtk_assistant_remove_action_widget (GtkAssistant *assistant,
1822                                     GtkWidget    *child)
1823 {
1824   GtkAssistantPrivate *priv;
1825
1826   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1827   g_return_if_fail (GTK_IS_WIDGET (child));
1828
1829   priv = assistant->priv;
1830
1831   if (GTK_IS_BUTTON (child))
1832     gtk_size_group_remove_widget (priv->size_group, child);
1833
1834   gtk_container_remove (GTK_CONTAINER (priv->action_area), child);
1835 }
1836
1837 /**
1838  * gtk_assistant_set_page_title:
1839  * @assistant: a #GtkAssistant
1840  * @page: a page of @assistant
1841  * @title: the new title for @page
1842  * 
1843  * Sets a title for @page. The title is displayed in the header
1844  * area of the assistant when @page is the current page.
1845  *
1846  * Since: 2.10
1847  **/
1848 void
1849 gtk_assistant_set_page_title (GtkAssistant *assistant,
1850                               GtkWidget    *page,
1851                               const gchar  *title)
1852 {
1853   GtkAssistantPage *page_info;
1854   GList *child;
1855
1856   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1857   g_return_if_fail (GTK_IS_WIDGET (page));
1858
1859   child = find_page (assistant, page);
1860
1861   g_return_if_fail (child != NULL);
1862
1863   page_info = (GtkAssistantPage*) child->data;
1864
1865   gtk_label_set_text ((GtkLabel*) page_info->title, title);
1866   gtk_widget_queue_resize (GTK_WIDGET (assistant));
1867   gtk_widget_child_notify (page, "title");
1868 }
1869
1870 /**
1871  * gtk_assistant_get_page_title:
1872  * @assistant: a #GtkAssistant
1873  * @page: a page of @assistant
1874  * 
1875  * Gets the title for @page. 
1876  * 
1877  * Return value: the title for @page.
1878  *
1879  * Since: 2.10
1880  **/
1881 G_CONST_RETURN gchar*
1882 gtk_assistant_get_page_title (GtkAssistant *assistant,
1883                               GtkWidget    *page)
1884 {
1885   GtkAssistantPage *page_info;
1886   GList *child;
1887
1888   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1889   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
1890
1891   child = find_page (assistant, page);
1892
1893   g_return_val_if_fail (child != NULL, NULL);
1894
1895   page_info = (GtkAssistantPage*) child->data;
1896
1897   return gtk_label_get_text ((GtkLabel*) page_info->title);
1898 }
1899
1900 /**
1901  * gtk_assistant_set_page_type:
1902  * @assistant: a #GtkAssistant
1903  * @page: a page of @assistant
1904  * @type: the new type for @page
1905  * 
1906  * Sets the page type for @page. The page type determines the page
1907  * behavior in the @assistant.
1908  *
1909  * Since: 2.10
1910  **/
1911 void
1912 gtk_assistant_set_page_type (GtkAssistant         *assistant,
1913                              GtkWidget            *page,
1914                              GtkAssistantPageType  type)
1915 {
1916   GtkAssistantPrivate *priv;
1917   GtkAssistantPage *page_info;
1918   GList *child;
1919
1920   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1921   g_return_if_fail (GTK_IS_WIDGET (page));
1922
1923   priv = assistant->priv;
1924   child = find_page (assistant, page);
1925
1926   g_return_if_fail (child != NULL);
1927
1928   page_info = (GtkAssistantPage*) child->data;
1929
1930   if (type != page_info->type)
1931     {
1932       page_info->type = type;
1933
1934       /* backwards compatibility to the era before fixing bug 604289 */
1935       if (type == GTK_ASSISTANT_PAGE_SUMMARY && !page_info->complete_set)
1936         {
1937           gtk_assistant_set_page_complete (assistant, page, TRUE);
1938           page_info->complete_set = FALSE;
1939         }
1940
1941       /* Always set buttons state, a change in a future page
1942          might change current page buttons */
1943       set_assistant_buttons_state (assistant);
1944
1945       gtk_widget_child_notify (page, "page-type");
1946     }
1947 }
1948
1949 /**
1950  * gtk_assistant_get_page_type:
1951  * @assistant: a #GtkAssistant
1952  * @page: a page of @assistant
1953  *
1954  * Gets the page type of @page.
1955  *
1956  * Return value: the page type of @page.
1957  *
1958  * Since: 2.10
1959  **/
1960 GtkAssistantPageType
1961 gtk_assistant_get_page_type (GtkAssistant *assistant,
1962                              GtkWidget    *page)
1963 {
1964   GtkAssistantPage *page_info;
1965   GList *child;
1966
1967   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), GTK_ASSISTANT_PAGE_CONTENT);
1968   g_return_val_if_fail (GTK_IS_WIDGET (page), GTK_ASSISTANT_PAGE_CONTENT);
1969
1970   child = find_page (assistant, page);
1971
1972   g_return_val_if_fail (child != NULL, GTK_ASSISTANT_PAGE_CONTENT);
1973
1974   page_info = (GtkAssistantPage*) child->data;
1975
1976   return page_info->type;
1977 }
1978
1979 /**
1980  * gtk_assistant_set_page_header_image:
1981  * @assistant: a #GtkAssistant
1982  * @page: a page of @assistant
1983  * @pixbuf: (allow-none): the new header image @page
1984  *
1985  * Sets a header image for @page. This image is displayed in the header
1986  * area of the assistant when @page is the current page.
1987  *
1988  * Since: 2.10
1989  **/
1990 void
1991 gtk_assistant_set_page_header_image (GtkAssistant *assistant,
1992                                      GtkWidget    *page,
1993                                      GdkPixbuf    *pixbuf)
1994 {
1995   GtkAssistantPrivate *priv;
1996   GtkAssistantPage *page_info;
1997   GList *child;
1998
1999   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2000   g_return_if_fail (GTK_IS_WIDGET (page));
2001   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2002
2003   priv = assistant->priv;
2004   child = find_page (assistant, page);
2005
2006   g_return_if_fail (child != NULL);
2007
2008   page_info = (GtkAssistantPage*) child->data;
2009
2010   if (pixbuf != page_info->header_image)
2011     {
2012       if (page_info->header_image)
2013         {
2014           g_object_unref (page_info->header_image);
2015           page_info->header_image = NULL;
2016         }
2017
2018       if (pixbuf)
2019         page_info->header_image = g_object_ref (pixbuf);
2020
2021       if (page_info == priv->current_page)
2022         set_assistant_header_image (assistant);
2023
2024       gtk_widget_child_notify (page, "header-image");
2025     }
2026 }
2027
2028 /**
2029  * gtk_assistant_get_page_header_image:
2030  * @assistant: a #GtkAssistant
2031  * @page: a page of @assistant
2032  * 
2033  * Gets the header image for @page. 
2034  * 
2035  * Return value: the header image for @page, or %NULL
2036  * if there's no header image for the page.
2037  *
2038  * Since: 2.10
2039  **/
2040 GdkPixbuf*
2041 gtk_assistant_get_page_header_image (GtkAssistant *assistant,
2042                                      GtkWidget    *page)
2043 {
2044   GtkAssistantPage *page_info;
2045   GList *child;
2046
2047   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2048   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2049
2050   child = find_page (assistant, page);
2051
2052   g_return_val_if_fail (child != NULL, NULL);
2053
2054   page_info = (GtkAssistantPage*) child->data;
2055
2056   return page_info->header_image;
2057 }
2058
2059 /**
2060  * gtk_assistant_set_page_side_image:
2061  * @assistant: a #GtkAssistant
2062  * @page: a page of @assistant
2063  * @pixbuf: (allow-none): the new header image @page
2064  *
2065  * Sets a header image for @page. This image is displayed in the side
2066  * area of the assistant when @page is the current page.
2067  *
2068  * Since: 2.10
2069  **/
2070 void
2071 gtk_assistant_set_page_side_image (GtkAssistant *assistant,
2072                                    GtkWidget    *page,
2073                                    GdkPixbuf    *pixbuf)
2074 {
2075   GtkAssistantPrivate *priv;
2076   GtkAssistantPage *page_info;
2077   GList *child;
2078
2079   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2080   g_return_if_fail (GTK_IS_WIDGET (page));
2081   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2082
2083   priv = assistant->priv;
2084   child = find_page (assistant, page);
2085
2086   g_return_if_fail (child != NULL);
2087
2088   page_info = (GtkAssistantPage*) child->data;
2089
2090   if (pixbuf != page_info->sidebar_image)
2091     {
2092       if (page_info->sidebar_image)
2093         {
2094           g_object_unref (page_info->sidebar_image);
2095           page_info->sidebar_image = NULL;
2096         }
2097
2098       if (pixbuf)
2099         page_info->sidebar_image = g_object_ref (pixbuf);
2100
2101       if (page_info == priv->current_page)
2102         set_assistant_sidebar_image (assistant);
2103
2104       gtk_widget_child_notify (page, "sidebar-image");
2105     }
2106 }
2107
2108 /**
2109  * gtk_assistant_get_page_side_image:
2110  * @assistant: a #GtkAssistant
2111  * @page: a page of @assistant
2112  * 
2113  * Gets the header image for @page. 
2114  * 
2115  * Return value: the side image for @page, or %NULL
2116  * if there's no side image for the page.
2117  *
2118  * Since: 2.10
2119  **/
2120 GdkPixbuf*
2121 gtk_assistant_get_page_side_image (GtkAssistant *assistant,
2122                                    GtkWidget    *page)
2123 {
2124   GtkAssistantPage *page_info;
2125   GList *child;
2126
2127   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2128   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2129
2130   child = find_page (assistant, page);
2131
2132   g_return_val_if_fail (child != NULL, NULL);
2133
2134   page_info = (GtkAssistantPage*) child->data;
2135
2136   return page_info->sidebar_image;
2137 }
2138
2139 /**
2140  * gtk_assistant_set_page_complete:
2141  * @assistant: a #GtkAssistant
2142  * @page: a page of @assistant
2143  * @complete: the completeness status of the page
2144  * 
2145  * Sets whether @page contents are complete. This will make
2146  * @assistant update the buttons state to be able to continue the task.
2147  *
2148  * Since: 2.10
2149  **/
2150 void
2151 gtk_assistant_set_page_complete (GtkAssistant *assistant,
2152                                  GtkWidget    *page,
2153                                  gboolean      complete)
2154 {
2155   GtkAssistantPrivate *priv;
2156   GtkAssistantPage *page_info;
2157   GList *child;
2158
2159   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2160   g_return_if_fail (GTK_IS_WIDGET (page));
2161
2162   priv = assistant->priv;
2163   child = find_page (assistant, page);
2164
2165   g_return_if_fail (child != NULL);
2166
2167   page_info = (GtkAssistantPage*) child->data;
2168
2169   if (complete != page_info->complete)
2170     {
2171       page_info->complete = complete;
2172       page_info->complete_set = TRUE;
2173
2174       /* Always set buttons state, a change in a future page
2175          might change current page buttons */
2176       set_assistant_buttons_state (assistant);
2177
2178       gtk_widget_child_notify (page, "complete");
2179     }
2180 }
2181
2182 /**
2183  * gtk_assistant_get_page_complete:
2184  * @assistant: a #GtkAssistant
2185  * @page: a page of @assistant
2186  * 
2187  * Gets whether @page is complete.
2188  * 
2189  * Return value: %TRUE if @page is complete.
2190  *
2191  * Since: 2.10
2192  **/
2193 gboolean
2194 gtk_assistant_get_page_complete (GtkAssistant *assistant,
2195                                  GtkWidget    *page)
2196 {
2197   GtkAssistantPage *page_info;
2198   GList *child;
2199
2200   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), FALSE);
2201   g_return_val_if_fail (GTK_IS_WIDGET (page), FALSE);
2202
2203   child = find_page (assistant, page);
2204
2205   g_return_val_if_fail (child != NULL, FALSE);
2206
2207   page_info = (GtkAssistantPage*) child->data;
2208
2209   return page_info->complete;
2210 }
2211
2212 /**
2213  * gtk_assistant_update_buttons_state:
2214  * @assistant: a #GtkAssistant
2215  * 
2216  * Forces @assistant to recompute the buttons state.
2217  * 
2218  * GTK+ automatically takes care of this in most situations, 
2219  * e.g. when the user goes to a different page, or when the
2220  * visibility or completeness of a page changes.
2221  *
2222  * One situation where it can be necessary to call this
2223  * function is when changing a value on the current page
2224  * affects the future page flow of the assistant.
2225  *
2226  * Since: 2.10
2227  **/
2228 void
2229 gtk_assistant_update_buttons_state (GtkAssistant *assistant)
2230 {
2231   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2232
2233   set_assistant_buttons_state (assistant);
2234 }
2235
2236
2237
2238 /* accessible implementation */
2239
2240 static gint
2241 gtk_assistant_accessible_get_n_children (AtkObject *accessible)
2242 {
2243   GtkAssistant *assistant;
2244   GtkWidget *widget;
2245
2246   widget = GTK_ACCESSIBLE (accessible)->widget;
2247
2248   if (!widget)
2249     return 0;
2250
2251   assistant = GTK_ASSISTANT (widget);
2252    
2253   return g_list_length (assistant->priv->pages) + 1;
2254 }
2255
2256
2257 static AtkObject *
2258 gtk_assistant_accessible_ref_child (AtkObject *accessible,
2259                                     gint       index)
2260 {
2261   GtkAssistant *assistant;
2262   GtkAssistantPrivate *priv;
2263   GtkWidget *widget, *child;
2264   gint n_pages;
2265   AtkObject *obj;
2266   const gchar *title;
2267
2268   widget = GTK_ACCESSIBLE (accessible)->widget;
2269   if (!widget)
2270     return NULL;
2271
2272   assistant = GTK_ASSISTANT (widget);
2273   priv = assistant->priv;
2274   n_pages = g_list_length (priv->pages);
2275
2276   if (index < 0)
2277     return NULL;
2278   else if (index < n_pages)
2279     {
2280       GtkAssistantPage *page = g_list_nth_data (priv->pages, index);
2281
2282       child = page->page;
2283       title = gtk_assistant_get_page_title (assistant, child);
2284     }
2285   else if (index == n_pages)
2286     {
2287       child = priv->action_area;
2288       title = NULL;
2289     }
2290   else
2291     return NULL;
2292   
2293   obj = gtk_widget_get_accessible (child);
2294
2295   if (title)
2296     atk_object_set_name (obj, title);
2297
2298   return g_object_ref (obj);
2299 }
2300
2301 static void
2302 gtk_assistant_accessible_class_init (AtkObjectClass *class)
2303 {
2304   class->get_n_children = gtk_assistant_accessible_get_n_children;
2305   class->ref_child = gtk_assistant_accessible_ref_child;
2306 }
2307
2308 static GType
2309 gtk_assistant_accessible_get_type (void)
2310 {
2311   static GType type = 0;
2312   
2313   if (!type)
2314     {
2315       /*
2316        * Figure out the size of the class and instance
2317        * we are deriving from
2318        */
2319       AtkObjectFactory *factory;
2320       GType derived_type;
2321       GTypeQuery query;
2322       GType derived_atk_type;
2323
2324       derived_type = g_type_parent (GTK_TYPE_ASSISTANT);
2325       factory = atk_registry_get_factory (atk_get_default_registry (),
2326                                           derived_type);
2327       derived_atk_type = atk_object_factory_get_accessible_type (factory);
2328       g_type_query (derived_atk_type, &query);
2329
2330       type = g_type_register_static_simple (derived_atk_type,
2331                                             I_("GtkAssistantAccessible"),
2332                                             query.class_size,
2333                                             (GClassInitFunc) gtk_assistant_accessible_class_init,
2334                                             query.instance_size,
2335                                             NULL, 0);
2336     }
2337
2338   return type;
2339 }
2340
2341 static AtkObject *
2342 gtk_assistant_accessible_new (GObject *obj)
2343 {
2344   AtkObject *accessible;
2345
2346   g_return_val_if_fail (GTK_IS_ASSISTANT (obj), NULL);
2347
2348   accessible = g_object_new (gtk_assistant_accessible_get_type (), NULL);
2349   atk_object_initialize (accessible, obj);
2350
2351   return accessible;
2352 }
2353
2354 static GType
2355 gtk_assistant_accessible_factory_get_accessible_type (void)
2356 {
2357   return gtk_assistant_accessible_get_type ();
2358 }
2359
2360 static AtkObject*
2361 gtk_assistant_accessible_factory_create_accessible (GObject *obj)
2362 {
2363   return gtk_assistant_accessible_new (obj);
2364 }
2365
2366 static void
2367 gtk_assistant_accessible_factory_class_init (AtkObjectFactoryClass *class)
2368 {
2369   class->create_accessible = gtk_assistant_accessible_factory_create_accessible;
2370   class->get_accessible_type = gtk_assistant_accessible_factory_get_accessible_type;
2371 }
2372
2373 static GType
2374 gtk_assistant_accessible_factory_get_type (void)
2375 {
2376   static GType type = 0;
2377
2378   if (!type)
2379     {
2380       type = g_type_register_static_simple (ATK_TYPE_OBJECT_FACTORY,
2381                                             I_("GtkAssistantAccessibleFactory"),
2382                                             sizeof (AtkObjectFactoryClass),
2383                                             (GClassInitFunc) gtk_assistant_accessible_factory_class_init,
2384                                             sizeof (AtkObjectFactory),
2385                                             NULL, 0);
2386     }
2387
2388   return type;
2389 }
2390
2391 static AtkObject *
2392 gtk_assistant_get_accessible (GtkWidget *widget)
2393 {
2394   static gboolean first_time = TRUE;
2395
2396   if (first_time)
2397     {
2398       AtkObjectFactory *factory;
2399       AtkRegistry *registry;
2400       GType derived_type;
2401       GType derived_atk_type;
2402
2403       /*
2404        * Figure out whether accessibility is enabled by looking at the
2405        * type of the accessible object which would be created for
2406        * the parent type of GtkAssistant.
2407        */
2408       derived_type = g_type_parent (GTK_TYPE_ASSISTANT);
2409
2410       registry = atk_get_default_registry ();
2411       factory = atk_registry_get_factory (registry,
2412                                           derived_type);
2413       derived_atk_type = atk_object_factory_get_accessible_type (factory);
2414       if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
2415         {
2416           atk_registry_set_factory_type (registry,
2417                                          GTK_TYPE_ASSISTANT,
2418                                          gtk_assistant_accessible_factory_get_type ());
2419         }
2420       first_time = FALSE;
2421     }
2422
2423   return GTK_WIDGET_CLASS (gtk_assistant_parent_class)->get_accessible (widget);
2424 }
2425
2426
2427 static GtkBuildableIface *parent_buildable_iface;
2428
2429 static void
2430 gtk_assistant_buildable_interface_init (GtkBuildableIface *iface)
2431 {
2432   parent_buildable_iface = g_type_interface_peek_parent (iface);
2433   iface->get_internal_child = gtk_assistant_buildable_get_internal_child;
2434   iface->custom_tag_start = gtk_assistant_buildable_custom_tag_start;
2435   iface->custom_finished = gtk_assistant_buildable_custom_finished;
2436 }
2437
2438 static GObject *
2439 gtk_assistant_buildable_get_internal_child (GtkBuildable *buildable,
2440                                             GtkBuilder   *builder,
2441                                             const gchar  *childname)
2442 {
2443     if (strcmp (childname, "action_area") == 0)
2444       return G_OBJECT (GTK_ASSISTANT (buildable)->priv->action_area);
2445
2446     return parent_buildable_iface->get_internal_child (buildable,
2447                                                        builder,
2448                                                        childname);
2449 }
2450
2451 gboolean
2452 gtk_assistant_buildable_custom_tag_start (GtkBuildable  *buildable,
2453                                           GtkBuilder    *builder,
2454                                           GObject       *child,
2455                                           const gchar   *tagname,
2456                                           GMarkupParser *parser,
2457                                           gpointer      *data)
2458 {
2459   return parent_buildable_iface->custom_tag_start (buildable, builder, child,
2460                                                    tagname, parser, data);
2461 }
2462
2463 static void
2464 gtk_assistant_buildable_custom_finished (GtkBuildable *buildable,
2465                                          GtkBuilder   *builder,
2466                                          GObject      *child,
2467                                          const gchar  *tagname,
2468                                          gpointer      user_data)
2469 {
2470   parent_buildable_iface->custom_finished (buildable, builder, child,
2471                                            tagname, user_data);
2472 }
2473
2474
2475 #define __GTK_ASSISTANT_C__
2476 #include "gtkaliasdef.c"