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