]> Pileus Git - ~andy/gtk/blob - gtk/gtkassistant.c
9451107a0da12232d4ea674e14c5c039def7628b
[~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   GtkAssistantPrivate *priv;
1784   GtkAssistantPage *page_info;
1785   GList *child;
1786
1787   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1788   g_return_if_fail (GTK_IS_WIDGET (page));
1789
1790   priv = assistant->priv;
1791   child = find_page (assistant, page);
1792
1793   g_return_if_fail (child != NULL);
1794
1795   page_info = (GtkAssistantPage*) child->data;
1796
1797   gtk_label_set_text ((GtkLabel*) page_info->title, title);
1798   gtk_widget_queue_resize (GTK_WIDGET (assistant));
1799   gtk_widget_child_notify (page, "title");
1800 }
1801
1802 /**
1803  * gtk_assistant_get_page_title:
1804  * @assistant: a #GtkAssistant
1805  * @page: a page of @assistant
1806  * 
1807  * Gets the title for @page. 
1808  * 
1809  * Return value: the title for @page.
1810  *
1811  * Since: 2.10
1812  **/
1813 G_CONST_RETURN gchar*
1814 gtk_assistant_get_page_title (GtkAssistant *assistant,
1815                               GtkWidget    *page)
1816 {
1817   GtkAssistantPrivate *priv;
1818   GtkAssistantPage *page_info;
1819   GList *child;
1820
1821   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1822   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
1823
1824   priv = assistant->priv;
1825   child = find_page (assistant, page);
1826
1827   g_return_val_if_fail (child != NULL, NULL);
1828
1829   page_info = (GtkAssistantPage*) child->data;
1830
1831   return gtk_label_get_text ((GtkLabel*) page_info->title);
1832 }
1833
1834 /**
1835  * gtk_assistant_set_page_type:
1836  * @assistant: a #GtkAssistant
1837  * @page: a page of @assistant
1838  * @type: the new type for @page
1839  * 
1840  * Sets the page type for @page. The page type determines the page
1841  * behavior in the @assistant.
1842  *
1843  * Since: 2.10
1844  **/
1845 void
1846 gtk_assistant_set_page_type (GtkAssistant         *assistant,
1847                              GtkWidget            *page,
1848                              GtkAssistantPageType  type)
1849 {
1850   GtkAssistantPrivate *priv;
1851   GtkAssistantPage *page_info;
1852   GList *child;
1853
1854   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1855   g_return_if_fail (GTK_IS_WIDGET (page));
1856
1857   priv = assistant->priv;
1858   child = find_page (assistant, page);
1859
1860   g_return_if_fail (child != NULL);
1861
1862   page_info = (GtkAssistantPage*) child->data;
1863
1864   if (type != page_info->type)
1865     {
1866       page_info->type = type;
1867
1868       /* Always set buttons state, a change in a future page
1869          might change current page buttons */
1870       if (priv->current_page)
1871         set_assistant_buttons_state (assistant);
1872
1873       gtk_widget_child_notify (page, "page-type");
1874     }
1875 }
1876
1877 /**
1878  * gtk_assistant_get_page_type:
1879  * @assistant: a #GtkAssistant
1880  * @page: a page of @assistant
1881  *
1882  * Gets the page type of @page.
1883  *
1884  * Return value: the page type of @page.
1885  *
1886  * Since: 2.10
1887  **/
1888 GtkAssistantPageType
1889 gtk_assistant_get_page_type (GtkAssistant *assistant,
1890                              GtkWidget    *page)
1891 {
1892   GtkAssistantPrivate *priv;
1893   GtkAssistantPage *page_info;
1894   GList *child;
1895
1896   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), GTK_ASSISTANT_PAGE_CONTENT);
1897   g_return_val_if_fail (GTK_IS_WIDGET (page), GTK_ASSISTANT_PAGE_CONTENT);
1898
1899   priv = assistant->priv;
1900   child = find_page (assistant, page);
1901
1902   g_return_val_if_fail (child != NULL, GTK_ASSISTANT_PAGE_CONTENT);
1903
1904   page_info = (GtkAssistantPage*) child->data;
1905
1906   return page_info->type;
1907 }
1908
1909 /**
1910  * gtk_assistant_set_page_header_image:
1911  * @assistant: a #GtkAssistant
1912  * @page: a page of @assistant
1913  * @pixbuf: the new header image @page
1914  * 
1915  * Sets a header image for @page. This image is displayed in the header
1916  * area of the assistant when @page is the current page.
1917  *
1918  * Since: 2.10
1919  **/
1920 void
1921 gtk_assistant_set_page_header_image (GtkAssistant *assistant,
1922                                      GtkWidget    *page,
1923                                      GdkPixbuf    *pixbuf)
1924 {
1925   GtkAssistantPrivate *priv;
1926   GtkAssistantPage *page_info;
1927   GList *child;
1928
1929   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1930   g_return_if_fail (GTK_IS_WIDGET (page));
1931   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
1932
1933   priv = assistant->priv;
1934   child = find_page (assistant, page);
1935
1936   g_return_if_fail (child != NULL);
1937
1938   page_info = (GtkAssistantPage*) child->data;
1939
1940   if (pixbuf != page_info->header_image)
1941     {
1942       if (page_info->header_image)
1943         {
1944           g_object_unref (page_info->header_image);
1945           page_info->header_image = NULL;
1946         }
1947
1948       if (pixbuf)
1949         page_info->header_image = g_object_ref (pixbuf);
1950
1951       if (page_info == priv->current_page)
1952         set_assistant_header_image (assistant);
1953
1954       gtk_widget_child_notify (page, "header-image");
1955     }
1956 }
1957
1958 /**
1959  * gtk_assistant_get_page_header_image:
1960  * @assistant: a #GtkAssistant
1961  * @page: a page of @assistant
1962  * 
1963  * Gets the header image for @page. 
1964  * 
1965  * Return value: the header image for @page, or %NULL
1966  * if there's no header image for the page.
1967  *
1968  * Since: 2.10
1969  **/
1970 GdkPixbuf*
1971 gtk_assistant_get_page_header_image (GtkAssistant *assistant,
1972                                      GtkWidget    *page)
1973 {
1974   GtkAssistantPrivate *priv;
1975   GtkAssistantPage *page_info;
1976   GList *child;
1977
1978   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1979   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
1980
1981   priv = assistant->priv;
1982   child = find_page (assistant, page);
1983
1984   g_return_val_if_fail (child != NULL, NULL);
1985
1986   page_info = (GtkAssistantPage*) child->data;
1987
1988   return page_info->header_image;
1989 }
1990
1991 /**
1992  * gtk_assistant_set_page_side_image:
1993  * @assistant: a #GtkAssistant
1994  * @page: a page of @assistant
1995  * @pixbuf: the new header image @page
1996  * 
1997  * Sets a header image for @page. This image is displayed in the side
1998  * area of the assistant when @page is the current page.
1999  *
2000  * Since: 2.10
2001  **/
2002 void
2003 gtk_assistant_set_page_side_image (GtkAssistant *assistant,
2004                                    GtkWidget    *page,
2005                                    GdkPixbuf    *pixbuf)
2006 {
2007   GtkAssistantPrivate *priv;
2008   GtkAssistantPage *page_info;
2009   GList *child;
2010
2011   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2012   g_return_if_fail (GTK_IS_WIDGET (page));
2013   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2014
2015   priv = assistant->priv;
2016   child = find_page (assistant, page);
2017
2018   g_return_if_fail (child != NULL);
2019
2020   page_info = (GtkAssistantPage*) child->data;
2021
2022   if (pixbuf != page_info->sidebar_image)
2023     {
2024       if (page_info->sidebar_image)
2025         {
2026           g_object_unref (page_info->sidebar_image);
2027           page_info->sidebar_image = NULL;
2028         }
2029
2030       if (pixbuf)
2031         page_info->sidebar_image = g_object_ref (pixbuf);
2032
2033       if (page_info == priv->current_page)
2034         set_assistant_sidebar_image (assistant);
2035
2036       gtk_widget_child_notify (page, "sidebar-image");
2037     }
2038 }
2039
2040 /**
2041  * gtk_assistant_get_page_side_image:
2042  * @assistant: a #GtkAssistant
2043  * @page: a page of @assistant
2044  * 
2045  * Gets the header image for @page. 
2046  * 
2047  * Return value: the side image for @page, or %NULL
2048  * if there's no side image for the page.
2049  *
2050  * Since: 2.10
2051  **/
2052 GdkPixbuf*
2053 gtk_assistant_get_page_side_image (GtkAssistant *assistant,
2054                                    GtkWidget    *page)
2055 {
2056   GtkAssistantPrivate *priv;
2057   GtkAssistantPage *page_info;
2058   GList *child;
2059
2060   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2061   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2062
2063   priv = assistant->priv;
2064   child = find_page (assistant, page);
2065
2066   g_return_val_if_fail (child != NULL, NULL);
2067
2068   page_info = (GtkAssistantPage*) child->data;
2069
2070   return page_info->sidebar_image;
2071 }
2072
2073 /**
2074  * gtk_assistant_set_page_complete:
2075  * @assistant: a #GtkAssistant
2076  * @page: a page of @assistant
2077  * @complete: the completeness status of the page
2078  * 
2079  * Sets whether @page contents are complete. This will make
2080  * @assistant update the buttons state to be able to continue the task.
2081  *
2082  * Since: 2.10
2083  **/
2084 void
2085 gtk_assistant_set_page_complete (GtkAssistant *assistant,
2086                                  GtkWidget    *page,
2087                                  gboolean      complete)
2088 {
2089   GtkAssistantPrivate *priv;
2090   GtkAssistantPage *page_info;
2091   GList *child;
2092
2093   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2094   g_return_if_fail (GTK_IS_WIDGET (page));
2095
2096   priv = assistant->priv;
2097   child = find_page (assistant, page);
2098
2099   g_return_if_fail (child != NULL);
2100
2101   page_info = (GtkAssistantPage*) child->data;
2102
2103   if (complete != page_info->complete)
2104     {
2105       page_info->complete = complete;
2106
2107       /* Always set buttons state, a change in a future page
2108          might change current page buttons */
2109       if (priv->current_page)
2110         set_assistant_buttons_state (assistant);
2111
2112       gtk_widget_child_notify (page, "complete");
2113     }
2114 }
2115
2116 /**
2117  * gtk_assistant_get_page_complete:
2118  * @assistant: a #GtkAssistant
2119  * @page: a page of @assistant
2120  * 
2121  * Gets whether @page is complete..
2122  * 
2123  * Return value: %TRUE if @page is complete.
2124  *
2125  * Since: 2.10
2126  **/
2127 gboolean
2128 gtk_assistant_get_page_complete (GtkAssistant *assistant,
2129                                  GtkWidget    *page)
2130 {
2131   GtkAssistantPrivate *priv;
2132   GtkAssistantPage *page_info;
2133   GList *child;
2134
2135   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), FALSE);
2136   g_return_val_if_fail (GTK_IS_WIDGET (page), FALSE);
2137
2138   priv = assistant->priv;
2139   child = find_page (assistant, page);
2140
2141   g_return_val_if_fail (child != NULL, FALSE);
2142
2143   page_info = (GtkAssistantPage*) child->data;
2144
2145   return page_info->complete;
2146 }
2147
2148 /**
2149  * gtk_assistant_update_buttons_state:
2150  * @assistant: a #GtkAssistant
2151  * 
2152  * Forces @assistant to recompute the buttons state.
2153  * 
2154  * GTK+ automatically takes care of this in most situations, 
2155  * e.g. when the user goes to a different page, or when the
2156  * visibility or completeness of a page changes.
2157  *
2158  * One situation where it can be necessary to call this
2159  * function is when changing a value on the current page
2160  * affects the future page flow of the assistant.
2161  *
2162  * Since: 2.10
2163  **/
2164 void
2165 gtk_assistant_update_buttons_state (GtkAssistant *assistant)
2166 {
2167   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2168
2169   set_assistant_buttons_state (assistant);
2170 }
2171
2172
2173
2174 /* accessible implementation */
2175
2176 static gint
2177 gtk_assistant_accessible_get_n_children (AtkObject *accessible)
2178 {
2179   GtkAssistant *assistant;
2180   GtkWidget *widget;
2181
2182   widget = GTK_ACCESSIBLE (accessible)->widget;
2183
2184   if (!widget)
2185     return 0;
2186
2187   assistant = GTK_ASSISTANT (widget);
2188    
2189   return g_list_length (assistant->priv->pages) + 1;
2190 }
2191
2192
2193 static AtkObject *
2194 gtk_assistant_accessible_ref_child (AtkObject *accessible,
2195                                     gint       index)
2196 {
2197   GtkAssistant *assistant;
2198   GtkAssistantPrivate *priv;
2199   GtkWidget *widget, *child;
2200   gint n_pages;
2201   AtkObject *obj;
2202
2203   widget = GTK_ACCESSIBLE (accessible)->widget;
2204   if (!widget)
2205     return NULL;
2206
2207   assistant = GTK_ASSISTANT (widget);
2208   priv = assistant->priv;
2209   n_pages = g_list_length (priv->pages);
2210
2211   if (index < 0)
2212     return NULL;
2213   else if (index < n_pages)
2214     {
2215       GtkAssistantPage *page = g_list_nth_data (priv->pages, index / 2);
2216
2217       child = page->page;
2218     }
2219   else if (index == n_pages)
2220     {
2221       child = priv->action_area;
2222     }
2223   else
2224     return NULL;
2225   
2226   obj = gtk_widget_get_accessible (child);
2227
2228   return g_object_ref (obj);
2229 }
2230
2231 static void
2232 gtk_assistant_accessible_class_init (AtkObjectClass *class)
2233 {
2234   class->get_n_children = gtk_assistant_accessible_get_n_children;
2235   class->ref_child = gtk_assistant_accessible_ref_child;
2236 }
2237
2238 static GType
2239 gtk_assistant_accessible_get_type (void)
2240 {
2241   static GType type = 0;
2242   
2243   if (!type)
2244     {
2245       /*
2246        * Figure out the size of the class and instance
2247        * we are deriving from
2248        */
2249       AtkObjectFactory *factory;
2250       GType derived_type;
2251       GTypeQuery query;
2252       GType derived_atk_type;
2253       
2254       derived_type = g_type_parent (GTK_TYPE_ASSISTANT);
2255       factory = atk_registry_get_factory (atk_get_default_registry (),
2256                                           derived_type);
2257       derived_atk_type = atk_object_factory_get_accessible_type (factory);
2258       g_type_query (derived_atk_type, &query);
2259       
2260       type = g_type_register_static_simple (derived_atk_type, 
2261                                             "GtkAssistantAccessible", 
2262                                             query.class_size,
2263                                             (GClassInitFunc) gtk_assistant_accessible_class_init,
2264                                             query.instance_size,
2265                                             NULL, 0);
2266     }
2267
2268   return type;
2269 }
2270
2271 static AtkObject *
2272 gtk_assistant_accessible_new (GObject *obj)
2273 {
2274   AtkObject *accessible;
2275
2276   g_return_val_if_fail (GTK_IS_ASSISTANT (obj), NULL);
2277
2278   accessible = g_object_new (gtk_assistant_accessible_get_type (), NULL); 
2279   atk_object_initialize (accessible, obj);
2280   
2281   return accessible;
2282 }
2283
2284 static GType
2285 gtk_assistant_accessible_factory_get_accessible_type (void)
2286 {
2287   return gtk_assistant_accessible_get_type ();
2288 }
2289
2290 static AtkObject*
2291 gtk_assistant_accessible_factory_create_accessible (GObject *obj)
2292 {
2293   return gtk_assistant_accessible_new (obj);
2294 }
2295
2296 static void
2297 gtk_assistant_accessible_factory_class_init (AtkObjectFactoryClass *class)
2298 {
2299   class->create_accessible = gtk_assistant_accessible_factory_create_accessible;
2300   class->get_accessible_type = gtk_assistant_accessible_factory_get_accessible_type;
2301 }
2302
2303 static GType
2304 gtk_assistant_accessible_factory_get_type (void)
2305 {
2306   static GType type = 0;
2307
2308   if (!type) 
2309     {
2310       type = g_type_register_static_simple (ATK_TYPE_OBJECT_FACTORY, 
2311                                             "GtkAssistantAccessibleFactory",
2312                                             sizeof (AtkObjectFactoryClass),
2313                                             (GClassInitFunc) gtk_assistant_accessible_factory_class_init,
2314                                             sizeof (AtkObjectFactory),
2315                                             NULL, 0);
2316     }
2317   
2318   return type;
2319 }
2320
2321 static AtkObject *
2322 gtk_assistant_get_accessible (GtkWidget *widget)
2323 {
2324   static gboolean first_time = TRUE;
2325
2326   if (first_time) 
2327     {
2328       AtkObjectFactory *factory;
2329       AtkRegistry *registry;
2330       GType derived_type; 
2331       GType derived_atk_type; 
2332
2333       /*
2334        * Figure out whether accessibility is enabled by looking at the
2335        * type of the accessible object which would be created for
2336        * the parent type of GtkAssistant.
2337        */
2338       derived_type = g_type_parent (GTK_TYPE_ASSISTANT);
2339
2340       registry = atk_get_default_registry ();
2341       factory = atk_registry_get_factory (registry,
2342                                           derived_type);
2343       derived_atk_type = atk_object_factory_get_accessible_type (factory);
2344       if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
2345         {
2346           atk_registry_set_factory_type (registry, 
2347                                          GTK_TYPE_ASSISTANT,
2348                                          gtk_assistant_accessible_factory_get_type ());
2349         }
2350       first_time = FALSE;
2351     }
2352
2353   return GTK_WIDGET_CLASS (gtk_assistant_parent_class)->get_accessible (widget);
2354 }
2355
2356
2357 #define __GTK_ASSISTANT_C__
2358 #include "gtkaliasdef.c"