]> Pileus Git - ~andy/gtk/blob - gtk/gtkassistant.c
Protect against giant title fonts
[~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    */
620   if (!priv->visited_pages)
621     gtk_widget_hide (priv->back);
622 }
623
624 static void
625 set_current_page (GtkAssistant     *assistant,
626                   GtkAssistantPage *page)
627 {
628   GtkAssistantPrivate *priv = assistant->priv;
629   GtkAssistantPage *old_page;
630
631   if (priv->current_page &&
632       gtk_widget_is_drawable (priv->current_page->page))
633     old_page = priv->current_page;
634   else
635     old_page = NULL;
636
637   priv->current_page = page;
638
639   set_assistant_buttons_state (assistant);
640   set_assistant_header_image (assistant);
641   set_assistant_sidebar_image (assistant);
642
643   g_signal_emit (assistant, signals [PREPARE], 0, priv->current_page->page);
644
645   if (gtk_widget_get_visible (priv->current_page->page) &&
646       gtk_widget_get_mapped (GTK_WIDGET (assistant)))
647     {
648       gtk_widget_set_child_visible (priv->current_page->page, TRUE);
649       gtk_widget_set_child_visible (priv->current_page->title, TRUE);
650       gtk_widget_map (priv->current_page->page);
651       gtk_widget_map (priv->current_page->title);
652     }
653
654   if (old_page && gtk_widget_get_mapped (old_page->page))
655     {
656       gtk_widget_set_child_visible (old_page->page, FALSE);
657       gtk_widget_set_child_visible (old_page->title, FALSE);
658       gtk_widget_unmap (old_page->page);
659       gtk_widget_unmap (old_page->title);
660     }
661
662   if (!gtk_widget_child_focus (priv->current_page->page, GTK_DIR_TAB_FORWARD))
663     {
664       GtkWidget *button[6];
665       gint i;
666
667       /* find the best button to focus */
668       button[0] = priv->apply;
669       button[1] = priv->close;
670       button[2] = priv->forward;
671       button[3] = priv->back;
672       button[4] = priv->cancel;
673       button[5] = priv->last;
674       for (i = 0; i < 6; i++)
675         {
676           if (gtk_widget_get_visible (button[i]) &&
677               gtk_widget_get_sensitive (button[i]))
678             {
679               gtk_widget_grab_focus (button[i]);
680               break;
681             }
682         }
683     }
684
685   gtk_widget_queue_resize (GTK_WIDGET (assistant));
686 }
687
688 static gint
689 compute_next_step (GtkAssistant *assistant)
690 {
691   GtkAssistantPrivate *priv = assistant->priv;
692   GtkAssistantPage *page_info;
693   gint current_page, n_pages, next_page;
694
695   current_page = gtk_assistant_get_current_page (assistant);
696   page_info = priv->current_page;
697   n_pages = gtk_assistant_get_n_pages (assistant);
698
699   next_page = (priv->forward_function) (current_page,
700                                         priv->forward_function_data);
701
702   if (next_page >= 0 && next_page < n_pages)
703     {
704       priv->visited_pages = g_slist_prepend (priv->visited_pages, page_info);
705       set_current_page (assistant, g_list_nth_data (priv->pages, next_page));
706
707       return TRUE;
708     }
709
710   return FALSE;
711 }
712
713 static void
714 on_assistant_close (GtkWidget    *widget,
715                     GtkAssistant *assistant)
716 {
717   g_signal_emit (assistant, signals [CLOSE], 0, NULL);
718 }
719
720 static void
721 on_assistant_apply (GtkWidget    *widget,
722                     GtkAssistant *assistant)
723 {
724   gboolean success;
725
726   g_signal_emit (assistant, signals [APPLY], 0);
727
728   success = compute_next_step (assistant);
729
730   /* if the assistant hasn't switched to another page, just emit
731    * the CLOSE signal, it't the last page in the assistant flow
732    */
733   if (!success)
734     g_signal_emit (assistant, signals [CLOSE], 0);
735 }
736
737 static void
738 on_assistant_forward (GtkWidget    *widget,
739                       GtkAssistant *assistant)
740 {
741   gtk_assistant_next_page (assistant);
742 }
743
744 static void
745 on_assistant_back (GtkWidget    *widget,
746                    GtkAssistant *assistant)
747 {
748   gtk_assistant_previous_page (assistant);
749 }
750
751 static void
752 on_assistant_cancel (GtkWidget    *widget,
753                      GtkAssistant *assistant)
754 {
755   g_signal_emit (assistant, signals [CANCEL], 0, NULL);
756 }
757
758 static void
759 on_assistant_last (GtkWidget    *widget,
760                    GtkAssistant *assistant)
761 {
762   GtkAssistantPrivate *priv = assistant->priv;
763
764   while (priv->current_page->type == GTK_ASSISTANT_PAGE_CONTENT &&
765          priv->current_page->complete)
766     compute_next_step (assistant);
767 }
768
769 static gboolean
770 alternative_button_order (GtkAssistant *assistant)
771 {
772   GtkSettings *settings;
773   GdkScreen *screen;
774   gboolean result;
775
776   screen   = gtk_widget_get_screen (GTK_WIDGET (assistant));
777   settings = gtk_settings_get_for_screen (screen);
778
779   g_object_get (settings,
780                 "gtk-alternative-button-order", &result,
781                 NULL);
782   return result;
783 }
784
785 static void
786 gtk_assistant_init (GtkAssistant *assistant)
787 {
788   GtkAssistantPrivate *priv;
789   GtkStyleContext *context;
790
791   assistant->priv = G_TYPE_INSTANCE_GET_PRIVATE (assistant,
792                                                  GTK_TYPE_ASSISTANT,
793                                                  GtkAssistantPrivate);
794   priv = assistant->priv;
795
796   gtk_container_set_reallocate_redraws (GTK_CONTAINER (assistant), TRUE);
797   gtk_container_set_border_width (GTK_CONTAINER (assistant), 12);
798
799   gtk_widget_push_composite_child ();
800
801   /* Header */
802   priv->header_image = gtk_image_new ();
803   gtk_misc_set_alignment (GTK_MISC (priv->header_image), 1., 0.5);
804   gtk_widget_set_parent (priv->header_image, GTK_WIDGET (assistant));
805   gtk_widget_show (priv->header_image);
806
807   context = gtk_widget_get_style_context (priv->header_image);
808   gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
809
810   /* Sidebar */
811   priv->sidebar_image = gtk_image_new ();
812   gtk_misc_set_alignment (GTK_MISC (priv->sidebar_image), 0., 0.);
813   gtk_widget_set_parent (priv->sidebar_image, GTK_WIDGET (assistant));
814   gtk_widget_show (priv->sidebar_image);
815
816   context = gtk_widget_get_style_context (priv->sidebar_image);
817   gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
818
819   /* Action area  */
820   priv->action_area  = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
821
822   priv->close   = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
823   priv->apply   = gtk_button_new_from_stock (GTK_STOCK_APPLY);
824   priv->forward = gtk_button_new_from_stock (GTK_STOCK_GO_FORWARD);
825   priv->back    = gtk_button_new_from_stock (GTK_STOCK_GO_BACK);
826   priv->cancel  = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
827   priv->last    = gtk_button_new_from_stock (GTK_STOCK_GOTO_LAST);
828   gtk_widget_set_can_default (priv->close, TRUE);
829   gtk_widget_set_can_default (priv->apply, TRUE);
830   gtk_widget_set_can_default (priv->forward, TRUE);
831
832   priv->size_group   = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
833   gtk_size_group_add_widget (priv->size_group, priv->close);
834   gtk_size_group_add_widget (priv->size_group, priv->apply);
835   gtk_size_group_add_widget (priv->size_group, priv->forward);
836   gtk_size_group_add_widget (priv->size_group, priv->back);
837   gtk_size_group_add_widget (priv->size_group, priv->cancel);
838   gtk_size_group_add_widget (priv->size_group, priv->last);
839
840   if (!alternative_button_order (assistant))
841     {
842       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->apply, FALSE, FALSE, 0);
843       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->forward, FALSE, FALSE, 0);
844       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->back, FALSE, FALSE, 0);
845       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->last, FALSE, FALSE, 0);
846       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->cancel, FALSE, FALSE, 0);
847       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->close, FALSE, FALSE, 0);
848     }
849   else
850     {
851       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->close, FALSE, FALSE, 0);
852       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->cancel, FALSE, FALSE, 0);
853       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->apply, FALSE, FALSE, 0);
854       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->forward, FALSE, FALSE, 0);
855       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->back, FALSE, FALSE, 0);
856       gtk_box_pack_end (GTK_BOX (priv->action_area), priv->last, FALSE, FALSE, 0);
857     }
858
859   gtk_widget_set_parent (priv->action_area, GTK_WIDGET (assistant));
860   gtk_widget_show (priv->forward);
861   gtk_widget_show (priv->back);
862   gtk_widget_show (priv->cancel);
863   gtk_widget_show (priv->action_area);
864
865   gtk_widget_pop_composite_child ();
866
867   priv->pages = NULL;
868   priv->current_page = NULL;
869   priv->visited_pages = NULL;
870
871   priv->forward_function = default_forward_function;
872   priv->forward_function_data = assistant;
873   priv->forward_data_destroy = NULL;
874
875   g_signal_connect (G_OBJECT (priv->close), "clicked",
876                     G_CALLBACK (on_assistant_close), assistant);
877   g_signal_connect (G_OBJECT (priv->apply), "clicked",
878                     G_CALLBACK (on_assistant_apply), assistant);
879   g_signal_connect (G_OBJECT (priv->forward), "clicked",
880                     G_CALLBACK (on_assistant_forward), assistant);
881   g_signal_connect (G_OBJECT (priv->back), "clicked",
882                     G_CALLBACK (on_assistant_back), assistant);
883   g_signal_connect (G_OBJECT (priv->cancel), "clicked",
884                     G_CALLBACK (on_assistant_cancel), assistant);
885   g_signal_connect (G_OBJECT (priv->last), "clicked",
886                     G_CALLBACK (on_assistant_last), assistant);
887 }
888
889 static void
890 gtk_assistant_set_child_property (GtkContainer    *container,
891                                   GtkWidget       *child,
892                                   guint            property_id,
893                                   const GValue    *value,
894                                   GParamSpec      *pspec)
895 {
896   switch (property_id)
897     {
898     case CHILD_PROP_PAGE_TYPE:
899       gtk_assistant_set_page_type (GTK_ASSISTANT (container), child,
900                                    g_value_get_enum (value));
901       break;
902     case CHILD_PROP_PAGE_TITLE:
903       gtk_assistant_set_page_title (GTK_ASSISTANT (container), child,
904                                     g_value_get_string (value));
905       break;
906     case CHILD_PROP_PAGE_HEADER_IMAGE:
907       gtk_assistant_set_page_header_image (GTK_ASSISTANT (container), child,
908                                            g_value_get_object (value));
909       break;
910     case CHILD_PROP_PAGE_SIDEBAR_IMAGE:
911       gtk_assistant_set_page_side_image (GTK_ASSISTANT (container), child,
912                                          g_value_get_object (value));
913       break;
914     case CHILD_PROP_PAGE_COMPLETE:
915       gtk_assistant_set_page_complete (GTK_ASSISTANT (container), child,
916                                        g_value_get_boolean (value));
917       break;
918     default:
919       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
920       break;
921     }
922 }
923
924 static void
925 gtk_assistant_get_child_property (GtkContainer *container,
926                                   GtkWidget    *child,
927                                   guint         property_id,
928                                   GValue       *value,
929                                   GParamSpec   *pspec)
930 {
931   switch (property_id)
932     {
933     case CHILD_PROP_PAGE_TYPE:
934       g_value_set_enum (value,
935                         gtk_assistant_get_page_type (GTK_ASSISTANT (container), child));
936       break;
937     case CHILD_PROP_PAGE_TITLE:
938       g_value_set_string (value,
939                           gtk_assistant_get_page_title (GTK_ASSISTANT (container), child));
940       break;
941     case CHILD_PROP_PAGE_HEADER_IMAGE:
942       g_value_set_object (value,
943                           gtk_assistant_get_page_header_image (GTK_ASSISTANT (container), child));
944       break;
945     case CHILD_PROP_PAGE_SIDEBAR_IMAGE:
946       g_value_set_object (value,
947                           gtk_assistant_get_page_side_image (GTK_ASSISTANT (container), child));
948       break;
949     case CHILD_PROP_PAGE_COMPLETE:
950       g_value_set_boolean (value,
951                            gtk_assistant_get_page_complete (GTK_ASSISTANT (container), child));
952       break;
953     default:
954       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
955       break;
956     }
957 }
958
959 static void
960 on_page_notify_visibility (GtkWidget  *widget,
961                            GParamSpec *arg,
962                            gpointer    data)
963 {
964   GtkAssistant *assistant = GTK_ASSISTANT (data);
965
966   /* update buttons state, flow may have changed */
967   if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
968     set_assistant_buttons_state (assistant);
969 }
970
971 static void
972 remove_page (GtkAssistant *assistant,
973              GList        *element)
974 {
975   GtkAssistantPrivate *priv = assistant->priv;
976   GtkAssistantPage *page_info;
977   GList *page_node;
978
979   page_info = element->data;
980
981   /* If this is the current page, we need to switch away. */
982   if (page_info == priv->current_page)
983     {
984       if (!compute_next_step (assistant))
985         {
986           /* The best we can do at this point is probably to pick
987            * the first visible page.
988            */
989           page_node = priv->pages;
990
991           while (page_node &&
992                  !gtk_widget_get_visible (((GtkAssistantPage *) page_node->data)->page))
993             page_node = page_node->next;
994
995           if (page_node == element)
996             page_node = page_node->next;
997
998           if (page_node)
999             priv->current_page = page_node->data;
1000           else
1001             priv->current_page = NULL;
1002         }
1003     }
1004
1005   priv->pages = g_list_remove_link (priv->pages, element);
1006   priv->visited_pages = g_slist_remove_all (priv->visited_pages, page_info);
1007
1008   g_signal_handlers_disconnect_by_func (page_info->page, on_page_notify_visibility, assistant);
1009   gtk_widget_unparent (page_info->page);
1010
1011   if (page_info->header_image)
1012     g_object_unref (page_info->header_image);
1013
1014   if (page_info->sidebar_image)
1015     g_object_unref (page_info->sidebar_image);
1016
1017   gtk_widget_destroy (page_info->title);
1018   g_slice_free (GtkAssistantPage, page_info);
1019   g_list_free_1 (element);
1020 }
1021
1022 static void
1023 gtk_assistant_destroy (GtkWidget *widget)
1024 {
1025   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1026   GtkAssistantPrivate *priv = assistant->priv;
1027
1028   if (priv->header_image)
1029     {
1030       gtk_widget_destroy (priv->header_image);
1031       priv->header_image = NULL;
1032     }
1033
1034   if (priv->sidebar_image)
1035     {
1036       gtk_widget_destroy (priv->sidebar_image);
1037       priv->sidebar_image = NULL;
1038     }
1039
1040   if (priv->action_area)
1041     {
1042       gtk_widget_destroy (priv->action_area);
1043       priv->action_area = NULL;
1044     }
1045
1046   if (priv->size_group)
1047     {
1048       g_object_unref (priv->size_group);
1049       priv->size_group = NULL;
1050     }
1051
1052   if (priv->forward_function)
1053     {
1054       if (priv->forward_function_data &&
1055           priv->forward_data_destroy)
1056         priv->forward_data_destroy (priv->forward_function_data);
1057
1058       priv->forward_function = NULL;
1059       priv->forward_function_data = NULL;
1060       priv->forward_data_destroy = NULL;
1061     }
1062
1063   if (priv->visited_pages)
1064     {
1065       g_slist_free (priv->visited_pages);
1066       priv->visited_pages = NULL;
1067     }
1068
1069   /* We set current to NULL so that the remove code doesn't try
1070    * to do anything funny */
1071   priv->current_page = NULL;
1072
1073   while (priv->pages)
1074     remove_page (assistant, priv->pages);
1075
1076   GTK_WIDGET_CLASS (gtk_assistant_parent_class)->destroy (widget);
1077 }
1078
1079 static GList*
1080 find_page (GtkAssistant  *assistant,
1081            GtkWidget     *page)
1082 {
1083   GtkAssistantPrivate *priv = assistant->priv;
1084   GList *child = priv->pages;
1085
1086   while (child)
1087     {
1088       GtkAssistantPage *page_info = child->data;
1089       if (page_info->page == page)
1090         return child;
1091
1092       child = child->next;
1093     }
1094
1095   return NULL;
1096 }
1097
1098 static void
1099 set_title_font (GtkWidget *assistant,
1100                 GtkWidget *title_label)
1101 {
1102   PangoFontDescription *desc;
1103   GtkStyleContext *context;
1104   gint size;
1105
1106   gtk_widget_override_font (title_label, NULL);
1107
1108   desc = pango_font_description_new ();
1109   context = gtk_widget_get_style_context (title_label);
1110   size = pango_font_description_get_size (gtk_style_context_get_font (context, 0));
1111
1112   pango_font_description_set_weight (desc, PANGO_WEIGHT_ULTRABOLD);
1113   pango_font_description_set_size   (desc, size * PANGO_SCALE_XX_LARGE);
1114
1115   gtk_widget_override_font (title_label, desc);
1116   pango_font_description_free (desc);
1117 }
1118
1119 static void
1120 gtk_assistant_style_updated (GtkWidget *widget)
1121 {
1122   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1123   GtkAssistantPrivate *priv = assistant->priv;
1124   GList *list;
1125
1126   list = priv->pages;
1127
1128   while (list)
1129     {
1130       GtkAssistantPage *page = list->data;
1131
1132       set_title_font (widget, page->title);
1133       list = list->next;
1134     }
1135 }
1136
1137 static void
1138 gtk_assistant_size_request (GtkWidget      *widget,
1139                             GtkRequisition *requisition)
1140 {
1141   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1142   GtkAssistantPrivate *priv = assistant->priv;
1143   GtkRequisition child_requisition;
1144   gint header_padding, content_padding;
1145   gint width, height, header_width, header_height;
1146   guint border_width;
1147   GList *list;
1148
1149   gtk_widget_style_get (widget,
1150                         "header-padding", &header_padding,
1151                         "content-padding", &content_padding,
1152                         NULL);
1153   width = height = 0;
1154   header_width = header_height = 0;
1155   list  = priv->pages;
1156
1157   while (list)
1158     {
1159       GtkAssistantPage *page = list->data;
1160       gint w, h;
1161
1162       gtk_widget_get_preferred_size (page->page,
1163                                      &child_requisition, NULL);
1164       width  = MAX (width,  child_requisition.width);
1165       height = MAX (height, child_requisition.height);
1166
1167       gtk_widget_get_preferred_size (page->title,
1168                                      &child_requisition, NULL);
1169       w = child_requisition.width;
1170       h = child_requisition.height;
1171
1172       if (page->header_image)
1173         {
1174           w += gdk_pixbuf_get_width (page->header_image) + HEADER_SPACING;
1175           h  = MAX (h, gdk_pixbuf_get_height (page->header_image));
1176         }
1177
1178       header_width  = MAX (header_width, w);
1179       header_height = MAX (header_height, h);
1180
1181       list = list->next;
1182     }
1183
1184   gtk_widget_get_preferred_size (priv->sidebar_image,
1185                                  &child_requisition, NULL);
1186   width  += child_requisition.width;
1187   height  = MAX (height, child_requisition.height);
1188
1189   gtk_widget_set_size_request (priv->header_image, header_width, header_height);
1190   gtk_widget_get_preferred_size (priv->header_image,
1191                                  &child_requisition, NULL);
1192   width   = MAX (width, header_width) + 2 * header_padding;
1193   height += header_height + 2 * header_padding;
1194
1195   gtk_widget_get_preferred_size (priv->action_area,
1196                                  &child_requisition, NULL);
1197   width   = MAX (width, child_requisition.width);
1198   height += child_requisition.height + ACTION_AREA_SPACING;
1199
1200   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1201   width += border_width * 2 + content_padding * 2;
1202   height += border_width * 2 + content_padding * 2;
1203
1204   requisition->width = width;
1205   requisition->height = height;
1206 }
1207
1208 static void
1209 gtk_assistant_get_preferred_width (GtkWidget *widget,
1210                                    gint      *minimum,
1211                                    gint      *natural)
1212 {
1213   GtkRequisition requisition;
1214
1215   gtk_assistant_size_request (widget, &requisition);
1216
1217   *minimum = *natural = requisition.width;
1218 }
1219
1220 static void
1221 gtk_assistant_get_preferred_height (GtkWidget *widget,
1222                                     gint      *minimum,
1223                                     gint      *natural)
1224 {
1225   GtkRequisition requisition;
1226
1227   gtk_assistant_size_request (widget, &requisition);
1228
1229   *minimum = *natural = requisition.height;
1230 }
1231
1232 static void
1233 gtk_assistant_size_allocate (GtkWidget      *widget,
1234                              GtkAllocation  *allocation)
1235 {
1236   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1237   GtkAssistantPrivate *priv = assistant->priv;
1238   GtkRequisition header_requisition, action_requisition, sidebar_requisition;
1239   GtkAllocation child_allocation, header_allocation;
1240   GtkAllocation action_area_allocation, header_image_allocation;
1241   gint header_padding, content_padding;
1242   guint border_width;
1243   gboolean rtl;
1244   GList *pages;
1245
1246   rtl   = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1247   pages = priv->pages;
1248
1249   gtk_widget_style_get (widget,
1250                         "header-padding", &header_padding,
1251                         "content-padding", &content_padding,
1252                         NULL);
1253
1254   gtk_widget_set_allocation (widget, allocation);
1255   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1256
1257   /* Header */
1258   gtk_widget_get_preferred_size (priv->header_image,
1259                                  &header_requisition, NULL);
1260
1261   header_allocation.x = border_width + header_padding;
1262   header_allocation.y = border_width + header_padding;
1263   header_allocation.width  = allocation->width - 2 * border_width - 2 * header_padding;
1264   header_allocation.height = header_requisition.height;
1265
1266   gtk_widget_size_allocate (priv->header_image, &header_allocation);
1267
1268   /* Action area */
1269   gtk_widget_get_preferred_size (priv->action_area,
1270                                  &action_requisition, NULL);
1271
1272   child_allocation.x = border_width;
1273   child_allocation.y = allocation->height - border_width - action_requisition.height;
1274   child_allocation.width  = allocation->width - 2 * border_width;
1275   child_allocation.height = action_requisition.height;
1276
1277   gtk_widget_size_allocate (priv->action_area, &child_allocation);
1278
1279   gtk_widget_get_allocation (priv->header_image, &header_image_allocation);
1280   gtk_widget_get_allocation (priv->action_area, &action_area_allocation);
1281
1282   /* Sidebar */
1283   gtk_widget_get_preferred_size (priv->sidebar_image,
1284                                  &sidebar_requisition, NULL);
1285
1286   if (rtl)
1287     child_allocation.x = allocation->width - border_width - sidebar_requisition.width;
1288   else
1289     child_allocation.x = border_width;
1290
1291   child_allocation.y = border_width + header_image_allocation.height + 2 * header_padding;
1292   child_allocation.width = sidebar_requisition.width;
1293   child_allocation.height = allocation->height - 2 * border_width -
1294     header_image_allocation.height - 2 * header_padding - action_area_allocation.height;
1295
1296   gtk_widget_size_allocate (priv->sidebar_image, &child_allocation);
1297
1298   /* Pages */
1299   child_allocation.x = border_width + content_padding;
1300   child_allocation.y = border_width +
1301     header_image_allocation.height + 2 * header_padding + content_padding;
1302   child_allocation.width  = allocation->width - 2 * border_width - 2 * content_padding;
1303   child_allocation.height = allocation->height - 2 * border_width -
1304     header_image_allocation.height - 2 * header_padding - ACTION_AREA_SPACING - action_area_allocation.height - 2 * content_padding;
1305
1306   if (gtk_widget_get_visible (priv->sidebar_image))
1307     {
1308       GtkAllocation sidebar_image_allocation;
1309
1310       gtk_widget_get_allocation (priv->sidebar_image, &sidebar_image_allocation);
1311
1312       if (!rtl)
1313         child_allocation.x += sidebar_image_allocation.width;
1314
1315       child_allocation.width -= sidebar_image_allocation.width;
1316     }
1317
1318   while (pages)
1319     {
1320       GtkAssistantPage *page = pages->data;
1321
1322       gtk_widget_size_allocate (page->page, &child_allocation);
1323       gtk_widget_size_allocate (page->title, &header_allocation);
1324       pages = pages->next;
1325     }
1326 }
1327
1328 static void
1329 gtk_assistant_map (GtkWidget *widget)
1330 {
1331   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1332   GtkAssistantPrivate *priv = assistant->priv;
1333   GList *page_node;
1334   GtkAssistantPage *page;
1335
1336   gtk_widget_set_mapped (widget, TRUE);
1337
1338   gtk_widget_map (priv->header_image);
1339   gtk_widget_map (priv->action_area);
1340
1341   if (gtk_widget_get_visible (priv->sidebar_image) &&
1342       !gtk_widget_get_mapped (priv->sidebar_image))
1343     gtk_widget_map (priv->sidebar_image);
1344
1345   /* if there's no default page, pick the first one */
1346   page = NULL;
1347   if (!priv->current_page)
1348     {
1349       page_node = priv->pages;
1350
1351       while (page_node && !gtk_widget_get_visible (((GtkAssistantPage *) page_node->data)->page))
1352         page_node = page_node->next;
1353
1354       if (page_node)
1355         page = page_node->data;
1356     }
1357
1358   if (page &&
1359       gtk_widget_get_visible (page->page) &&
1360       !gtk_widget_get_mapped (page->page))
1361     set_current_page (assistant, page);
1362
1363   GTK_WIDGET_CLASS (gtk_assistant_parent_class)->map (widget);
1364 }
1365
1366 static void
1367 gtk_assistant_unmap (GtkWidget *widget)
1368 {
1369   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1370   GtkAssistantPrivate *priv = assistant->priv;
1371
1372   gtk_widget_set_mapped (widget, FALSE);
1373
1374   gtk_widget_unmap (priv->header_image);
1375   gtk_widget_unmap (priv->action_area);
1376
1377   if (gtk_widget_is_drawable (priv->sidebar_image))
1378     gtk_widget_unmap (priv->sidebar_image);
1379
1380   if (priv->current_page &&
1381       gtk_widget_is_drawable (priv->current_page->page))
1382     {
1383       gtk_widget_set_child_visible (priv->current_page->page, FALSE);
1384       gtk_widget_set_child_visible (priv->current_page->title, FALSE);
1385       gtk_widget_unmap (priv->current_page->title);
1386       gtk_widget_unmap (priv->current_page->page);
1387     }
1388
1389   g_slist_free (priv->visited_pages);
1390   priv->visited_pages = NULL;
1391   priv->current_page  = NULL;
1392
1393   GTK_WIDGET_CLASS (gtk_assistant_parent_class)->unmap (widget);
1394 }
1395
1396 static gboolean
1397 gtk_assistant_delete_event (GtkWidget   *widget,
1398                             GdkEventAny *event)
1399 {
1400   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1401   GtkAssistantPrivate *priv = assistant->priv;
1402
1403   /* Do not allow cancelling in the middle of a progress page */
1404   if (priv->current_page &&
1405       (priv->current_page->type != GTK_ASSISTANT_PAGE_PROGRESS ||
1406        priv->current_page->complete))
1407     g_signal_emit (widget, signals [CANCEL], 0, NULL);
1408
1409   return TRUE;
1410 }
1411
1412 static void
1413 assistant_paint_colored_box (GtkWidget *widget,
1414                              cairo_t   *cr)
1415 {
1416   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1417   GtkAssistantPrivate *priv = assistant->priv;
1418   GtkAllocation allocation, action_area_allocation, header_image_allocation;
1419   GtkStyleContext *context;
1420   GtkStateFlags state;
1421   GdkRGBA color;
1422   gint border_width, header_padding, content_padding;
1423   gint content_x, content_width;
1424   gboolean rtl;
1425
1426   rtl  = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1427   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1428
1429   gtk_widget_style_get (widget,
1430                         "header-padding",  &header_padding,
1431                         "content-padding", &content_padding,
1432                         NULL);
1433
1434   context = gtk_widget_get_style_context (widget);
1435   state = gtk_widget_get_state_flags (widget);
1436
1437   gtk_widget_get_allocation (widget, &allocation);
1438   gtk_widget_get_allocation (priv->action_area, &action_area_allocation);
1439   gtk_widget_get_allocation (priv->header_image, &header_image_allocation);
1440
1441   /* colored box */
1442   gtk_style_context_save (context);
1443   gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
1444
1445   gtk_style_context_get_background_color (context, state, &color);
1446   gdk_cairo_set_source_rgba (cr, &color);
1447
1448   cairo_rectangle (cr,
1449                    border_width,
1450                    border_width,
1451                    allocation.width - 2 * border_width,
1452                    allocation.height - action_area_allocation.height - 2 * border_width - ACTION_AREA_SPACING);
1453   cairo_fill (cr);
1454
1455   /* content box */
1456   content_x = content_padding + border_width;
1457   content_width = allocation.width - 2 * content_padding - 2 * border_width;
1458
1459   if (gtk_widget_get_visible (priv->sidebar_image))
1460     {
1461       GtkAllocation sidebar_image_allocation;
1462
1463       gtk_widget_get_allocation (priv->sidebar_image, &sidebar_image_allocation);
1464
1465       if (!rtl)
1466         content_x += sidebar_image_allocation.width;
1467       content_width -= sidebar_image_allocation.width;
1468     }
1469
1470   gtk_style_context_restore (context);
1471
1472   gtk_style_context_get_background_color (context, state, &color);
1473   gdk_cairo_set_source_rgba (cr, &color);
1474
1475   cairo_rectangle (cr,
1476                    content_x,
1477                    header_image_allocation.height + content_padding + 2 * header_padding + border_width,
1478                    content_width,
1479                    allocation.height - 2 * border_width - action_area_allocation.height -
1480                    header_image_allocation.height - 2 * content_padding - 2 * header_padding - ACTION_AREA_SPACING);
1481   cairo_fill (cr);
1482 }
1483
1484 static gboolean
1485 gtk_assistant_draw (GtkWidget *widget,
1486                     cairo_t   *cr)
1487 {
1488   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1489   GtkAssistantPrivate *priv = assistant->priv;
1490   GtkContainer *container = GTK_CONTAINER (widget);
1491
1492   if (GTK_WIDGET_CLASS (gtk_assistant_parent_class)->draw)
1493     GTK_WIDGET_CLASS (gtk_assistant_parent_class)->draw (widget, cr);
1494
1495   assistant_paint_colored_box (widget, cr);
1496
1497   gtk_container_propagate_draw (container, priv->header_image, cr);
1498   gtk_container_propagate_draw (container, priv->sidebar_image, cr);
1499   gtk_container_propagate_draw (container, priv->action_area, cr);
1500
1501   if (priv->current_page)
1502     {
1503       gtk_container_propagate_draw (container, priv->current_page->page, cr);
1504       gtk_container_propagate_draw (container, priv->current_page->title, cr);
1505     }
1506
1507   return FALSE;
1508 }
1509
1510 static gboolean
1511 gtk_assistant_focus (GtkWidget        *widget,
1512                      GtkDirectionType  direction)
1513 {
1514   GtkAssistantPrivate *priv;
1515   GtkContainer *container;
1516
1517   container = GTK_CONTAINER (widget);
1518   priv = GTK_ASSISTANT (widget)->priv;
1519
1520   /* we only have to care about 2 widgets, action area and the current page */
1521   if (gtk_container_get_focus_child (container) == priv->action_area)
1522     {
1523       if (!gtk_widget_child_focus (priv->action_area, direction) &&
1524           (priv->current_page == NULL ||
1525            !gtk_widget_child_focus (priv->current_page->page, direction)))
1526         {
1527           /* if we're leaving the action area and the current page has no
1528            * focusable widget, clear focus and go back to the action area
1529            */
1530           gtk_container_set_focus_child (GTK_CONTAINER (priv->action_area), NULL);
1531           gtk_widget_child_focus (priv->action_area, direction);
1532         }
1533     }
1534   else
1535     {
1536       if ((priv->current_page ==  NULL ||
1537            !gtk_widget_child_focus (priv->current_page->page, direction)) &&
1538           !gtk_widget_child_focus (priv->action_area, direction))
1539         {
1540           /* if we're leaving the current page and there isn't nothing
1541            * focusable in the action area, try to clear focus and go back
1542            * to the page
1543            */
1544           gtk_window_set_focus (GTK_WINDOW (widget), NULL);
1545           if (priv->current_page != NULL)
1546             gtk_widget_child_focus (priv->current_page->page, direction);
1547         }
1548     }
1549
1550   return TRUE;
1551 }
1552
1553 static void
1554 gtk_assistant_add (GtkContainer *container,
1555                    GtkWidget    *page)
1556 {
1557   gtk_assistant_append_page (GTK_ASSISTANT (container), page);
1558 }
1559
1560 static void
1561 gtk_assistant_remove (GtkContainer *container,
1562                       GtkWidget    *page)
1563 {
1564   GtkAssistant *assistant = (GtkAssistant*) container;
1565   GList *element;
1566
1567   element = find_page (assistant, page);
1568
1569   if (element)
1570     {
1571       remove_page (assistant, element);
1572       gtk_widget_queue_resize ((GtkWidget *) container);
1573     }
1574 }
1575
1576 static void
1577 gtk_assistant_forall (GtkContainer *container,
1578                       gboolean      include_internals,
1579                       GtkCallback   callback,
1580                       gpointer      callback_data)
1581 {
1582   GtkAssistant *assistant = (GtkAssistant*) container;
1583   GtkAssistantPrivate *priv = assistant->priv;
1584   GList *pages;
1585
1586   if (include_internals)
1587     {
1588       (*callback) (priv->header_image, callback_data);
1589       (*callback) (priv->sidebar_image, callback_data);
1590       (*callback) (priv->action_area, callback_data);
1591     }
1592
1593   pages = priv->pages;
1594
1595   while (pages)
1596     {
1597       GtkAssistantPage *page = (GtkAssistantPage *) pages->data;
1598
1599       (*callback) (page->page, callback_data);
1600
1601       if (include_internals)
1602         (*callback) (page->title, callback_data);
1603
1604       pages = pages->next;
1605     }
1606 }
1607
1608 /**
1609  * gtk_assistant_new:
1610  *
1611  * Creates a new #GtkAssistant.
1612  *
1613  * Return value: a newly created #GtkAssistant
1614  *
1615  * Since: 2.10
1616  */
1617 GtkWidget*
1618 gtk_assistant_new (void)
1619 {
1620   GtkWidget *assistant;
1621
1622   assistant = g_object_new (GTK_TYPE_ASSISTANT, NULL);
1623
1624   return assistant;
1625 }
1626
1627 /**
1628  * gtk_assistant_get_current_page:
1629  * @assistant: a #GtkAssistant
1630  *
1631  * Returns the page number of the current page
1632  *
1633  * Return value: The index (starting from 0) of the current
1634  *     page in the @assistant, if the @assistant has no pages,
1635  *     -1 will be returned
1636  *
1637  * Since: 2.10
1638  */
1639 gint
1640 gtk_assistant_get_current_page (GtkAssistant *assistant)
1641 {
1642   GtkAssistantPrivate *priv;
1643
1644   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), -1);
1645
1646   priv = assistant->priv;
1647
1648   if (!priv->pages || !priv->current_page)
1649     return -1;
1650
1651   return g_list_index (priv->pages, priv->current_page);
1652 }
1653
1654 /**
1655  * gtk_assistant_set_current_page:
1656  * @assistant: a #GtkAssistant
1657  * @page_num: index of the page to switch to, starting from 0.
1658  *     If negative, the last page will be used. If greater
1659  *     than the number of pages in the @assistant, nothing
1660  *     will be done.
1661  *
1662  * Switches the page to @page_num.
1663  *
1664  * Note that this will only be necessary in custom buttons,
1665  * as the @assistant flow can be set with
1666  * gtk_assistant_set_forward_page_func().
1667  *
1668  * Since: 2.10
1669  */
1670 void
1671 gtk_assistant_set_current_page (GtkAssistant *assistant,
1672                                 gint          page_num)
1673 {
1674   GtkAssistantPrivate *priv;
1675   GtkAssistantPage *page;
1676
1677   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1678
1679   priv = assistant->priv;
1680
1681   if (page_num >= 0)
1682     page = (GtkAssistantPage *) g_list_nth_data (priv->pages, page_num);
1683   else
1684     page = (GtkAssistantPage *) g_list_last (priv->pages)->data;
1685
1686   g_return_if_fail (page != NULL);
1687
1688   if (priv->current_page == page)
1689     return;
1690
1691   /* only add the page to the visited list if the assistant is mapped,
1692    * if not, just use it as an initial page setting, for the cases where
1693    * the initial page is != to 0
1694    */
1695   if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
1696     priv->visited_pages = g_slist_prepend (priv->visited_pages,
1697                                            priv->current_page);
1698
1699   set_current_page (assistant, page);
1700 }
1701
1702 /**
1703  * gtk_assistant_next_page:
1704  * @assistant: a #GtkAssistant
1705  *
1706  * Navigate to the next page.
1707  *
1708  * It is a programming error to call this function when
1709  * there is no next page.
1710  *
1711  * This function is for use when creating pages of the
1712  * #GTK_ASSISTANT_PAGE_CUSTOM type.
1713  *
1714  * Since: 3.0
1715  */
1716 void
1717 gtk_assistant_next_page (GtkAssistant *assistant)
1718 {
1719   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1720
1721   if (!compute_next_step (assistant))
1722     g_critical ("Page flow is broken.\n"
1723                 "You may want to end it with a page of type\n"
1724                 "GTK_ASSISTANT_PAGE_CONFIRM or GTK_ASSISTANT_PAGE_SUMMARY");
1725 }
1726
1727 /**
1728  * gtk_assistant_previous_page:
1729  * @assistant: a #GtkAssistant
1730  *
1731  * Navigate to the previous visited page.
1732  *
1733  * It is a programming error to call this function when
1734  * no previous page is available.
1735  *
1736  * This function is for use when creating pages of the
1737  * #GTK_ASSISTANT_PAGE_CUSTOM type.
1738  *
1739  * Since: 3.0
1740  */
1741 void
1742 gtk_assistant_previous_page (GtkAssistant *assistant)
1743 {
1744   GtkAssistantPrivate *priv;
1745   GtkAssistantPage *page_info;
1746   GSList *page_node;
1747
1748   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1749
1750   priv = assistant->priv;
1751
1752   /* skip the progress pages when going back */
1753   do
1754     {
1755       page_node = priv->visited_pages;
1756
1757       g_return_if_fail (page_node != NULL);
1758
1759       priv->visited_pages = priv->visited_pages->next;
1760       page_info = (GtkAssistantPage *) page_node->data;
1761       g_slist_free_1 (page_node);
1762     }
1763   while (page_info->type == GTK_ASSISTANT_PAGE_PROGRESS ||
1764          !gtk_widget_get_visible (page_info->page));
1765
1766   set_current_page (assistant, page_info);
1767 }
1768
1769 /**
1770  * gtk_assistant_get_n_pages:
1771  * @assistant: a #GtkAssistant
1772  *
1773  * Returns the number of pages in the @assistant
1774  *
1775  * Return value: the number of pages in the @assistant
1776  *
1777  * Since: 2.10
1778  */
1779 gint
1780 gtk_assistant_get_n_pages (GtkAssistant *assistant)
1781 {
1782   GtkAssistantPrivate *priv;
1783
1784   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1785
1786   priv = assistant->priv;
1787
1788   return g_list_length (priv->pages);
1789 }
1790
1791 /**
1792  * gtk_assistant_get_nth_page:
1793  * @assistant: a #GtkAssistant
1794  * @page_num: the index of a page in the @assistant,
1795  *     or -1 to get the last page
1796  *
1797  * Returns the child widget contained in page number @page_num.
1798  *
1799  * Return value: (transfer none): the child widget, or %NULL
1800  *     if @page_num is out of bounds
1801  *
1802  * Since: 2.10
1803  */
1804 GtkWidget*
1805 gtk_assistant_get_nth_page (GtkAssistant *assistant,
1806                             gint          page_num)
1807 {
1808   GtkAssistantPrivate *priv;
1809   GtkAssistantPage *page;
1810   GList *elem;
1811
1812   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1813   g_return_val_if_fail (page_num >= -1, NULL);
1814
1815   priv = assistant->priv;
1816
1817   if (page_num == -1)
1818     elem = g_list_last (priv->pages);
1819   else
1820     elem = g_list_nth (priv->pages, page_num);
1821
1822   if (!elem)
1823     return NULL;
1824
1825   page = (GtkAssistantPage *) elem->data;
1826
1827   return page->page;
1828 }
1829
1830 /**
1831  * gtk_assistant_prepend_page:
1832  * @assistant: a #GtkAssistant
1833  * @page: a #GtkWidget
1834  *
1835  * Prepends a page to the @assistant.
1836  *
1837  * Return value: the index (starting at 0) of the inserted page
1838  *
1839  * Since: 2.10
1840  */
1841 gint
1842 gtk_assistant_prepend_page (GtkAssistant *assistant,
1843                             GtkWidget    *page)
1844 {
1845   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1846   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1847
1848   return gtk_assistant_insert_page (assistant, page, 0);
1849 }
1850
1851 /**
1852  * gtk_assistant_append_page:
1853  * @assistant: a #GtkAssistant
1854  * @page: a #GtkWidget
1855  *
1856  * Appends a page to the @assistant.
1857  *
1858  * Return value: the index (starting at 0) of the inserted page
1859  *
1860  * Since: 2.10
1861  */
1862 gint
1863 gtk_assistant_append_page (GtkAssistant *assistant,
1864                            GtkWidget    *page)
1865 {
1866   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1867   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1868
1869   return gtk_assistant_insert_page (assistant, page, -1);
1870 }
1871
1872 /**
1873  * gtk_assistant_insert_page:
1874  * @assistant: a #GtkAssistant
1875  * @page: a #GtkWidget
1876  * @position: the index (starting at 0) at which to insert the page,
1877  *     or -1 to append the page to the @assistant
1878  *
1879  * Inserts a page in the @assistant at a given position.
1880  *
1881  * Return value: the index (starting from 0) of the inserted page
1882  *
1883  * Since: 2.10
1884  */
1885 gint
1886 gtk_assistant_insert_page (GtkAssistant *assistant,
1887                            GtkWidget    *page,
1888                            gint          position)
1889 {
1890   GtkAssistantPrivate *priv;
1891   GtkAssistantPage *page_info;
1892   GtkStyleContext *context;
1893   gint n_pages;
1894
1895   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1896   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1897   g_return_val_if_fail (gtk_widget_get_parent (page) == NULL, 0);
1898   g_return_val_if_fail (!gtk_widget_is_toplevel (page), 0);
1899
1900   priv = assistant->priv;
1901
1902   page_info = g_slice_new0 (GtkAssistantPage);
1903   page_info->page  = page;
1904   page_info->title = gtk_label_new (NULL);
1905
1906   g_signal_connect (G_OBJECT (page), "notify::visible",
1907                     G_CALLBACK (on_page_notify_visibility), assistant);
1908
1909   gtk_misc_set_alignment (GTK_MISC (page_info->title), 0.,0.5);
1910   set_title_font   (GTK_WIDGET (assistant), page_info->title);
1911   gtk_widget_show  (page_info->title);
1912
1913   context = gtk_widget_get_style_context (page_info->title);
1914   gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
1915
1916   n_pages = g_list_length (priv->pages);
1917
1918   if (position < 0 || position > n_pages)
1919     position = n_pages;
1920
1921   priv->pages = g_list_insert (priv->pages, page_info, position);
1922
1923   gtk_widget_set_child_visible (page_info->page, FALSE);
1924   gtk_widget_set_child_visible (page_info->title, FALSE);
1925   gtk_widget_set_parent (page_info->page,  GTK_WIDGET (assistant));
1926   gtk_widget_set_parent (page_info->title, GTK_WIDGET (assistant));
1927
1928   if (gtk_widget_get_realized (GTK_WIDGET (assistant)))
1929     {
1930       gtk_widget_realize (page_info->page);
1931       gtk_widget_realize (page_info->title);
1932     }
1933
1934   gtk_widget_queue_resize (GTK_WIDGET (assistant));
1935
1936   return position;
1937 }
1938
1939 /**
1940  * gtk_assistant_set_forward_page_func:
1941  * @assistant: a #GtkAssistant
1942  * @page_func: (allow-none): the #GtkAssistantPageFunc, or %NULL
1943  *     to use the default one
1944  * @data: user data for @page_func
1945  * @destroy: destroy notifier for @data
1946  *
1947  * Sets the page forwarding function to be @page_func.
1948  *
1949  * This function will be used to determine what will be
1950  * the next page when the user presses the forward button.
1951  * Setting @page_func to %NULL will make the assistant to
1952  * use the default forward function, which just goes to the
1953  * next visible page.
1954  *
1955  * Since: 2.10
1956  */
1957 void
1958 gtk_assistant_set_forward_page_func (GtkAssistant         *assistant,
1959                                      GtkAssistantPageFunc  page_func,
1960                                      gpointer              data,
1961                                      GDestroyNotify        destroy)
1962 {
1963   GtkAssistantPrivate *priv;
1964
1965   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1966
1967   priv = assistant->priv;
1968
1969   if (priv->forward_data_destroy &&
1970       priv->forward_function_data)
1971     (*priv->forward_data_destroy) (priv->forward_function_data);
1972
1973   if (page_func)
1974     {
1975       priv->forward_function = page_func;
1976       priv->forward_function_data = data;
1977       priv->forward_data_destroy = destroy;
1978     }
1979   else
1980     {
1981       priv->forward_function = default_forward_function;
1982       priv->forward_function_data = assistant;
1983       priv->forward_data_destroy = NULL;
1984     }
1985
1986   /* Page flow has possibly changed, so the
1987    * buttons state might need to change too
1988    */
1989   set_assistant_buttons_state (assistant);
1990 }
1991
1992 /**
1993  * gtk_assistant_add_action_widget:
1994  * @assistant: a #GtkAssistant
1995  * @child: a #GtkWidget
1996  *
1997  * Adds a widget to the action area of a #GtkAssistant.
1998  *
1999  * Since: 2.10
2000  */
2001 void
2002 gtk_assistant_add_action_widget (GtkAssistant *assistant,
2003                                  GtkWidget    *child)
2004 {
2005   GtkAssistantPrivate *priv;
2006
2007   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2008   g_return_if_fail (GTK_IS_WIDGET (child));
2009
2010   priv = assistant->priv;
2011
2012   if (GTK_IS_BUTTON (child))
2013     gtk_size_group_add_widget (priv->size_group, child);
2014
2015   gtk_box_pack_end (GTK_BOX (priv->action_area), child, FALSE, FALSE, 0);
2016 }
2017
2018 /**
2019  * gtk_assistant_remove_action_widget:
2020  * @assistant: a #GtkAssistant
2021  * @child: a #GtkWidget
2022  *
2023  * Removes a widget from the action area of a #GtkAssistant.
2024  *
2025  * Since: 2.10
2026  */
2027 void
2028 gtk_assistant_remove_action_widget (GtkAssistant *assistant,
2029                                     GtkWidget    *child)
2030 {
2031   GtkAssistantPrivate *priv;
2032
2033   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2034   g_return_if_fail (GTK_IS_WIDGET (child));
2035
2036   priv = assistant->priv;
2037
2038   if (GTK_IS_BUTTON (child))
2039     gtk_size_group_remove_widget (priv->size_group, child);
2040
2041   gtk_container_remove (GTK_CONTAINER (priv->action_area), child);
2042 }
2043
2044 /**
2045  * gtk_assistant_set_page_title:
2046  * @assistant: a #GtkAssistant
2047  * @page: a page of @assistant
2048  * @title: the new title for @page
2049  *
2050  * Sets a title for @page.
2051  *
2052  * The title is displayed in the header area of the assistant
2053  * when @page is the current page.
2054  *
2055  * Since: 2.10
2056  */
2057 void
2058 gtk_assistant_set_page_title (GtkAssistant *assistant,
2059                               GtkWidget    *page,
2060                               const gchar  *title)
2061 {
2062   GtkAssistantPage *page_info;
2063   GList *child;
2064
2065   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2066   g_return_if_fail (GTK_IS_WIDGET (page));
2067
2068   child = find_page (assistant, page);
2069
2070   g_return_if_fail (child != NULL);
2071
2072   page_info = (GtkAssistantPage*) child->data;
2073
2074   gtk_label_set_text ((GtkLabel*) page_info->title, title);
2075   gtk_widget_queue_resize (GTK_WIDGET (assistant));
2076   gtk_widget_child_notify (page, "title");
2077 }
2078
2079 /**
2080  * gtk_assistant_get_page_title:
2081  * @assistant: a #GtkAssistant
2082  * @page: a page of @assistant
2083  *
2084  * Gets the title for @page.
2085  *
2086  * Return value: the title for @page
2087  *
2088  * Since: 2.10
2089  */
2090 G_CONST_RETURN gchar*
2091 gtk_assistant_get_page_title (GtkAssistant *assistant,
2092                               GtkWidget    *page)
2093 {
2094   GtkAssistantPage *page_info;
2095   GList *child;
2096
2097   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2098   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2099
2100   child = find_page (assistant, page);
2101
2102   g_return_val_if_fail (child != NULL, NULL);
2103
2104   page_info = (GtkAssistantPage*) child->data;
2105
2106   return gtk_label_get_text ((GtkLabel*) page_info->title);
2107 }
2108
2109 /**
2110  * gtk_assistant_set_page_type:
2111  * @assistant: a #GtkAssistant
2112  * @page: a page of @assistant
2113  * @type: the new type for @page
2114  *
2115  * Sets the page type for @page.
2116  *
2117  * The page type determines the page behavior in the @assistant.
2118  *
2119  * Since: 2.10
2120  */
2121 void
2122 gtk_assistant_set_page_type (GtkAssistant         *assistant,
2123                              GtkWidget            *page,
2124                              GtkAssistantPageType  type)
2125 {
2126   GtkAssistantPage *page_info;
2127   GList *child;
2128
2129   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2130   g_return_if_fail (GTK_IS_WIDGET (page));
2131
2132   child = find_page (assistant, page);
2133
2134   g_return_if_fail (child != NULL);
2135
2136   page_info = (GtkAssistantPage*) child->data;
2137
2138   if (type != page_info->type)
2139     {
2140       page_info->type = type;
2141
2142       /* backwards compatibility to the era before fixing bug 604289 */
2143       if (type == GTK_ASSISTANT_PAGE_SUMMARY && !page_info->complete_set)
2144         {
2145           gtk_assistant_set_page_complete (assistant, page, TRUE);
2146           page_info->complete_set = FALSE;
2147         }
2148
2149       /* Always set buttons state, a change in a future page
2150        * might change current page buttons
2151        */
2152       set_assistant_buttons_state (assistant);
2153
2154       gtk_widget_child_notify (page, "page-type");
2155     }
2156 }
2157
2158 /**
2159  * gtk_assistant_get_page_type:
2160  * @assistant: a #GtkAssistant
2161  * @page: a page of @assistant
2162  *
2163  * Gets the page type of @page.
2164  *
2165  * Return value: the page type of @page
2166  *
2167  * Since: 2.10
2168  */
2169 GtkAssistantPageType
2170 gtk_assistant_get_page_type (GtkAssistant *assistant,
2171                              GtkWidget    *page)
2172 {
2173   GtkAssistantPage *page_info;
2174   GList *child;
2175
2176   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), GTK_ASSISTANT_PAGE_CONTENT);
2177   g_return_val_if_fail (GTK_IS_WIDGET (page), GTK_ASSISTANT_PAGE_CONTENT);
2178
2179   child = find_page (assistant, page);
2180
2181   g_return_val_if_fail (child != NULL, GTK_ASSISTANT_PAGE_CONTENT);
2182
2183   page_info = (GtkAssistantPage*) child->data;
2184
2185   return page_info->type;
2186 }
2187
2188 /**
2189  * gtk_assistant_set_page_header_image:
2190  * @assistant: a #GtkAssistant
2191  * @page: a page of @assistant
2192  * @pixbuf: (allow-none): the new header image @page
2193  *
2194  * Sets a header image for @page.
2195  *
2196  * This image is displayed in the header area of the assistant
2197  * when @page is the current page.
2198  *
2199  * Since: 2.10
2200  */
2201 void
2202 gtk_assistant_set_page_header_image (GtkAssistant *assistant,
2203                                      GtkWidget    *page,
2204                                      GdkPixbuf    *pixbuf)
2205 {
2206   GtkAssistantPrivate *priv;
2207   GtkAssistantPage *page_info;
2208   GList *child;
2209
2210   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2211   g_return_if_fail (GTK_IS_WIDGET (page));
2212   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2213
2214   priv = assistant->priv;
2215   child = find_page (assistant, page);
2216
2217   g_return_if_fail (child != NULL);
2218
2219   page_info = (GtkAssistantPage*) child->data;
2220
2221   if (pixbuf != page_info->header_image)
2222     {
2223       if (page_info->header_image)
2224         {
2225           g_object_unref (page_info->header_image);
2226           page_info->header_image = NULL;
2227         }
2228
2229       if (pixbuf)
2230         page_info->header_image = g_object_ref (pixbuf);
2231
2232       if (page_info == priv->current_page)
2233         set_assistant_header_image (assistant);
2234
2235       gtk_widget_child_notify (page, "header-image");
2236     }
2237 }
2238
2239 /**
2240  * gtk_assistant_get_page_header_image:
2241  * @assistant: a #GtkAssistant
2242  * @page: a page of @assistant
2243  *
2244  * Gets the header image for @page.
2245  *
2246  * Return value: (transfer none): the header image for @page,
2247  *     or %NULL if there's no header image for the page
2248  *
2249  * Since: 2.10
2250  */
2251 GdkPixbuf*
2252 gtk_assistant_get_page_header_image (GtkAssistant *assistant,
2253                                      GtkWidget    *page)
2254 {
2255   GtkAssistantPage *page_info;
2256   GList *child;
2257
2258   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2259   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2260
2261   child = find_page (assistant, page);
2262
2263   g_return_val_if_fail (child != NULL, NULL);
2264
2265   page_info = (GtkAssistantPage*) child->data;
2266
2267   return page_info->header_image;
2268 }
2269
2270 /**
2271  * gtk_assistant_set_page_side_image:
2272  * @assistant: a #GtkAssistant
2273  * @page: a page of @assistant
2274  * @pixbuf: (allow-none): the new header image @page
2275  *
2276  * Sets a header image for @page.
2277  *
2278  * This image is displayed in the side area of the assistant
2279  * when @page is the current page.
2280  *
2281  * Since: 2.10
2282  */
2283 void
2284 gtk_assistant_set_page_side_image (GtkAssistant *assistant,
2285                                    GtkWidget    *page,
2286                                    GdkPixbuf    *pixbuf)
2287 {
2288   GtkAssistantPrivate *priv;
2289   GtkAssistantPage *page_info;
2290   GList *child;
2291
2292   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2293   g_return_if_fail (GTK_IS_WIDGET (page));
2294   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2295
2296   priv = assistant->priv;
2297   child = find_page (assistant, page);
2298
2299   g_return_if_fail (child != NULL);
2300
2301   page_info = (GtkAssistantPage*) child->data;
2302
2303   if (pixbuf != page_info->sidebar_image)
2304     {
2305       if (page_info->sidebar_image)
2306         {
2307           g_object_unref (page_info->sidebar_image);
2308           page_info->sidebar_image = NULL;
2309         }
2310
2311       if (pixbuf)
2312         page_info->sidebar_image = g_object_ref (pixbuf);
2313
2314       if (page_info == priv->current_page)
2315         set_assistant_sidebar_image (assistant);
2316
2317       gtk_widget_child_notify (page, "sidebar-image");
2318     }
2319 }
2320
2321 /**
2322  * gtk_assistant_get_page_side_image:
2323  * @assistant: a #GtkAssistant
2324  * @page: a page of @assistant
2325  *
2326  * Gets the header image for @page.
2327  *
2328  * Return value: (transfer none): the side image for @page,
2329  *     or %NULL if there's no side image for the page
2330  *
2331  * Since: 2.10
2332  */
2333 GdkPixbuf*
2334 gtk_assistant_get_page_side_image (GtkAssistant *assistant,
2335                                    GtkWidget    *page)
2336 {
2337   GtkAssistantPage *page_info;
2338   GList *child;
2339
2340   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2341   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2342
2343   child = find_page (assistant, page);
2344
2345   g_return_val_if_fail (child != NULL, NULL);
2346
2347   page_info = (GtkAssistantPage*) child->data;
2348
2349   return page_info->sidebar_image;
2350 }
2351
2352 /**
2353  * gtk_assistant_set_page_complete:
2354  * @assistant: a #GtkAssistant
2355  * @page: a page of @assistant
2356  * @complete: the completeness status of the page
2357  *
2358  * Sets whether @page contents are complete.
2359  *
2360  * This will make @assistant update the buttons state
2361  * to be able to continue the task.
2362  *
2363  * Since: 2.10
2364  */
2365 void
2366 gtk_assistant_set_page_complete (GtkAssistant *assistant,
2367                                  GtkWidget    *page,
2368                                  gboolean      complete)
2369 {
2370   GtkAssistantPage *page_info;
2371   GList *child;
2372
2373   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2374   g_return_if_fail (GTK_IS_WIDGET (page));
2375
2376   child = find_page (assistant, page);
2377
2378   g_return_if_fail (child != NULL);
2379
2380   page_info = (GtkAssistantPage*) child->data;
2381
2382   if (complete != page_info->complete)
2383     {
2384       page_info->complete = complete;
2385       page_info->complete_set = TRUE;
2386
2387       /* Always set buttons state, a change in a future page
2388        * might change current page buttons
2389        */
2390       set_assistant_buttons_state (assistant);
2391
2392       gtk_widget_child_notify (page, "complete");
2393     }
2394 }
2395
2396 /**
2397  * gtk_assistant_get_page_complete:
2398  * @assistant: a #GtkAssistant
2399  * @page: a page of @assistant
2400  *
2401  * Gets whether @page is complete.
2402  *
2403  * Return value: %TRUE if @page is complete.
2404  *
2405  * Since: 2.10
2406  */
2407 gboolean
2408 gtk_assistant_get_page_complete (GtkAssistant *assistant,
2409                                  GtkWidget    *page)
2410 {
2411   GtkAssistantPage *page_info;
2412   GList *child;
2413
2414   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), FALSE);
2415   g_return_val_if_fail (GTK_IS_WIDGET (page), FALSE);
2416
2417   child = find_page (assistant, page);
2418
2419   g_return_val_if_fail (child != NULL, FALSE);
2420
2421   page_info = (GtkAssistantPage*) child->data;
2422
2423   return page_info->complete;
2424 }
2425
2426 /**
2427  * gtk_assistant_update_buttons_state:
2428  * @assistant: a #GtkAssistant
2429  *
2430  * Forces @assistant to recompute the buttons state.
2431  *
2432  * GTK+ automatically takes care of this in most situations,
2433  * e.g. when the user goes to a different page, or when the
2434  * visibility or completeness of a page changes.
2435  *
2436  * One situation where it can be necessary to call this
2437  * function is when changing a value on the current page
2438  * affects the future page flow of the assistant.
2439  *
2440  * Since: 2.10
2441  */
2442 void
2443 gtk_assistant_update_buttons_state (GtkAssistant *assistant)
2444 {
2445   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2446
2447   set_assistant_buttons_state (assistant);
2448 }
2449
2450 /**
2451  * gtk_assistant_commit:
2452  * @assistant: a #GtkAssistant
2453  *
2454  * Erases the visited page history so the back button is not
2455  * shown on the current page, and removes the cancel button
2456  * from subsequent pages.
2457  *
2458  * Use this when the information provided up to the current
2459  * page is hereafter deemed permanent and cannot be modified
2460  * or undone. For example, showing a progress page to track
2461  * a long-running, unreversible operation after the user has
2462  * clicked apply on a confirmation page.
2463  *
2464  * Since: 2.22
2465  */
2466 void
2467 gtk_assistant_commit (GtkAssistant *assistant)
2468 {
2469   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2470
2471   g_slist_free (assistant->priv->visited_pages);
2472   assistant->priv->visited_pages = NULL;
2473
2474   assistant->priv->committed = TRUE;
2475
2476   set_assistant_buttons_state (assistant);
2477 }
2478
2479 static AtkObject *
2480 gtk_assistant_get_accessible (GtkWidget *widget)
2481 {
2482   static gboolean first_time = TRUE;
2483
2484   if (first_time)
2485     {
2486       AtkObjectFactory *factory;
2487       AtkRegistry *registry;
2488       GType derived_type;
2489       GType derived_atk_type;
2490
2491       /* Figure out whether accessibility is enabled by looking
2492        * at the type of the accessible object which would be
2493        * created for the parent type of GtkAssistant.
2494        */
2495       derived_type = g_type_parent (GTK_TYPE_ASSISTANT);
2496
2497       registry = atk_get_default_registry ();
2498       factory = atk_registry_get_factory (registry, derived_type);
2499       derived_atk_type = atk_object_factory_get_accessible_type (factory);
2500       if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
2501         atk_registry_set_factory_type (registry,
2502                                        GTK_TYPE_ASSISTANT,
2503                                        gtk_assistant_accessible_factory_get_type ());
2504
2505       first_time = FALSE;
2506     }
2507
2508   return GTK_WIDGET_CLASS (gtk_assistant_parent_class)->get_accessible (widget);
2509 }
2510
2511 /* accessible implementation */
2512
2513 /* dummy typedefs */
2514 typedef struct _GtkAssistantAccessible          GtkAssistantAccessible;
2515 typedef struct _GtkAssistantAccessibleClass     GtkAssistantAccessibleClass;
2516
2517 ATK_DEFINE_TYPE (GtkAssistantAccessible, _gtk_assistant_accessible, GTK_TYPE_ASSISTANT);
2518
2519 static gint
2520 gtk_assistant_accessible_get_n_children (AtkObject *accessible)
2521 {
2522   GtkWidget *widget;
2523
2524   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
2525   if (widget == NULL)
2526     return 0;
2527
2528   return g_list_length (GTK_ASSISTANT (accessible)->priv->pages) + 1;
2529 }
2530
2531 static AtkObject *
2532 gtk_assistant_accessible_ref_child (AtkObject *accessible,
2533                                     gint       index)
2534 {
2535   GtkAssistant *assistant;
2536   GtkAssistantPrivate *priv;
2537   GtkWidget *widget, *child;
2538   gint n_pages;
2539   AtkObject *obj;
2540   const gchar *title;
2541
2542   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
2543   if (widget == NULL)
2544     return NULL;
2545
2546   assistant = GTK_ASSISTANT (widget);
2547   priv = assistant->priv;
2548   n_pages = g_list_length (priv->pages);
2549
2550   if (index < 0)
2551     return NULL;
2552   else if (index < n_pages)
2553     {
2554       GtkAssistantPage *page = g_list_nth_data (priv->pages, index);
2555
2556       child = page->page;
2557       title = gtk_assistant_get_page_title (assistant, child);
2558     }
2559   else if (index == n_pages)
2560     {
2561       child = priv->action_area;
2562       title = NULL;
2563     }
2564   else
2565     return NULL;
2566
2567   obj = gtk_widget_get_accessible (child);
2568
2569   if (title)
2570     atk_object_set_name (obj, title);
2571
2572   return g_object_ref (obj);
2573 }
2574
2575 static void
2576 _gtk_assistant_accessible_class_init (GtkAssistantAccessibleClass *klass)
2577 {
2578   AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
2579
2580   atk_class->get_n_children = gtk_assistant_accessible_get_n_children;
2581   atk_class->ref_child = gtk_assistant_accessible_ref_child;
2582 }
2583
2584 static void
2585 _gtk_assistant_accessible_init (GtkAssistantAccessible *self)
2586 {
2587 }
2588
2589 /* factory */
2590 typedef AtkObjectFactory        GtkAssistantAccessibleFactory;
2591 typedef AtkObjectFactoryClass   GtkAssistantAccessibleFactoryClass;
2592
2593 G_DEFINE_TYPE (GtkAssistantAccessibleFactory,
2594                gtk_assistant_accessible_factory,
2595                ATK_TYPE_OBJECT_FACTORY);
2596
2597 static GType
2598 gtk_assistant_accessible_factory_get_accessible_type (void)
2599 {
2600   return _gtk_assistant_accessible_get_type ();
2601 }
2602
2603 static AtkObject*
2604 gtk_assistant_accessible_factory_create_accessible (GObject *obj)
2605 {
2606   AtkObject *accessible;
2607
2608   accessible = g_object_new (_gtk_assistant_accessible_get_type (), NULL);
2609   atk_object_initialize (accessible, obj);
2610
2611   return accessible;
2612 }
2613
2614 static void
2615 gtk_assistant_accessible_factory_class_init (AtkObjectFactoryClass *class)
2616 {
2617   class->create_accessible = gtk_assistant_accessible_factory_create_accessible;
2618   class->get_accessible_type = gtk_assistant_accessible_factory_get_accessible_type;
2619 }
2620
2621 static void
2622 gtk_assistant_accessible_factory_init (AtkObjectFactory *factory)
2623 {
2624 }
2625
2626 /* buildable implementation */
2627
2628 static GtkBuildableIface *parent_buildable_iface;
2629
2630 static void
2631 gtk_assistant_buildable_interface_init (GtkBuildableIface *iface)
2632 {
2633   parent_buildable_iface = g_type_interface_peek_parent (iface);
2634   iface->get_internal_child = gtk_assistant_buildable_get_internal_child;
2635   iface->custom_tag_start = gtk_assistant_buildable_custom_tag_start;
2636   iface->custom_finished = gtk_assistant_buildable_custom_finished;
2637 }
2638
2639 static GObject *
2640 gtk_assistant_buildable_get_internal_child (GtkBuildable *buildable,
2641                                             GtkBuilder   *builder,
2642                                             const gchar  *childname)
2643 {
2644     if (strcmp (childname, "action_area") == 0)
2645       return G_OBJECT (GTK_ASSISTANT (buildable)->priv->action_area);
2646
2647     return parent_buildable_iface->get_internal_child (buildable,
2648                                                        builder,
2649                                                        childname);
2650 }
2651
2652 gboolean
2653 gtk_assistant_buildable_custom_tag_start (GtkBuildable  *buildable,
2654                                           GtkBuilder    *builder,
2655                                           GObject       *child,
2656                                           const gchar   *tagname,
2657                                           GMarkupParser *parser,
2658                                           gpointer      *data)
2659 {
2660   return parent_buildable_iface->custom_tag_start (buildable, builder, child,
2661                                                    tagname, parser, data);
2662 }
2663
2664 static void
2665 gtk_assistant_buildable_custom_finished (GtkBuildable *buildable,
2666                                          GtkBuilder   *builder,
2667                                          GObject      *child,
2668                                          const gchar  *tagname,
2669                                          gpointer      user_data)
2670 {
2671   parent_buildable_iface->custom_finished (buildable, builder, child,
2672                                            tagname, user_data);
2673 }