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