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