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