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