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