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