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