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