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