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