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