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