]> Pileus Git - ~andy/gtk/blob - gtk/gtkassistant.c
Don't use GTK_WIDGET_*SET_FLAGS (wid, GTK_CAN_DEFAULT)
[~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_get_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_is_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_get_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_get_visible (button[i]) && gtk_widget_get_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_get_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_can_default (assistant->close, TRUE);
764   gtk_widget_set_can_default (assistant->apply, TRUE);
765   gtk_widget_set_can_default (assistant->forward, TRUE);
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_get_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_get_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_get_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_get_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_get_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_is_drawable (priv->sidebar_image))
1280     gtk_widget_unmap (priv->sidebar_image);
1281
1282   if (priv->current_page &&
1283       gtk_widget_is_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_get_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_is_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)->data;
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   g_return_val_if_fail (page_num >= -1, NULL);
1619
1620   priv = assistant->priv;
1621
1622   if (page_num == -1)
1623     elem = g_list_last (priv->pages);
1624   else
1625     elem = g_list_nth (priv->pages, page_num);
1626
1627   if (!elem)
1628     return NULL;
1629
1630   page = (GtkAssistantPage *) elem->data;
1631
1632   return page->page;
1633 }
1634
1635 /**
1636  * gtk_assistant_prepend_page:
1637  * @assistant: a #GtkAssistant
1638  * @page: a #GtkWidget
1639  *
1640  * Prepends a page to the @assistant.
1641  *
1642  * Return value: the index (starting at 0) of the inserted page
1643  *
1644  * Since: 2.10
1645  **/
1646 gint
1647 gtk_assistant_prepend_page (GtkAssistant *assistant,
1648                             GtkWidget    *page)
1649 {
1650   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1651   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1652
1653   return gtk_assistant_insert_page (assistant, page, 0);
1654 }
1655
1656 /**
1657  * gtk_assistant_append_page:
1658  * @assistant: a #GtkAssistant
1659  * @page: a #GtkWidget
1660  *
1661  * Appends a page to the @assistant.
1662  *
1663  * Return value: the index (starting at 0) of the inserted page
1664  *
1665  * Since: 2.10
1666  **/
1667 gint
1668 gtk_assistant_append_page (GtkAssistant *assistant,
1669                            GtkWidget    *page)
1670 {
1671   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1672   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1673
1674   return gtk_assistant_insert_page (assistant, page, -1);
1675 }
1676
1677 /**
1678  * gtk_assistant_insert_page:
1679  * @assistant: a #GtkAssistant
1680  * @page: a #GtkWidget
1681  * @position: the index (starting at 0) at which to insert the page,
1682  *            or -1 to append the page to the @assistant
1683  *
1684  * Inserts a page in the @assistant at a given position.
1685  *
1686  * Return value: the index (starting from 0) of the inserted page
1687  *
1688  * Since: 2.10
1689  **/
1690 gint
1691 gtk_assistant_insert_page (GtkAssistant *assistant,
1692                            GtkWidget    *page,
1693                            gint          position)
1694 {
1695   GtkAssistantPrivate *priv;
1696   GtkAssistantPage *page_info;
1697   gint n_pages;
1698
1699   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1700   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1701   g_return_val_if_fail (page->parent == NULL, 0);
1702   g_return_val_if_fail (!gtk_widget_is_toplevel (page), 0);
1703
1704   priv = assistant->priv;
1705
1706   page_info = g_slice_new0 (GtkAssistantPage);
1707   page_info->page  = page;
1708   page_info->title = gtk_label_new (NULL);
1709
1710   g_signal_connect (G_OBJECT (page), "notify::visible",
1711                     G_CALLBACK (on_page_notify_visibility), assistant);
1712
1713   gtk_misc_set_alignment (GTK_MISC (page_info->title), 0.,0.5);
1714   set_title_colors (GTK_WIDGET (assistant), page_info->title);
1715   set_title_font   (GTK_WIDGET (assistant), page_info->title);
1716   gtk_widget_show  (page_info->title);
1717
1718   n_pages = g_list_length (priv->pages);
1719
1720   if (position < 0 || position > n_pages)
1721     position = n_pages;
1722
1723   priv->pages = g_list_insert (priv->pages, page_info, position);
1724
1725   gtk_widget_set_child_visible (page_info->page, FALSE);
1726   gtk_widget_set_parent (page_info->page,  GTK_WIDGET (assistant));
1727   gtk_widget_set_parent (page_info->title, GTK_WIDGET (assistant));
1728
1729   if (GTK_WIDGET_REALIZED (GTK_WIDGET (assistant)))
1730     {
1731       gtk_widget_realize (page_info->page);
1732       gtk_widget_realize (page_info->title);
1733     }
1734
1735   gtk_widget_queue_resize (GTK_WIDGET (assistant));
1736
1737   return position;
1738 }
1739
1740 /**
1741  * gtk_assistant_set_forward_page_func:
1742  * @assistant: a #GtkAssistant
1743  * @page_func: (allow-none): the #GtkAssistantPageFunc, or %NULL to use the default one
1744  * @data: user data for @page_func
1745  * @destroy: destroy notifier for @data
1746  *
1747  * Sets the page forwarding function to be @page_func, this function will
1748  * be used to determine what will be the next page when the user presses
1749  * the forward button. Setting @page_func to %NULL will make the assistant
1750  * to use the default forward function, which just goes to the next visible 
1751  * page.
1752  *
1753  * Since: 2.10
1754  **/
1755 void
1756 gtk_assistant_set_forward_page_func (GtkAssistant         *assistant,
1757                                      GtkAssistantPageFunc  page_func,
1758                                      gpointer              data,
1759                                      GDestroyNotify        destroy)
1760 {
1761   GtkAssistantPrivate *priv;
1762
1763   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1764
1765   priv = assistant->priv;
1766
1767   if (priv->forward_data_destroy &&
1768       priv->forward_function_data)
1769     (*priv->forward_data_destroy) (priv->forward_function_data);
1770
1771   if (page_func)
1772     {
1773       priv->forward_function = page_func;
1774       priv->forward_function_data = data;
1775       priv->forward_data_destroy = destroy;
1776     }
1777   else
1778     {
1779       priv->forward_function = default_forward_function;
1780       priv->forward_function_data = assistant;
1781       priv->forward_data_destroy = NULL;
1782     }
1783
1784   /* Page flow has possibly changed, so the
1785      buttons state might need to change too */
1786   set_assistant_buttons_state (assistant);
1787 }
1788
1789 /**
1790  * gtk_assistant_add_action_widget:
1791  * @assistant: a #GtkAssistant
1792  * @child: a #GtkWidget
1793  * 
1794  * Adds a widget to the action area of a #GtkAssistant.
1795  *
1796  * Since: 2.10
1797  **/
1798 void
1799 gtk_assistant_add_action_widget (GtkAssistant *assistant,
1800                                  GtkWidget    *child)
1801 {
1802   GtkAssistantPrivate *priv;
1803
1804   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1805   g_return_if_fail (GTK_IS_WIDGET (child));
1806
1807   priv = assistant->priv;
1808
1809   if (GTK_IS_BUTTON (child))
1810     gtk_size_group_add_widget (priv->size_group, child);
1811
1812   gtk_box_pack_end (GTK_BOX (priv->action_area), child, FALSE, FALSE, 0);
1813 }
1814
1815 /**
1816  * gtk_assistant_remove_action_widget:
1817  * @assistant: a #GtkAssistant
1818  * @child: a #GtkWidget
1819  *
1820  * Removes a widget from the action area of a #GtkAssistant.
1821  *
1822  * Since: 2.10
1823  **/
1824 void
1825 gtk_assistant_remove_action_widget (GtkAssistant *assistant,
1826                                     GtkWidget    *child)
1827 {
1828   GtkAssistantPrivate *priv;
1829
1830   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1831   g_return_if_fail (GTK_IS_WIDGET (child));
1832
1833   priv = assistant->priv;
1834
1835   if (GTK_IS_BUTTON (child))
1836     gtk_size_group_remove_widget (priv->size_group, child);
1837
1838   gtk_container_remove (GTK_CONTAINER (priv->action_area), child);
1839 }
1840
1841 /**
1842  * gtk_assistant_set_page_title:
1843  * @assistant: a #GtkAssistant
1844  * @page: a page of @assistant
1845  * @title: the new title for @page
1846  * 
1847  * Sets a title for @page. The title is displayed in the header
1848  * area of the assistant when @page is the current page.
1849  *
1850  * Since: 2.10
1851  **/
1852 void
1853 gtk_assistant_set_page_title (GtkAssistant *assistant,
1854                               GtkWidget    *page,
1855                               const gchar  *title)
1856 {
1857   GtkAssistantPage *page_info;
1858   GList *child;
1859
1860   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1861   g_return_if_fail (GTK_IS_WIDGET (page));
1862
1863   child = find_page (assistant, page);
1864
1865   g_return_if_fail (child != NULL);
1866
1867   page_info = (GtkAssistantPage*) child->data;
1868
1869   gtk_label_set_text ((GtkLabel*) page_info->title, title);
1870   gtk_widget_queue_resize (GTK_WIDGET (assistant));
1871   gtk_widget_child_notify (page, "title");
1872 }
1873
1874 /**
1875  * gtk_assistant_get_page_title:
1876  * @assistant: a #GtkAssistant
1877  * @page: a page of @assistant
1878  * 
1879  * Gets the title for @page. 
1880  * 
1881  * Return value: the title for @page.
1882  *
1883  * Since: 2.10
1884  **/
1885 G_CONST_RETURN gchar*
1886 gtk_assistant_get_page_title (GtkAssistant *assistant,
1887                               GtkWidget    *page)
1888 {
1889   GtkAssistantPage *page_info;
1890   GList *child;
1891
1892   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1893   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
1894
1895   child = find_page (assistant, page);
1896
1897   g_return_val_if_fail (child != NULL, NULL);
1898
1899   page_info = (GtkAssistantPage*) child->data;
1900
1901   return gtk_label_get_text ((GtkLabel*) page_info->title);
1902 }
1903
1904 /**
1905  * gtk_assistant_set_page_type:
1906  * @assistant: a #GtkAssistant
1907  * @page: a page of @assistant
1908  * @type: the new type for @page
1909  * 
1910  * Sets the page type for @page. The page type determines the page
1911  * behavior in the @assistant.
1912  *
1913  * Since: 2.10
1914  **/
1915 void
1916 gtk_assistant_set_page_type (GtkAssistant         *assistant,
1917                              GtkWidget            *page,
1918                              GtkAssistantPageType  type)
1919 {
1920   GtkAssistantPrivate *priv;
1921   GtkAssistantPage *page_info;
1922   GList *child;
1923
1924   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1925   g_return_if_fail (GTK_IS_WIDGET (page));
1926
1927   priv = assistant->priv;
1928   child = find_page (assistant, page);
1929
1930   g_return_if_fail (child != NULL);
1931
1932   page_info = (GtkAssistantPage*) child->data;
1933
1934   if (type != page_info->type)
1935     {
1936       page_info->type = type;
1937
1938       /* backwards compatibility to the era before fixing bug 604289 */
1939       if (type == GTK_ASSISTANT_PAGE_SUMMARY && !page_info->complete_set)
1940         {
1941           gtk_assistant_set_page_complete (assistant, page, TRUE);
1942           page_info->complete_set = FALSE;
1943         }
1944
1945       /* Always set buttons state, a change in a future page
1946          might change current page buttons */
1947       set_assistant_buttons_state (assistant);
1948
1949       gtk_widget_child_notify (page, "page-type");
1950     }
1951 }
1952
1953 /**
1954  * gtk_assistant_get_page_type:
1955  * @assistant: a #GtkAssistant
1956  * @page: a page of @assistant
1957  *
1958  * Gets the page type of @page.
1959  *
1960  * Return value: the page type of @page.
1961  *
1962  * Since: 2.10
1963  **/
1964 GtkAssistantPageType
1965 gtk_assistant_get_page_type (GtkAssistant *assistant,
1966                              GtkWidget    *page)
1967 {
1968   GtkAssistantPage *page_info;
1969   GList *child;
1970
1971   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), GTK_ASSISTANT_PAGE_CONTENT);
1972   g_return_val_if_fail (GTK_IS_WIDGET (page), GTK_ASSISTANT_PAGE_CONTENT);
1973
1974   child = find_page (assistant, page);
1975
1976   g_return_val_if_fail (child != NULL, GTK_ASSISTANT_PAGE_CONTENT);
1977
1978   page_info = (GtkAssistantPage*) child->data;
1979
1980   return page_info->type;
1981 }
1982
1983 /**
1984  * gtk_assistant_set_page_header_image:
1985  * @assistant: a #GtkAssistant
1986  * @page: a page of @assistant
1987  * @pixbuf: (allow-none): the new header image @page
1988  *
1989  * Sets a header image for @page. This image is displayed in the header
1990  * area of the assistant when @page is the current page.
1991  *
1992  * Since: 2.10
1993  **/
1994 void
1995 gtk_assistant_set_page_header_image (GtkAssistant *assistant,
1996                                      GtkWidget    *page,
1997                                      GdkPixbuf    *pixbuf)
1998 {
1999   GtkAssistantPrivate *priv;
2000   GtkAssistantPage *page_info;
2001   GList *child;
2002
2003   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2004   g_return_if_fail (GTK_IS_WIDGET (page));
2005   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2006
2007   priv = assistant->priv;
2008   child = find_page (assistant, page);
2009
2010   g_return_if_fail (child != NULL);
2011
2012   page_info = (GtkAssistantPage*) child->data;
2013
2014   if (pixbuf != page_info->header_image)
2015     {
2016       if (page_info->header_image)
2017         {
2018           g_object_unref (page_info->header_image);
2019           page_info->header_image = NULL;
2020         }
2021
2022       if (pixbuf)
2023         page_info->header_image = g_object_ref (pixbuf);
2024
2025       if (page_info == priv->current_page)
2026         set_assistant_header_image (assistant);
2027
2028       gtk_widget_child_notify (page, "header-image");
2029     }
2030 }
2031
2032 /**
2033  * gtk_assistant_get_page_header_image:
2034  * @assistant: a #GtkAssistant
2035  * @page: a page of @assistant
2036  * 
2037  * Gets the header image for @page. 
2038  * 
2039  * Return value: the header image for @page, or %NULL
2040  * if there's no header image for the page.
2041  *
2042  * Since: 2.10
2043  **/
2044 GdkPixbuf*
2045 gtk_assistant_get_page_header_image (GtkAssistant *assistant,
2046                                      GtkWidget    *page)
2047 {
2048   GtkAssistantPage *page_info;
2049   GList *child;
2050
2051   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2052   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2053
2054   child = find_page (assistant, page);
2055
2056   g_return_val_if_fail (child != NULL, NULL);
2057
2058   page_info = (GtkAssistantPage*) child->data;
2059
2060   return page_info->header_image;
2061 }
2062
2063 /**
2064  * gtk_assistant_set_page_side_image:
2065  * @assistant: a #GtkAssistant
2066  * @page: a page of @assistant
2067  * @pixbuf: (allow-none): the new header image @page
2068  *
2069  * Sets a header image for @page. This image is displayed in the side
2070  * area of the assistant when @page is the current page.
2071  *
2072  * Since: 2.10
2073  **/
2074 void
2075 gtk_assistant_set_page_side_image (GtkAssistant *assistant,
2076                                    GtkWidget    *page,
2077                                    GdkPixbuf    *pixbuf)
2078 {
2079   GtkAssistantPrivate *priv;
2080   GtkAssistantPage *page_info;
2081   GList *child;
2082
2083   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2084   g_return_if_fail (GTK_IS_WIDGET (page));
2085   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2086
2087   priv = assistant->priv;
2088   child = find_page (assistant, page);
2089
2090   g_return_if_fail (child != NULL);
2091
2092   page_info = (GtkAssistantPage*) child->data;
2093
2094   if (pixbuf != page_info->sidebar_image)
2095     {
2096       if (page_info->sidebar_image)
2097         {
2098           g_object_unref (page_info->sidebar_image);
2099           page_info->sidebar_image = NULL;
2100         }
2101
2102       if (pixbuf)
2103         page_info->sidebar_image = g_object_ref (pixbuf);
2104
2105       if (page_info == priv->current_page)
2106         set_assistant_sidebar_image (assistant);
2107
2108       gtk_widget_child_notify (page, "sidebar-image");
2109     }
2110 }
2111
2112 /**
2113  * gtk_assistant_get_page_side_image:
2114  * @assistant: a #GtkAssistant
2115  * @page: a page of @assistant
2116  * 
2117  * Gets the header image for @page. 
2118  * 
2119  * Return value: the side image for @page, or %NULL
2120  * if there's no side image for the page.
2121  *
2122  * Since: 2.10
2123  **/
2124 GdkPixbuf*
2125 gtk_assistant_get_page_side_image (GtkAssistant *assistant,
2126                                    GtkWidget    *page)
2127 {
2128   GtkAssistantPage *page_info;
2129   GList *child;
2130
2131   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2132   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2133
2134   child = find_page (assistant, page);
2135
2136   g_return_val_if_fail (child != NULL, NULL);
2137
2138   page_info = (GtkAssistantPage*) child->data;
2139
2140   return page_info->sidebar_image;
2141 }
2142
2143 /**
2144  * gtk_assistant_set_page_complete:
2145  * @assistant: a #GtkAssistant
2146  * @page: a page of @assistant
2147  * @complete: the completeness status of the page
2148  * 
2149  * Sets whether @page contents are complete. This will make
2150  * @assistant update the buttons state to be able to continue the task.
2151  *
2152  * Since: 2.10
2153  **/
2154 void
2155 gtk_assistant_set_page_complete (GtkAssistant *assistant,
2156                                  GtkWidget    *page,
2157                                  gboolean      complete)
2158 {
2159   GtkAssistantPrivate *priv;
2160   GtkAssistantPage *page_info;
2161   GList *child;
2162
2163   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2164   g_return_if_fail (GTK_IS_WIDGET (page));
2165
2166   priv = assistant->priv;
2167   child = find_page (assistant, page);
2168
2169   g_return_if_fail (child != NULL);
2170
2171   page_info = (GtkAssistantPage*) child->data;
2172
2173   if (complete != page_info->complete)
2174     {
2175       page_info->complete = complete;
2176       page_info->complete_set = TRUE;
2177
2178       /* Always set buttons state, a change in a future page
2179          might change current page buttons */
2180       set_assistant_buttons_state (assistant);
2181
2182       gtk_widget_child_notify (page, "complete");
2183     }
2184 }
2185
2186 /**
2187  * gtk_assistant_get_page_complete:
2188  * @assistant: a #GtkAssistant
2189  * @page: a page of @assistant
2190  * 
2191  * Gets whether @page is complete.
2192  * 
2193  * Return value: %TRUE if @page is complete.
2194  *
2195  * Since: 2.10
2196  **/
2197 gboolean
2198 gtk_assistant_get_page_complete (GtkAssistant *assistant,
2199                                  GtkWidget    *page)
2200 {
2201   GtkAssistantPage *page_info;
2202   GList *child;
2203
2204   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), FALSE);
2205   g_return_val_if_fail (GTK_IS_WIDGET (page), FALSE);
2206
2207   child = find_page (assistant, page);
2208
2209   g_return_val_if_fail (child != NULL, FALSE);
2210
2211   page_info = (GtkAssistantPage*) child->data;
2212
2213   return page_info->complete;
2214 }
2215
2216 /**
2217  * gtk_assistant_update_buttons_state:
2218  * @assistant: a #GtkAssistant
2219  * 
2220  * Forces @assistant to recompute the buttons state.
2221  * 
2222  * GTK+ automatically takes care of this in most situations, 
2223  * e.g. when the user goes to a different page, or when the
2224  * visibility or completeness of a page changes.
2225  *
2226  * One situation where it can be necessary to call this
2227  * function is when changing a value on the current page
2228  * affects the future page flow of the assistant.
2229  *
2230  * Since: 2.10
2231  **/
2232 void
2233 gtk_assistant_update_buttons_state (GtkAssistant *assistant)
2234 {
2235   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2236
2237   set_assistant_buttons_state (assistant);
2238 }
2239
2240
2241
2242 /* accessible implementation */
2243
2244 static gint
2245 gtk_assistant_accessible_get_n_children (AtkObject *accessible)
2246 {
2247   GtkAssistant *assistant;
2248   GtkWidget *widget;
2249
2250   widget = GTK_ACCESSIBLE (accessible)->widget;
2251
2252   if (!widget)
2253     return 0;
2254
2255   assistant = GTK_ASSISTANT (widget);
2256    
2257   return g_list_length (assistant->priv->pages) + 1;
2258 }
2259
2260
2261 static AtkObject *
2262 gtk_assistant_accessible_ref_child (AtkObject *accessible,
2263                                     gint       index)
2264 {
2265   GtkAssistant *assistant;
2266   GtkAssistantPrivate *priv;
2267   GtkWidget *widget, *child;
2268   gint n_pages;
2269   AtkObject *obj;
2270   const gchar *title;
2271
2272   widget = GTK_ACCESSIBLE (accessible)->widget;
2273   if (!widget)
2274     return NULL;
2275
2276   assistant = GTK_ASSISTANT (widget);
2277   priv = assistant->priv;
2278   n_pages = g_list_length (priv->pages);
2279
2280   if (index < 0)
2281     return NULL;
2282   else if (index < n_pages)
2283     {
2284       GtkAssistantPage *page = g_list_nth_data (priv->pages, index);
2285
2286       child = page->page;
2287       title = gtk_assistant_get_page_title (assistant, child);
2288     }
2289   else if (index == n_pages)
2290     {
2291       child = priv->action_area;
2292       title = NULL;
2293     }
2294   else
2295     return NULL;
2296   
2297   obj = gtk_widget_get_accessible (child);
2298
2299   if (title)
2300     atk_object_set_name (obj, title);
2301
2302   return g_object_ref (obj);
2303 }
2304
2305 static void
2306 gtk_assistant_accessible_class_init (AtkObjectClass *class)
2307 {
2308   class->get_n_children = gtk_assistant_accessible_get_n_children;
2309   class->ref_child = gtk_assistant_accessible_ref_child;
2310 }
2311
2312 static GType
2313 gtk_assistant_accessible_get_type (void)
2314 {
2315   static GType type = 0;
2316   
2317   if (!type)
2318     {
2319       /*
2320        * Figure out the size of the class and instance
2321        * we are deriving from
2322        */
2323       AtkObjectFactory *factory;
2324       GType derived_type;
2325       GTypeQuery query;
2326       GType derived_atk_type;
2327
2328       derived_type = g_type_parent (GTK_TYPE_ASSISTANT);
2329       factory = atk_registry_get_factory (atk_get_default_registry (),
2330                                           derived_type);
2331       derived_atk_type = atk_object_factory_get_accessible_type (factory);
2332       g_type_query (derived_atk_type, &query);
2333
2334       type = g_type_register_static_simple (derived_atk_type,
2335                                             I_("GtkAssistantAccessible"),
2336                                             query.class_size,
2337                                             (GClassInitFunc) gtk_assistant_accessible_class_init,
2338                                             query.instance_size,
2339                                             NULL, 0);
2340     }
2341
2342   return type;
2343 }
2344
2345 static AtkObject *
2346 gtk_assistant_accessible_new (GObject *obj)
2347 {
2348   AtkObject *accessible;
2349
2350   g_return_val_if_fail (GTK_IS_ASSISTANT (obj), NULL);
2351
2352   accessible = g_object_new (gtk_assistant_accessible_get_type (), NULL);
2353   atk_object_initialize (accessible, obj);
2354
2355   return accessible;
2356 }
2357
2358 static GType
2359 gtk_assistant_accessible_factory_get_accessible_type (void)
2360 {
2361   return gtk_assistant_accessible_get_type ();
2362 }
2363
2364 static AtkObject*
2365 gtk_assistant_accessible_factory_create_accessible (GObject *obj)
2366 {
2367   return gtk_assistant_accessible_new (obj);
2368 }
2369
2370 static void
2371 gtk_assistant_accessible_factory_class_init (AtkObjectFactoryClass *class)
2372 {
2373   class->create_accessible = gtk_assistant_accessible_factory_create_accessible;
2374   class->get_accessible_type = gtk_assistant_accessible_factory_get_accessible_type;
2375 }
2376
2377 static GType
2378 gtk_assistant_accessible_factory_get_type (void)
2379 {
2380   static GType type = 0;
2381
2382   if (!type)
2383     {
2384       type = g_type_register_static_simple (ATK_TYPE_OBJECT_FACTORY,
2385                                             I_("GtkAssistantAccessibleFactory"),
2386                                             sizeof (AtkObjectFactoryClass),
2387                                             (GClassInitFunc) gtk_assistant_accessible_factory_class_init,
2388                                             sizeof (AtkObjectFactory),
2389                                             NULL, 0);
2390     }
2391
2392   return type;
2393 }
2394
2395 static AtkObject *
2396 gtk_assistant_get_accessible (GtkWidget *widget)
2397 {
2398   static gboolean first_time = TRUE;
2399
2400   if (first_time)
2401     {
2402       AtkObjectFactory *factory;
2403       AtkRegistry *registry;
2404       GType derived_type;
2405       GType derived_atk_type;
2406
2407       /*
2408        * Figure out whether accessibility is enabled by looking at the
2409        * type of the accessible object which would be created for
2410        * the parent type of GtkAssistant.
2411        */
2412       derived_type = g_type_parent (GTK_TYPE_ASSISTANT);
2413
2414       registry = atk_get_default_registry ();
2415       factory = atk_registry_get_factory (registry,
2416                                           derived_type);
2417       derived_atk_type = atk_object_factory_get_accessible_type (factory);
2418       if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
2419         {
2420           atk_registry_set_factory_type (registry,
2421                                          GTK_TYPE_ASSISTANT,
2422                                          gtk_assistant_accessible_factory_get_type ());
2423         }
2424       first_time = FALSE;
2425     }
2426
2427   return GTK_WIDGET_CLASS (gtk_assistant_parent_class)->get_accessible (widget);
2428 }
2429
2430
2431 static GtkBuildableIface *parent_buildable_iface;
2432
2433 static void
2434 gtk_assistant_buildable_interface_init (GtkBuildableIface *iface)
2435 {
2436   parent_buildable_iface = g_type_interface_peek_parent (iface);
2437   iface->get_internal_child = gtk_assistant_buildable_get_internal_child;
2438   iface->custom_tag_start = gtk_assistant_buildable_custom_tag_start;
2439   iface->custom_finished = gtk_assistant_buildable_custom_finished;
2440 }
2441
2442 static GObject *
2443 gtk_assistant_buildable_get_internal_child (GtkBuildable *buildable,
2444                                             GtkBuilder   *builder,
2445                                             const gchar  *childname)
2446 {
2447     if (strcmp (childname, "action_area") == 0)
2448       return G_OBJECT (GTK_ASSISTANT (buildable)->priv->action_area);
2449
2450     return parent_buildable_iface->get_internal_child (buildable,
2451                                                        builder,
2452                                                        childname);
2453 }
2454
2455 gboolean
2456 gtk_assistant_buildable_custom_tag_start (GtkBuildable  *buildable,
2457                                           GtkBuilder    *builder,
2458                                           GObject       *child,
2459                                           const gchar   *tagname,
2460                                           GMarkupParser *parser,
2461                                           gpointer      *data)
2462 {
2463   return parent_buildable_iface->custom_tag_start (buildable, builder, child,
2464                                                    tagname, parser, data);
2465 }
2466
2467 static void
2468 gtk_assistant_buildable_custom_finished (GtkBuildable *buildable,
2469                                          GtkBuilder   *builder,
2470                                          GObject      *child,
2471                                          const gchar  *tagname,
2472                                          gpointer      user_data)
2473 {
2474   parent_buildable_iface->custom_finished (buildable, builder, child,
2475                                            tagname, user_data);
2476 }
2477
2478
2479 #define __GTK_ASSISTANT_C__
2480 #include "gtkaliasdef.c"