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