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