]> Pileus Git - ~andy/gtk/blob - gtk/gtkassistant.c
wayland: Remove non-existing gdkscreen-wayland.h from SOURCES
[~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 "gtkaccessibleprivate.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   GTK_WIDGET_CLASS (gtk_assistant_parent_class)->style_updated (widget);
1127
1128   list = priv->pages;
1129
1130   while (list)
1131     {
1132       GtkAssistantPage *page = list->data;
1133
1134       set_title_font (widget, page->title);
1135       list = list->next;
1136     }
1137 }
1138
1139 static void
1140 gtk_assistant_size_request (GtkWidget      *widget,
1141                             GtkRequisition *requisition)
1142 {
1143   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1144   GtkAssistantPrivate *priv = assistant->priv;
1145   GtkRequisition child_requisition;
1146   gint header_padding, content_padding;
1147   gint width, height, header_width, header_height;
1148   guint border_width;
1149   GList *list;
1150
1151   gtk_widget_style_get (widget,
1152                         "header-padding", &header_padding,
1153                         "content-padding", &content_padding,
1154                         NULL);
1155   width = height = 0;
1156   header_width = header_height = 0;
1157   list  = priv->pages;
1158
1159   while (list)
1160     {
1161       GtkAssistantPage *page = list->data;
1162       gint w, h;
1163
1164       gtk_widget_get_preferred_size (page->page,
1165                                      &child_requisition, NULL);
1166       width  = MAX (width,  child_requisition.width);
1167       height = MAX (height, child_requisition.height);
1168
1169       gtk_widget_get_preferred_size (page->title,
1170                                      &child_requisition, NULL);
1171       w = child_requisition.width;
1172       h = child_requisition.height;
1173
1174       if (page->header_image)
1175         {
1176           w += gdk_pixbuf_get_width (page->header_image) + HEADER_SPACING;
1177           h  = MAX (h, gdk_pixbuf_get_height (page->header_image));
1178         }
1179
1180       header_width  = MAX (header_width, w);
1181       header_height = MAX (header_height, h);
1182
1183       list = list->next;
1184     }
1185
1186   gtk_widget_get_preferred_size (priv->sidebar_image,
1187                                  &child_requisition, NULL);
1188   width  += child_requisition.width;
1189   height  = MAX (height, child_requisition.height);
1190
1191   gtk_widget_set_size_request (priv->header_image, header_width, header_height);
1192   gtk_widget_get_preferred_size (priv->header_image,
1193                                  &child_requisition, NULL);
1194   width   = MAX (width, header_width) + 2 * header_padding;
1195   height += header_height + 2 * header_padding;
1196
1197   gtk_widget_get_preferred_size (priv->action_area,
1198                                  &child_requisition, NULL);
1199   width   = MAX (width, child_requisition.width);
1200   height += child_requisition.height + ACTION_AREA_SPACING;
1201
1202   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1203   width += border_width * 2 + content_padding * 2;
1204   height += border_width * 2 + content_padding * 2;
1205
1206   requisition->width = width;
1207   requisition->height = height;
1208 }
1209
1210 static void
1211 gtk_assistant_get_preferred_width (GtkWidget *widget,
1212                                    gint      *minimum,
1213                                    gint      *natural)
1214 {
1215   GtkRequisition requisition;
1216
1217   gtk_assistant_size_request (widget, &requisition);
1218
1219   *minimum = *natural = requisition.width;
1220 }
1221
1222 static void
1223 gtk_assistant_get_preferred_height (GtkWidget *widget,
1224                                     gint      *minimum,
1225                                     gint      *natural)
1226 {
1227   GtkRequisition requisition;
1228
1229   gtk_assistant_size_request (widget, &requisition);
1230
1231   *minimum = *natural = requisition.height;
1232 }
1233
1234 static void
1235 gtk_assistant_size_allocate (GtkWidget      *widget,
1236                              GtkAllocation  *allocation)
1237 {
1238   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1239   GtkAssistantPrivate *priv = assistant->priv;
1240   GtkRequisition header_requisition, action_requisition, sidebar_requisition;
1241   GtkAllocation child_allocation, header_allocation;
1242   GtkAllocation action_area_allocation, header_image_allocation;
1243   gint header_padding, content_padding;
1244   guint border_width;
1245   gboolean rtl;
1246   GList *pages;
1247
1248   rtl   = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1249   pages = priv->pages;
1250
1251   gtk_widget_style_get (widget,
1252                         "header-padding", &header_padding,
1253                         "content-padding", &content_padding,
1254                         NULL);
1255
1256   gtk_widget_set_allocation (widget, allocation);
1257   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1258
1259   /* Header */
1260   gtk_widget_get_preferred_size (priv->header_image,
1261                                  &header_requisition, NULL);
1262
1263   header_allocation.x = border_width + header_padding;
1264   header_allocation.y = border_width + header_padding;
1265   header_allocation.width  = allocation->width - 2 * border_width - 2 * header_padding;
1266   header_allocation.height = header_requisition.height;
1267
1268   gtk_widget_size_allocate (priv->header_image, &header_allocation);
1269
1270   /* Action area */
1271   gtk_widget_get_preferred_size (priv->action_area,
1272                                  &action_requisition, NULL);
1273
1274   child_allocation.x = border_width;
1275   child_allocation.y = allocation->height - border_width - action_requisition.height;
1276   child_allocation.width  = allocation->width - 2 * border_width;
1277   child_allocation.height = action_requisition.height;
1278
1279   gtk_widget_size_allocate (priv->action_area, &child_allocation);
1280
1281   gtk_widget_get_allocation (priv->header_image, &header_image_allocation);
1282   gtk_widget_get_allocation (priv->action_area, &action_area_allocation);
1283
1284   /* Sidebar */
1285   gtk_widget_get_preferred_size (priv->sidebar_image,
1286                                  &sidebar_requisition, NULL);
1287
1288   if (rtl)
1289     child_allocation.x = allocation->width - border_width - sidebar_requisition.width;
1290   else
1291     child_allocation.x = border_width;
1292
1293   child_allocation.y = border_width + header_image_allocation.height + 2 * header_padding;
1294   child_allocation.width = sidebar_requisition.width;
1295   child_allocation.height = allocation->height - 2 * border_width -
1296     header_image_allocation.height - 2 * header_padding - action_area_allocation.height;
1297
1298   gtk_widget_size_allocate (priv->sidebar_image, &child_allocation);
1299
1300   /* Pages */
1301   child_allocation.x = border_width + content_padding;
1302   child_allocation.y = border_width +
1303     header_image_allocation.height + 2 * header_padding + content_padding;
1304   child_allocation.width  = allocation->width - 2 * border_width - 2 * content_padding;
1305   child_allocation.height = allocation->height - 2 * border_width -
1306     header_image_allocation.height - 2 * header_padding - ACTION_AREA_SPACING - action_area_allocation.height - 2 * content_padding;
1307
1308   if (gtk_widget_get_visible (priv->sidebar_image))
1309     {
1310       GtkAllocation sidebar_image_allocation;
1311
1312       gtk_widget_get_allocation (priv->sidebar_image, &sidebar_image_allocation);
1313
1314       if (!rtl)
1315         child_allocation.x += sidebar_image_allocation.width;
1316
1317       child_allocation.width -= sidebar_image_allocation.width;
1318     }
1319
1320   while (pages)
1321     {
1322       GtkAssistantPage *page = pages->data;
1323
1324       gtk_widget_size_allocate (page->page, &child_allocation);
1325       gtk_widget_size_allocate (page->title, &header_allocation);
1326       pages = pages->next;
1327     }
1328 }
1329
1330 static void
1331 gtk_assistant_map (GtkWidget *widget)
1332 {
1333   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1334   GtkAssistantPrivate *priv = assistant->priv;
1335   GList *page_node;
1336   GtkAssistantPage *page;
1337
1338   gtk_widget_set_mapped (widget, TRUE);
1339
1340   gtk_widget_map (priv->header_image);
1341   gtk_widget_map (priv->action_area);
1342
1343   if (gtk_widget_get_visible (priv->sidebar_image) &&
1344       !gtk_widget_get_mapped (priv->sidebar_image))
1345     gtk_widget_map (priv->sidebar_image);
1346
1347   /* if there's no default page, pick the first one */
1348   page = NULL;
1349   if (!priv->current_page)
1350     {
1351       page_node = priv->pages;
1352
1353       while (page_node && !gtk_widget_get_visible (((GtkAssistantPage *) page_node->data)->page))
1354         page_node = page_node->next;
1355
1356       if (page_node)
1357         page = page_node->data;
1358     }
1359
1360   if (page &&
1361       gtk_widget_get_visible (page->page) &&
1362       !gtk_widget_get_mapped (page->page))
1363     set_current_page (assistant, page);
1364
1365   GTK_WIDGET_CLASS (gtk_assistant_parent_class)->map (widget);
1366 }
1367
1368 static void
1369 gtk_assistant_unmap (GtkWidget *widget)
1370 {
1371   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1372   GtkAssistantPrivate *priv = assistant->priv;
1373
1374   gtk_widget_set_mapped (widget, FALSE);
1375
1376   gtk_widget_unmap (priv->header_image);
1377   gtk_widget_unmap (priv->action_area);
1378
1379   if (gtk_widget_is_drawable (priv->sidebar_image))
1380     gtk_widget_unmap (priv->sidebar_image);
1381
1382   if (priv->current_page &&
1383       gtk_widget_is_drawable (priv->current_page->page))
1384     {
1385       gtk_widget_set_child_visible (priv->current_page->page, FALSE);
1386       gtk_widget_set_child_visible (priv->current_page->title, FALSE);
1387       gtk_widget_unmap (priv->current_page->title);
1388       gtk_widget_unmap (priv->current_page->page);
1389     }
1390
1391   g_slist_free (priv->visited_pages);
1392   priv->visited_pages = NULL;
1393   priv->current_page  = NULL;
1394
1395   GTK_WIDGET_CLASS (gtk_assistant_parent_class)->unmap (widget);
1396 }
1397
1398 static gboolean
1399 gtk_assistant_delete_event (GtkWidget   *widget,
1400                             GdkEventAny *event)
1401 {
1402   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1403   GtkAssistantPrivate *priv = assistant->priv;
1404
1405   /* Do not allow cancelling in the middle of a progress page */
1406   if (priv->current_page &&
1407       (priv->current_page->type != GTK_ASSISTANT_PAGE_PROGRESS ||
1408        priv->current_page->complete))
1409     g_signal_emit (widget, signals [CANCEL], 0, NULL);
1410
1411   return TRUE;
1412 }
1413
1414 static void
1415 assistant_paint_colored_box (GtkWidget *widget,
1416                              cairo_t   *cr)
1417 {
1418   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1419   GtkAssistantPrivate *priv = assistant->priv;
1420   GtkAllocation allocation, action_area_allocation, header_image_allocation;
1421   GtkStyleContext *context;
1422   GtkStateFlags state;
1423   GdkRGBA color;
1424   gint border_width, header_padding, content_padding;
1425   gint content_x, content_width;
1426   gboolean rtl;
1427
1428   rtl  = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1429   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1430
1431   gtk_widget_style_get (widget,
1432                         "header-padding",  &header_padding,
1433                         "content-padding", &content_padding,
1434                         NULL);
1435
1436   context = gtk_widget_get_style_context (widget);
1437   state = gtk_widget_get_state_flags (widget);
1438
1439   gtk_widget_get_allocation (widget, &allocation);
1440   gtk_widget_get_allocation (priv->action_area, &action_area_allocation);
1441   gtk_widget_get_allocation (priv->header_image, &header_image_allocation);
1442
1443   /* colored box */
1444   gtk_style_context_save (context);
1445   gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
1446
1447   gtk_style_context_get_background_color (context, state, &color);
1448   gdk_cairo_set_source_rgba (cr, &color);
1449
1450   cairo_rectangle (cr,
1451                    border_width,
1452                    border_width,
1453                    allocation.width - 2 * border_width,
1454                    allocation.height - action_area_allocation.height - 2 * border_width - ACTION_AREA_SPACING);
1455   cairo_fill (cr);
1456
1457   /* content box */
1458   content_x = content_padding + border_width;
1459   content_width = allocation.width - 2 * content_padding - 2 * border_width;
1460
1461   if (gtk_widget_get_visible (priv->sidebar_image))
1462     {
1463       GtkAllocation sidebar_image_allocation;
1464
1465       gtk_widget_get_allocation (priv->sidebar_image, &sidebar_image_allocation);
1466
1467       if (!rtl)
1468         content_x += sidebar_image_allocation.width;
1469       content_width -= sidebar_image_allocation.width;
1470     }
1471
1472   gtk_style_context_restore (context);
1473
1474   gtk_style_context_get_background_color (context, state, &color);
1475   gdk_cairo_set_source_rgba (cr, &color);
1476
1477   cairo_rectangle (cr,
1478                    content_x,
1479                    header_image_allocation.height + content_padding + 2 * header_padding + border_width,
1480                    content_width,
1481                    allocation.height - 2 * border_width - action_area_allocation.height -
1482                    header_image_allocation.height - 2 * content_padding - 2 * header_padding - ACTION_AREA_SPACING);
1483   cairo_fill (cr);
1484 }
1485
1486 static gboolean
1487 gtk_assistant_draw (GtkWidget *widget,
1488                     cairo_t   *cr)
1489 {
1490   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1491   GtkAssistantPrivate *priv = assistant->priv;
1492   GtkContainer *container = GTK_CONTAINER (widget);
1493
1494   if (GTK_WIDGET_CLASS (gtk_assistant_parent_class)->draw)
1495     GTK_WIDGET_CLASS (gtk_assistant_parent_class)->draw (widget, cr);
1496
1497   assistant_paint_colored_box (widget, cr);
1498
1499   gtk_container_propagate_draw (container, priv->header_image, cr);
1500   gtk_container_propagate_draw (container, priv->sidebar_image, cr);
1501   gtk_container_propagate_draw (container, priv->action_area, cr);
1502
1503   if (priv->current_page)
1504     {
1505       gtk_container_propagate_draw (container, priv->current_page->page, cr);
1506       gtk_container_propagate_draw (container, priv->current_page->title, cr);
1507     }
1508
1509   return FALSE;
1510 }
1511
1512 static gboolean
1513 gtk_assistant_focus (GtkWidget        *widget,
1514                      GtkDirectionType  direction)
1515 {
1516   GtkAssistantPrivate *priv;
1517   GtkContainer *container;
1518
1519   container = GTK_CONTAINER (widget);
1520   priv = GTK_ASSISTANT (widget)->priv;
1521
1522   /* we only have to care about 2 widgets, action area and the current page */
1523   if (gtk_container_get_focus_child (container) == priv->action_area)
1524     {
1525       if (!gtk_widget_child_focus (priv->action_area, direction) &&
1526           (priv->current_page == NULL ||
1527            !gtk_widget_child_focus (priv->current_page->page, direction)))
1528         {
1529           /* if we're leaving the action area and the current page has no
1530            * focusable widget, clear focus and go back to the action area
1531            */
1532           gtk_container_set_focus_child (GTK_CONTAINER (priv->action_area), NULL);
1533           gtk_widget_child_focus (priv->action_area, direction);
1534         }
1535     }
1536   else
1537     {
1538       if ((priv->current_page ==  NULL ||
1539            !gtk_widget_child_focus (priv->current_page->page, direction)) &&
1540           !gtk_widget_child_focus (priv->action_area, direction))
1541         {
1542           /* if we're leaving the current page and there isn't nothing
1543            * focusable in the action area, try to clear focus and go back
1544            * to the page
1545            */
1546           gtk_window_set_focus (GTK_WINDOW (widget), NULL);
1547           if (priv->current_page != NULL)
1548             gtk_widget_child_focus (priv->current_page->page, direction);
1549         }
1550     }
1551
1552   return TRUE;
1553 }
1554
1555 static void
1556 gtk_assistant_add (GtkContainer *container,
1557                    GtkWidget    *page)
1558 {
1559   gtk_assistant_append_page (GTK_ASSISTANT (container), page);
1560 }
1561
1562 static void
1563 gtk_assistant_remove (GtkContainer *container,
1564                       GtkWidget    *page)
1565 {
1566   GtkAssistant *assistant = (GtkAssistant*) container;
1567   GList *element;
1568
1569   element = find_page (assistant, page);
1570
1571   if (element)
1572     {
1573       remove_page (assistant, element);
1574       gtk_widget_queue_resize ((GtkWidget *) container);
1575     }
1576 }
1577
1578 static void
1579 gtk_assistant_forall (GtkContainer *container,
1580                       gboolean      include_internals,
1581                       GtkCallback   callback,
1582                       gpointer      callback_data)
1583 {
1584   GtkAssistant *assistant = (GtkAssistant*) container;
1585   GtkAssistantPrivate *priv = assistant->priv;
1586   GList *pages;
1587
1588   if (include_internals)
1589     {
1590       (*callback) (priv->header_image, callback_data);
1591       (*callback) (priv->sidebar_image, callback_data);
1592       (*callback) (priv->action_area, callback_data);
1593     }
1594
1595   pages = priv->pages;
1596
1597   while (pages)
1598     {
1599       GtkAssistantPage *page = (GtkAssistantPage *) pages->data;
1600
1601       (*callback) (page->page, callback_data);
1602
1603       if (include_internals)
1604         (*callback) (page->title, callback_data);
1605
1606       pages = pages->next;
1607     }
1608 }
1609
1610 /**
1611  * gtk_assistant_new:
1612  *
1613  * Creates a new #GtkAssistant.
1614  *
1615  * Return value: a newly created #GtkAssistant
1616  *
1617  * Since: 2.10
1618  */
1619 GtkWidget*
1620 gtk_assistant_new (void)
1621 {
1622   GtkWidget *assistant;
1623
1624   assistant = g_object_new (GTK_TYPE_ASSISTANT, NULL);
1625
1626   return assistant;
1627 }
1628
1629 /**
1630  * gtk_assistant_get_current_page:
1631  * @assistant: a #GtkAssistant
1632  *
1633  * Returns the page number of the current page
1634  *
1635  * Return value: The index (starting from 0) of the current
1636  *     page in the @assistant, if the @assistant has no pages,
1637  *     -1 will be returned
1638  *
1639  * Since: 2.10
1640  */
1641 gint
1642 gtk_assistant_get_current_page (GtkAssistant *assistant)
1643 {
1644   GtkAssistantPrivate *priv;
1645
1646   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), -1);
1647
1648   priv = assistant->priv;
1649
1650   if (!priv->pages || !priv->current_page)
1651     return -1;
1652
1653   return g_list_index (priv->pages, priv->current_page);
1654 }
1655
1656 /**
1657  * gtk_assistant_set_current_page:
1658  * @assistant: a #GtkAssistant
1659  * @page_num: index of the page to switch to, starting from 0.
1660  *     If negative, the last page will be used. If greater
1661  *     than the number of pages in the @assistant, nothing
1662  *     will be done.
1663  *
1664  * Switches the page to @page_num.
1665  *
1666  * Note that this will only be necessary in custom buttons,
1667  * as the @assistant flow can be set with
1668  * gtk_assistant_set_forward_page_func().
1669  *
1670  * Since: 2.10
1671  */
1672 void
1673 gtk_assistant_set_current_page (GtkAssistant *assistant,
1674                                 gint          page_num)
1675 {
1676   GtkAssistantPrivate *priv;
1677   GtkAssistantPage *page;
1678
1679   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1680
1681   priv = assistant->priv;
1682
1683   if (page_num >= 0)
1684     page = (GtkAssistantPage *) g_list_nth_data (priv->pages, page_num);
1685   else
1686     page = (GtkAssistantPage *) g_list_last (priv->pages)->data;
1687
1688   g_return_if_fail (page != NULL);
1689
1690   if (priv->current_page == page)
1691     return;
1692
1693   /* only add the page to the visited list if the assistant is mapped,
1694    * if not, just use it as an initial page setting, for the cases where
1695    * the initial page is != to 0
1696    */
1697   if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
1698     priv->visited_pages = g_slist_prepend (priv->visited_pages,
1699                                            priv->current_page);
1700
1701   set_current_page (assistant, page);
1702 }
1703
1704 /**
1705  * gtk_assistant_next_page:
1706  * @assistant: a #GtkAssistant
1707  *
1708  * Navigate to the next page.
1709  *
1710  * It is a programming error to call this function when
1711  * there is no next page.
1712  *
1713  * This function is for use when creating pages of the
1714  * #GTK_ASSISTANT_PAGE_CUSTOM type.
1715  *
1716  * Since: 3.0
1717  */
1718 void
1719 gtk_assistant_next_page (GtkAssistant *assistant)
1720 {
1721   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1722
1723   if (!compute_next_step (assistant))
1724     g_critical ("Page flow is broken.\n"
1725                 "You may want to end it with a page of type\n"
1726                 "GTK_ASSISTANT_PAGE_CONFIRM or GTK_ASSISTANT_PAGE_SUMMARY");
1727 }
1728
1729 /**
1730  * gtk_assistant_previous_page:
1731  * @assistant: a #GtkAssistant
1732  *
1733  * Navigate to the previous visited page.
1734  *
1735  * It is a programming error to call this function when
1736  * no previous page is available.
1737  *
1738  * This function is for use when creating pages of the
1739  * #GTK_ASSISTANT_PAGE_CUSTOM type.
1740  *
1741  * Since: 3.0
1742  */
1743 void
1744 gtk_assistant_previous_page (GtkAssistant *assistant)
1745 {
1746   GtkAssistantPrivate *priv;
1747   GtkAssistantPage *page_info;
1748   GSList *page_node;
1749
1750   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1751
1752   priv = assistant->priv;
1753
1754   /* skip the progress pages when going back */
1755   do
1756     {
1757       page_node = priv->visited_pages;
1758
1759       g_return_if_fail (page_node != NULL);
1760
1761       priv->visited_pages = priv->visited_pages->next;
1762       page_info = (GtkAssistantPage *) page_node->data;
1763       g_slist_free_1 (page_node);
1764     }
1765   while (page_info->type == GTK_ASSISTANT_PAGE_PROGRESS ||
1766          !gtk_widget_get_visible (page_info->page));
1767
1768   set_current_page (assistant, page_info);
1769 }
1770
1771 /**
1772  * gtk_assistant_get_n_pages:
1773  * @assistant: a #GtkAssistant
1774  *
1775  * Returns the number of pages in the @assistant
1776  *
1777  * Return value: the number of pages in the @assistant
1778  *
1779  * Since: 2.10
1780  */
1781 gint
1782 gtk_assistant_get_n_pages (GtkAssistant *assistant)
1783 {
1784   GtkAssistantPrivate *priv;
1785
1786   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1787
1788   priv = assistant->priv;
1789
1790   return g_list_length (priv->pages);
1791 }
1792
1793 /**
1794  * gtk_assistant_get_nth_page:
1795  * @assistant: a #GtkAssistant
1796  * @page_num: the index of a page in the @assistant,
1797  *     or -1 to get the last page
1798  *
1799  * Returns the child widget contained in page number @page_num.
1800  *
1801  * Return value: (transfer none): the child widget, or %NULL
1802  *     if @page_num is out of bounds
1803  *
1804  * Since: 2.10
1805  */
1806 GtkWidget*
1807 gtk_assistant_get_nth_page (GtkAssistant *assistant,
1808                             gint          page_num)
1809 {
1810   GtkAssistantPrivate *priv;
1811   GtkAssistantPage *page;
1812   GList *elem;
1813
1814   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1815   g_return_val_if_fail (page_num >= -1, NULL);
1816
1817   priv = assistant->priv;
1818
1819   if (page_num == -1)
1820     elem = g_list_last (priv->pages);
1821   else
1822     elem = g_list_nth (priv->pages, page_num);
1823
1824   if (!elem)
1825     return NULL;
1826
1827   page = (GtkAssistantPage *) elem->data;
1828
1829   return page->page;
1830 }
1831
1832 /**
1833  * gtk_assistant_prepend_page:
1834  * @assistant: a #GtkAssistant
1835  * @page: a #GtkWidget
1836  *
1837  * Prepends a page to the @assistant.
1838  *
1839  * Return value: the index (starting at 0) of the inserted page
1840  *
1841  * Since: 2.10
1842  */
1843 gint
1844 gtk_assistant_prepend_page (GtkAssistant *assistant,
1845                             GtkWidget    *page)
1846 {
1847   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1848   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1849
1850   return gtk_assistant_insert_page (assistant, page, 0);
1851 }
1852
1853 /**
1854  * gtk_assistant_append_page:
1855  * @assistant: a #GtkAssistant
1856  * @page: a #GtkWidget
1857  *
1858  * Appends a page to the @assistant.
1859  *
1860  * Return value: the index (starting at 0) of the inserted page
1861  *
1862  * Since: 2.10
1863  */
1864 gint
1865 gtk_assistant_append_page (GtkAssistant *assistant,
1866                            GtkWidget    *page)
1867 {
1868   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1869   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1870
1871   return gtk_assistant_insert_page (assistant, page, -1);
1872 }
1873
1874 /**
1875  * gtk_assistant_insert_page:
1876  * @assistant: a #GtkAssistant
1877  * @page: a #GtkWidget
1878  * @position: the index (starting at 0) at which to insert the page,
1879  *     or -1 to append the page to the @assistant
1880  *
1881  * Inserts a page in the @assistant at a given position.
1882  *
1883  * Return value: the index (starting from 0) of the inserted page
1884  *
1885  * Since: 2.10
1886  */
1887 gint
1888 gtk_assistant_insert_page (GtkAssistant *assistant,
1889                            GtkWidget    *page,
1890                            gint          position)
1891 {
1892   GtkAssistantPrivate *priv;
1893   GtkAssistantPage *page_info;
1894   GtkStyleContext *context;
1895   gint n_pages;
1896
1897   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1898   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1899   g_return_val_if_fail (gtk_widget_get_parent (page) == NULL, 0);
1900   g_return_val_if_fail (!gtk_widget_is_toplevel (page), 0);
1901
1902   priv = assistant->priv;
1903
1904   page_info = g_slice_new0 (GtkAssistantPage);
1905   page_info->page  = page;
1906   page_info->title = gtk_label_new (NULL);
1907
1908   g_signal_connect (G_OBJECT (page), "notify::visible",
1909                     G_CALLBACK (on_page_notify_visibility), assistant);
1910
1911   gtk_misc_set_alignment (GTK_MISC (page_info->title), 0.,0.5);
1912   set_title_font   (GTK_WIDGET (assistant), page_info->title);
1913   gtk_widget_show  (page_info->title);
1914
1915   context = gtk_widget_get_style_context (page_info->title);
1916   gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
1917
1918   n_pages = g_list_length (priv->pages);
1919
1920   if (position < 0 || position > n_pages)
1921     position = n_pages;
1922
1923   priv->pages = g_list_insert (priv->pages, page_info, position);
1924
1925   gtk_widget_set_child_visible (page_info->page, FALSE);
1926   gtk_widget_set_child_visible (page_info->title, FALSE);
1927   gtk_widget_set_parent (page_info->page,  GTK_WIDGET (assistant));
1928   gtk_widget_set_parent (page_info->title, GTK_WIDGET (assistant));
1929
1930   if (gtk_widget_get_realized (GTK_WIDGET (assistant)))
1931     {
1932       gtk_widget_realize (page_info->page);
1933       gtk_widget_realize (page_info->title);
1934     }
1935
1936   gtk_widget_queue_resize (GTK_WIDGET (assistant));
1937
1938   return position;
1939 }
1940
1941 /**
1942  * gtk_assistant_set_forward_page_func:
1943  * @assistant: a #GtkAssistant
1944  * @page_func: (allow-none): the #GtkAssistantPageFunc, or %NULL
1945  *     to use the default one
1946  * @data: user data for @page_func
1947  * @destroy: destroy notifier for @data
1948  *
1949  * Sets the page forwarding function to be @page_func.
1950  *
1951  * This function will be used to determine what will be
1952  * the next page when the user presses the forward button.
1953  * Setting @page_func to %NULL will make the assistant to
1954  * use the default forward function, which just goes to the
1955  * next visible page.
1956  *
1957  * Since: 2.10
1958  */
1959 void
1960 gtk_assistant_set_forward_page_func (GtkAssistant         *assistant,
1961                                      GtkAssistantPageFunc  page_func,
1962                                      gpointer              data,
1963                                      GDestroyNotify        destroy)
1964 {
1965   GtkAssistantPrivate *priv;
1966
1967   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1968
1969   priv = assistant->priv;
1970
1971   if (priv->forward_data_destroy &&
1972       priv->forward_function_data)
1973     (*priv->forward_data_destroy) (priv->forward_function_data);
1974
1975   if (page_func)
1976     {
1977       priv->forward_function = page_func;
1978       priv->forward_function_data = data;
1979       priv->forward_data_destroy = destroy;
1980     }
1981   else
1982     {
1983       priv->forward_function = default_forward_function;
1984       priv->forward_function_data = assistant;
1985       priv->forward_data_destroy = NULL;
1986     }
1987
1988   /* Page flow has possibly changed, so the
1989    * buttons state might need to change too
1990    */
1991   set_assistant_buttons_state (assistant);
1992 }
1993
1994 /**
1995  * gtk_assistant_add_action_widget:
1996  * @assistant: a #GtkAssistant
1997  * @child: a #GtkWidget
1998  *
1999  * Adds a widget to the action area of a #GtkAssistant.
2000  *
2001  * Since: 2.10
2002  */
2003 void
2004 gtk_assistant_add_action_widget (GtkAssistant *assistant,
2005                                  GtkWidget    *child)
2006 {
2007   GtkAssistantPrivate *priv;
2008
2009   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2010   g_return_if_fail (GTK_IS_WIDGET (child));
2011
2012   priv = assistant->priv;
2013
2014   if (GTK_IS_BUTTON (child))
2015     gtk_size_group_add_widget (priv->size_group, child);
2016
2017   gtk_box_pack_end (GTK_BOX (priv->action_area), child, FALSE, FALSE, 0);
2018 }
2019
2020 /**
2021  * gtk_assistant_remove_action_widget:
2022  * @assistant: a #GtkAssistant
2023  * @child: a #GtkWidget
2024  *
2025  * Removes a widget from the action area of a #GtkAssistant.
2026  *
2027  * Since: 2.10
2028  */
2029 void
2030 gtk_assistant_remove_action_widget (GtkAssistant *assistant,
2031                                     GtkWidget    *child)
2032 {
2033   GtkAssistantPrivate *priv;
2034
2035   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2036   g_return_if_fail (GTK_IS_WIDGET (child));
2037
2038   priv = assistant->priv;
2039
2040   if (GTK_IS_BUTTON (child))
2041     gtk_size_group_remove_widget (priv->size_group, child);
2042
2043   gtk_container_remove (GTK_CONTAINER (priv->action_area), child);
2044 }
2045
2046 /**
2047  * gtk_assistant_set_page_title:
2048  * @assistant: a #GtkAssistant
2049  * @page: a page of @assistant
2050  * @title: the new title for @page
2051  *
2052  * Sets a title for @page.
2053  *
2054  * The title is displayed in the header area of the assistant
2055  * when @page is the current page.
2056  *
2057  * Since: 2.10
2058  */
2059 void
2060 gtk_assistant_set_page_title (GtkAssistant *assistant,
2061                               GtkWidget    *page,
2062                               const gchar  *title)
2063 {
2064   GtkAssistantPage *page_info;
2065   GList *child;
2066
2067   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2068   g_return_if_fail (GTK_IS_WIDGET (page));
2069
2070   child = find_page (assistant, page);
2071
2072   g_return_if_fail (child != NULL);
2073
2074   page_info = (GtkAssistantPage*) child->data;
2075
2076   gtk_label_set_text ((GtkLabel*) page_info->title, title);
2077   gtk_widget_queue_resize (GTK_WIDGET (assistant));
2078   gtk_widget_child_notify (page, "title");
2079 }
2080
2081 /**
2082  * gtk_assistant_get_page_title:
2083  * @assistant: a #GtkAssistant
2084  * @page: a page of @assistant
2085  *
2086  * Gets the title for @page.
2087  *
2088  * Return value: the title for @page
2089  *
2090  * Since: 2.10
2091  */
2092 G_CONST_RETURN gchar*
2093 gtk_assistant_get_page_title (GtkAssistant *assistant,
2094                               GtkWidget    *page)
2095 {
2096   GtkAssistantPage *page_info;
2097   GList *child;
2098
2099   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2100   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2101
2102   child = find_page (assistant, page);
2103
2104   g_return_val_if_fail (child != NULL, NULL);
2105
2106   page_info = (GtkAssistantPage*) child->data;
2107
2108   return gtk_label_get_text ((GtkLabel*) page_info->title);
2109 }
2110
2111 /**
2112  * gtk_assistant_set_page_type:
2113  * @assistant: a #GtkAssistant
2114  * @page: a page of @assistant
2115  * @type: the new type for @page
2116  *
2117  * Sets the page type for @page.
2118  *
2119  * The page type determines the page behavior in the @assistant.
2120  *
2121  * Since: 2.10
2122  */
2123 void
2124 gtk_assistant_set_page_type (GtkAssistant         *assistant,
2125                              GtkWidget            *page,
2126                              GtkAssistantPageType  type)
2127 {
2128   GtkAssistantPage *page_info;
2129   GList *child;
2130
2131   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2132   g_return_if_fail (GTK_IS_WIDGET (page));
2133
2134   child = find_page (assistant, page);
2135
2136   g_return_if_fail (child != NULL);
2137
2138   page_info = (GtkAssistantPage*) child->data;
2139
2140   if (type != page_info->type)
2141     {
2142       page_info->type = type;
2143
2144       /* backwards compatibility to the era before fixing bug 604289 */
2145       if (type == GTK_ASSISTANT_PAGE_SUMMARY && !page_info->complete_set)
2146         {
2147           gtk_assistant_set_page_complete (assistant, page, TRUE);
2148           page_info->complete_set = FALSE;
2149         }
2150
2151       /* Always set buttons state, a change in a future page
2152        * might change current page buttons
2153        */
2154       set_assistant_buttons_state (assistant);
2155
2156       gtk_widget_child_notify (page, "page-type");
2157     }
2158 }
2159
2160 /**
2161  * gtk_assistant_get_page_type:
2162  * @assistant: a #GtkAssistant
2163  * @page: a page of @assistant
2164  *
2165  * Gets the page type of @page.
2166  *
2167  * Return value: the page type of @page
2168  *
2169  * Since: 2.10
2170  */
2171 GtkAssistantPageType
2172 gtk_assistant_get_page_type (GtkAssistant *assistant,
2173                              GtkWidget    *page)
2174 {
2175   GtkAssistantPage *page_info;
2176   GList *child;
2177
2178   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), GTK_ASSISTANT_PAGE_CONTENT);
2179   g_return_val_if_fail (GTK_IS_WIDGET (page), GTK_ASSISTANT_PAGE_CONTENT);
2180
2181   child = find_page (assistant, page);
2182
2183   g_return_val_if_fail (child != NULL, GTK_ASSISTANT_PAGE_CONTENT);
2184
2185   page_info = (GtkAssistantPage*) child->data;
2186
2187   return page_info->type;
2188 }
2189
2190 /**
2191  * gtk_assistant_set_page_header_image:
2192  * @assistant: a #GtkAssistant
2193  * @page: a page of @assistant
2194  * @pixbuf: (allow-none): the new header image @page
2195  *
2196  * Sets a header image for @page.
2197  *
2198  * This image is displayed in the header area of the assistant
2199  * when @page is the current page.
2200  *
2201  * Since: 2.10
2202  */
2203 void
2204 gtk_assistant_set_page_header_image (GtkAssistant *assistant,
2205                                      GtkWidget    *page,
2206                                      GdkPixbuf    *pixbuf)
2207 {
2208   GtkAssistantPrivate *priv;
2209   GtkAssistantPage *page_info;
2210   GList *child;
2211
2212   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2213   g_return_if_fail (GTK_IS_WIDGET (page));
2214   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2215
2216   priv = assistant->priv;
2217   child = find_page (assistant, page);
2218
2219   g_return_if_fail (child != NULL);
2220
2221   page_info = (GtkAssistantPage*) child->data;
2222
2223   if (pixbuf != page_info->header_image)
2224     {
2225       if (page_info->header_image)
2226         {
2227           g_object_unref (page_info->header_image);
2228           page_info->header_image = NULL;
2229         }
2230
2231       if (pixbuf)
2232         page_info->header_image = g_object_ref (pixbuf);
2233
2234       if (page_info == priv->current_page)
2235         set_assistant_header_image (assistant);
2236
2237       gtk_widget_child_notify (page, "header-image");
2238     }
2239 }
2240
2241 /**
2242  * gtk_assistant_get_page_header_image:
2243  * @assistant: a #GtkAssistant
2244  * @page: a page of @assistant
2245  *
2246  * Gets the header image for @page.
2247  *
2248  * Return value: (transfer none): the header image for @page,
2249  *     or %NULL if there's no header image for the page
2250  *
2251  * Since: 2.10
2252  */
2253 GdkPixbuf*
2254 gtk_assistant_get_page_header_image (GtkAssistant *assistant,
2255                                      GtkWidget    *page)
2256 {
2257   GtkAssistantPage *page_info;
2258   GList *child;
2259
2260   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2261   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2262
2263   child = find_page (assistant, page);
2264
2265   g_return_val_if_fail (child != NULL, NULL);
2266
2267   page_info = (GtkAssistantPage*) child->data;
2268
2269   return page_info->header_image;
2270 }
2271
2272 /**
2273  * gtk_assistant_set_page_side_image:
2274  * @assistant: a #GtkAssistant
2275  * @page: a page of @assistant
2276  * @pixbuf: (allow-none): the new header image @page
2277  *
2278  * Sets a header image for @page.
2279  *
2280  * This image is displayed in the side area of the assistant
2281  * when @page is the current page.
2282  *
2283  * Since: 2.10
2284  */
2285 void
2286 gtk_assistant_set_page_side_image (GtkAssistant *assistant,
2287                                    GtkWidget    *page,
2288                                    GdkPixbuf    *pixbuf)
2289 {
2290   GtkAssistantPrivate *priv;
2291   GtkAssistantPage *page_info;
2292   GList *child;
2293
2294   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2295   g_return_if_fail (GTK_IS_WIDGET (page));
2296   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2297
2298   priv = assistant->priv;
2299   child = find_page (assistant, page);
2300
2301   g_return_if_fail (child != NULL);
2302
2303   page_info = (GtkAssistantPage*) child->data;
2304
2305   if (pixbuf != page_info->sidebar_image)
2306     {
2307       if (page_info->sidebar_image)
2308         {
2309           g_object_unref (page_info->sidebar_image);
2310           page_info->sidebar_image = NULL;
2311         }
2312
2313       if (pixbuf)
2314         page_info->sidebar_image = g_object_ref (pixbuf);
2315
2316       if (page_info == priv->current_page)
2317         set_assistant_sidebar_image (assistant);
2318
2319       gtk_widget_child_notify (page, "sidebar-image");
2320     }
2321 }
2322
2323 /**
2324  * gtk_assistant_get_page_side_image:
2325  * @assistant: a #GtkAssistant
2326  * @page: a page of @assistant
2327  *
2328  * Gets the header image for @page.
2329  *
2330  * Return value: (transfer none): the side image for @page,
2331  *     or %NULL if there's no side image for the page
2332  *
2333  * Since: 2.10
2334  */
2335 GdkPixbuf*
2336 gtk_assistant_get_page_side_image (GtkAssistant *assistant,
2337                                    GtkWidget    *page)
2338 {
2339   GtkAssistantPage *page_info;
2340   GList *child;
2341
2342   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2343   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2344
2345   child = find_page (assistant, page);
2346
2347   g_return_val_if_fail (child != NULL, NULL);
2348
2349   page_info = (GtkAssistantPage*) child->data;
2350
2351   return page_info->sidebar_image;
2352 }
2353
2354 /**
2355  * gtk_assistant_set_page_complete:
2356  * @assistant: a #GtkAssistant
2357  * @page: a page of @assistant
2358  * @complete: the completeness status of the page
2359  *
2360  * Sets whether @page contents are complete.
2361  *
2362  * This will make @assistant update the buttons state
2363  * to be able to continue the task.
2364  *
2365  * Since: 2.10
2366  */
2367 void
2368 gtk_assistant_set_page_complete (GtkAssistant *assistant,
2369                                  GtkWidget    *page,
2370                                  gboolean      complete)
2371 {
2372   GtkAssistantPage *page_info;
2373   GList *child;
2374
2375   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2376   g_return_if_fail (GTK_IS_WIDGET (page));
2377
2378   child = find_page (assistant, page);
2379
2380   g_return_if_fail (child != NULL);
2381
2382   page_info = (GtkAssistantPage*) child->data;
2383
2384   if (complete != page_info->complete)
2385     {
2386       page_info->complete = complete;
2387       page_info->complete_set = TRUE;
2388
2389       /* Always set buttons state, a change in a future page
2390        * might change current page buttons
2391        */
2392       set_assistant_buttons_state (assistant);
2393
2394       gtk_widget_child_notify (page, "complete");
2395     }
2396 }
2397
2398 /**
2399  * gtk_assistant_get_page_complete:
2400  * @assistant: a #GtkAssistant
2401  * @page: a page of @assistant
2402  *
2403  * Gets whether @page is complete.
2404  *
2405  * Return value: %TRUE if @page is complete.
2406  *
2407  * Since: 2.10
2408  */
2409 gboolean
2410 gtk_assistant_get_page_complete (GtkAssistant *assistant,
2411                                  GtkWidget    *page)
2412 {
2413   GtkAssistantPage *page_info;
2414   GList *child;
2415
2416   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), FALSE);
2417   g_return_val_if_fail (GTK_IS_WIDGET (page), FALSE);
2418
2419   child = find_page (assistant, page);
2420
2421   g_return_val_if_fail (child != NULL, FALSE);
2422
2423   page_info = (GtkAssistantPage*) child->data;
2424
2425   return page_info->complete;
2426 }
2427
2428 /**
2429  * gtk_assistant_update_buttons_state:
2430  * @assistant: a #GtkAssistant
2431  *
2432  * Forces @assistant to recompute the buttons state.
2433  *
2434  * GTK+ automatically takes care of this in most situations,
2435  * e.g. when the user goes to a different page, or when the
2436  * visibility or completeness of a page changes.
2437  *
2438  * One situation where it can be necessary to call this
2439  * function is when changing a value on the current page
2440  * affects the future page flow of the assistant.
2441  *
2442  * Since: 2.10
2443  */
2444 void
2445 gtk_assistant_update_buttons_state (GtkAssistant *assistant)
2446 {
2447   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2448
2449   set_assistant_buttons_state (assistant);
2450 }
2451
2452 /**
2453  * gtk_assistant_commit:
2454  * @assistant: a #GtkAssistant
2455  *
2456  * Erases the visited page history so the back button is not
2457  * shown on the current page, and removes the cancel button
2458  * from subsequent pages.
2459  *
2460  * Use this when the information provided up to the current
2461  * page is hereafter deemed permanent and cannot be modified
2462  * or undone. For example, showing a progress page to track
2463  * a long-running, unreversible operation after the user has
2464  * clicked apply on a confirmation page.
2465  *
2466  * Since: 2.22
2467  */
2468 void
2469 gtk_assistant_commit (GtkAssistant *assistant)
2470 {
2471   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2472
2473   g_slist_free (assistant->priv->visited_pages);
2474   assistant->priv->visited_pages = NULL;
2475
2476   assistant->priv->committed = TRUE;
2477
2478   set_assistant_buttons_state (assistant);
2479 }
2480
2481 static AtkObject *
2482 gtk_assistant_get_accessible (GtkWidget *widget)
2483 {
2484   static gboolean first_time = TRUE;
2485
2486   if (first_time)
2487     {
2488       _gtk_accessible_set_factory_type (GTK_TYPE_ASSISTANT,
2489                                         gtk_assistant_accessible_factory_get_type ());
2490
2491       first_time = FALSE;
2492     }
2493
2494   return GTK_WIDGET_CLASS (gtk_assistant_parent_class)->get_accessible (widget);
2495 }
2496
2497 /* accessible implementation */
2498
2499 /* dummy typedefs */
2500 typedef struct _GtkAssistantAccessible          GtkAssistantAccessible;
2501 typedef struct _GtkAssistantAccessibleClass     GtkAssistantAccessibleClass;
2502
2503 ATK_DEFINE_TYPE (GtkAssistantAccessible, _gtk_assistant_accessible, GTK_TYPE_ASSISTANT);
2504
2505 static gint
2506 gtk_assistant_accessible_get_n_children (AtkObject *accessible)
2507 {
2508   GtkWidget *widget;
2509
2510   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
2511   if (widget == NULL)
2512     return 0;
2513
2514   return g_list_length (GTK_ASSISTANT (widget)->priv->pages) + 1;
2515 }
2516
2517 static AtkObject *
2518 gtk_assistant_accessible_ref_child (AtkObject *accessible,
2519                                     gint       index)
2520 {
2521   GtkAssistant *assistant;
2522   GtkAssistantPrivate *priv;
2523   GtkWidget *widget, *child;
2524   gint n_pages;
2525   AtkObject *obj;
2526   const gchar *title;
2527
2528   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
2529   if (widget == NULL)
2530     return NULL;
2531
2532   assistant = GTK_ASSISTANT (widget);
2533   priv = assistant->priv;
2534   n_pages = g_list_length (priv->pages);
2535
2536   if (index < 0)
2537     return NULL;
2538   else if (index < n_pages)
2539     {
2540       GtkAssistantPage *page = g_list_nth_data (priv->pages, index);
2541
2542       child = page->page;
2543       title = gtk_assistant_get_page_title (assistant, child);
2544     }
2545   else if (index == n_pages)
2546     {
2547       child = priv->action_area;
2548       title = NULL;
2549     }
2550   else
2551     return NULL;
2552
2553   obj = gtk_widget_get_accessible (child);
2554
2555   if (title)
2556     atk_object_set_name (obj, title);
2557
2558   return g_object_ref (obj);
2559 }
2560
2561 static void
2562 _gtk_assistant_accessible_class_init (GtkAssistantAccessibleClass *klass)
2563 {
2564   AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
2565
2566   atk_class->get_n_children = gtk_assistant_accessible_get_n_children;
2567   atk_class->ref_child = gtk_assistant_accessible_ref_child;
2568 }
2569
2570 static void
2571 _gtk_assistant_accessible_init (GtkAssistantAccessible *self)
2572 {
2573 }
2574
2575 /* factory */
2576 typedef AtkObjectFactory        GtkAssistantAccessibleFactory;
2577 typedef AtkObjectFactoryClass   GtkAssistantAccessibleFactoryClass;
2578
2579 G_DEFINE_TYPE (GtkAssistantAccessibleFactory,
2580                gtk_assistant_accessible_factory,
2581                ATK_TYPE_OBJECT_FACTORY);
2582
2583 static GType
2584 gtk_assistant_accessible_factory_get_accessible_type (void)
2585 {
2586   return _gtk_assistant_accessible_get_type ();
2587 }
2588
2589 static AtkObject*
2590 gtk_assistant_accessible_factory_create_accessible (GObject *obj)
2591 {
2592   AtkObject *accessible;
2593
2594   accessible = g_object_new (_gtk_assistant_accessible_get_type (), NULL);
2595   atk_object_initialize (accessible, obj);
2596
2597   return accessible;
2598 }
2599
2600 static void
2601 gtk_assistant_accessible_factory_class_init (AtkObjectFactoryClass *class)
2602 {
2603   class->create_accessible = gtk_assistant_accessible_factory_create_accessible;
2604   class->get_accessible_type = gtk_assistant_accessible_factory_get_accessible_type;
2605 }
2606
2607 static void
2608 gtk_assistant_accessible_factory_init (AtkObjectFactory *factory)
2609 {
2610 }
2611
2612 /* buildable implementation */
2613
2614 static GtkBuildableIface *parent_buildable_iface;
2615
2616 static void
2617 gtk_assistant_buildable_interface_init (GtkBuildableIface *iface)
2618 {
2619   parent_buildable_iface = g_type_interface_peek_parent (iface);
2620   iface->get_internal_child = gtk_assistant_buildable_get_internal_child;
2621   iface->custom_tag_start = gtk_assistant_buildable_custom_tag_start;
2622   iface->custom_finished = gtk_assistant_buildable_custom_finished;
2623 }
2624
2625 static GObject *
2626 gtk_assistant_buildable_get_internal_child (GtkBuildable *buildable,
2627                                             GtkBuilder   *builder,
2628                                             const gchar  *childname)
2629 {
2630     if (strcmp (childname, "action_area") == 0)
2631       return G_OBJECT (GTK_ASSISTANT (buildable)->priv->action_area);
2632
2633     return parent_buildable_iface->get_internal_child (buildable,
2634                                                        builder,
2635                                                        childname);
2636 }
2637
2638 gboolean
2639 gtk_assistant_buildable_custom_tag_start (GtkBuildable  *buildable,
2640                                           GtkBuilder    *builder,
2641                                           GObject       *child,
2642                                           const gchar   *tagname,
2643                                           GMarkupParser *parser,
2644                                           gpointer      *data)
2645 {
2646   return parent_buildable_iface->custom_tag_start (buildable, builder, child,
2647                                                    tagname, parser, data);
2648 }
2649
2650 static void
2651 gtk_assistant_buildable_custom_finished (GtkBuildable *buildable,
2652                                          GtkBuilder   *builder,
2653                                          GObject      *child,
2654                                          const gchar  *tagname,
2655                                          gpointer      user_data)
2656 {
2657   parent_buildable_iface->custom_finished (buildable, builder, child,
2658                                            tagname, user_data);
2659 }