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