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