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