]> Pileus Git - ~andy/gtk/blob - gtk/gtkassistant.c
If the current page is removed, always pick a different one. (#392457,
[~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_container_set_reallocate_redraws (GTK_CONTAINER (assistant), TRUE);
682
683   gtk_widget_push_composite_child ();
684
685   /* Header */
686   priv->header_image = gtk_image_new ();
687   gtk_misc_set_alignment (GTK_MISC (priv->header_image), 1., 0.5);
688   gtk_widget_set_parent (priv->header_image, GTK_WIDGET (assistant));
689   gtk_widget_show (priv->header_image);
690
691   /* Sidebar */
692   priv->sidebar_image = gtk_image_new ();
693   gtk_misc_set_alignment (GTK_MISC (priv->sidebar_image), 0., 0.);
694   gtk_widget_set_parent (priv->sidebar_image, GTK_WIDGET (assistant));
695   gtk_widget_show (priv->sidebar_image);
696
697   /* Action area  */
698   priv->action_area  = gtk_hbox_new (FALSE, 6);
699   
700   assistant->close   = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
701   assistant->apply   = gtk_button_new_from_stock (GTK_STOCK_APPLY);
702   assistant->forward = gtk_button_new_from_stock (GTK_STOCK_GO_FORWARD);
703   assistant->back    = gtk_button_new_from_stock (GTK_STOCK_GO_BACK);
704   assistant->cancel  = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
705   assistant->last    = gtk_button_new_from_stock (GTK_STOCK_GOTO_LAST);
706
707   priv->size_group   = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
708   gtk_size_group_add_widget (priv->size_group, assistant->close);
709   gtk_size_group_add_widget (priv->size_group, assistant->apply);
710   gtk_size_group_add_widget (priv->size_group, assistant->forward);
711   gtk_size_group_add_widget (priv->size_group, assistant->back);
712   gtk_size_group_add_widget (priv->size_group, assistant->cancel);
713   gtk_size_group_add_widget (priv->size_group, assistant->last);
714
715   if (!alternative_button_order (assistant))
716     {
717       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->apply, FALSE, FALSE, 0);
718       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->forward, FALSE, FALSE, 0);
719       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->back, FALSE, FALSE, 0);
720       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->last, FALSE, FALSE, 0);
721       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->cancel, FALSE, FALSE, 0);
722       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->close, FALSE, FALSE, 0);
723     }
724   else
725     {
726       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->close, FALSE, FALSE, 0);
727       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->cancel, FALSE, FALSE, 0);
728       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->last, FALSE, FALSE, 0);
729       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->back, FALSE, FALSE, 0);
730       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->forward, FALSE, FALSE, 0);
731       gtk_box_pack_end (GTK_BOX (priv->action_area), assistant->apply, FALSE, FALSE, 0);
732     }
733
734   gtk_widget_set_parent (priv->action_area, GTK_WIDGET (assistant));
735   gtk_widget_show (assistant->forward);
736   gtk_widget_show (assistant->back);
737   gtk_widget_show (assistant->cancel);
738   gtk_widget_show (priv->action_area);
739
740   gtk_widget_pop_composite_child ();
741
742   priv->pages = NULL;
743   priv->current_page = NULL;
744   priv->visited_pages = NULL;
745
746   priv->forward_function = default_forward_function;
747   priv->forward_function_data = assistant;
748   priv->forward_data_destroy = NULL;
749
750   g_signal_connect (G_OBJECT (assistant->close), "clicked",
751                     G_CALLBACK (on_assistant_close), assistant);
752   g_signal_connect (G_OBJECT (assistant->apply), "clicked",
753                     G_CALLBACK (on_assistant_apply), assistant);
754   g_signal_connect (G_OBJECT (assistant->forward), "clicked",
755                     G_CALLBACK (on_assistant_forward), assistant);
756   g_signal_connect (G_OBJECT (assistant->back), "clicked",
757                     G_CALLBACK (on_assistant_back), assistant);
758   g_signal_connect (G_OBJECT (assistant->cancel), "clicked",
759                     G_CALLBACK (on_assistant_cancel), assistant);
760   g_signal_connect (G_OBJECT (assistant->last), "clicked",
761                     G_CALLBACK (on_assistant_last), assistant);
762 }
763
764 static void
765 gtk_assistant_set_child_property (GtkContainer    *container,
766                                   GtkWidget       *child,
767                                   guint            property_id,
768                                   const GValue    *value,
769                                   GParamSpec      *pspec)
770 {
771   switch (property_id)
772     {
773     case CHILD_PROP_PAGE_TYPE:
774       gtk_assistant_set_page_type (GTK_ASSISTANT (container), child,
775                                    g_value_get_enum (value));
776       break;
777     case CHILD_PROP_PAGE_TITLE:
778       gtk_assistant_set_page_title (GTK_ASSISTANT (container), child,
779                                     g_value_get_string (value));
780       break;
781     case CHILD_PROP_PAGE_HEADER_IMAGE:
782       gtk_assistant_set_page_header_image (GTK_ASSISTANT (container), child,
783                                            g_value_get_object (value));
784       break;
785     case CHILD_PROP_PAGE_SIDEBAR_IMAGE:
786       gtk_assistant_set_page_side_image (GTK_ASSISTANT (container), child,
787                                          g_value_get_object (value));
788       break;
789     case CHILD_PROP_PAGE_COMPLETE:
790       gtk_assistant_set_page_complete (GTK_ASSISTANT (container), child,
791                                        g_value_get_boolean (value));
792       break;
793     default:
794       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
795       break;
796     }
797 }
798
799 static void
800 gtk_assistant_get_child_property (GtkContainer *container,
801                                   GtkWidget    *child,
802                                   guint         property_id,
803                                   GValue       *value,
804                                   GParamSpec   *pspec)
805 {
806   switch (property_id)
807     {
808     case CHILD_PROP_PAGE_TYPE:
809       g_value_set_enum (value,
810                         gtk_assistant_get_page_type (GTK_ASSISTANT (container), child));
811       break;
812     case CHILD_PROP_PAGE_TITLE:
813       g_value_set_string (value,
814                           gtk_assistant_get_page_title (GTK_ASSISTANT (container), child));
815       break;
816     case CHILD_PROP_PAGE_HEADER_IMAGE:
817       g_value_set_object (value,
818                           gtk_assistant_get_page_header_image (GTK_ASSISTANT (container), child));
819       break;
820     case CHILD_PROP_PAGE_SIDEBAR_IMAGE:
821       g_value_set_object (value,
822                           gtk_assistant_get_page_side_image (GTK_ASSISTANT (container), child));
823       break;
824     case CHILD_PROP_PAGE_COMPLETE:
825       g_value_set_boolean (value,
826                            gtk_assistant_get_page_complete (GTK_ASSISTANT (container), child));
827       break;
828     default:
829       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
830       break;
831     }
832 }
833
834 static void
835 on_page_notify_visibility (GtkWidget  *widget,
836                            GParamSpec *arg,
837                            gpointer    data)
838 {
839   GtkAssistant *assistant = GTK_ASSISTANT (data);
840
841   /* update buttons state, flow may have changed */
842   if (GTK_WIDGET_MAPPED (assistant))
843     set_assistant_buttons_state (assistant);
844 }
845
846 static void
847 remove_page (GtkAssistant *assistant, 
848              GList        *element)
849 {
850   GtkAssistantPrivate *priv = assistant->priv;
851   GtkAssistantPage *page_info;
852   GList *page_node;
853
854   page_info = element->data;
855
856   /* If this is the current page, we need to switch away. */
857   if (page_info == priv->current_page)
858     {
859       if (!compute_next_step (assistant))
860         {
861           /* The best we can do at this point is probably to pick the first
862            * visible page.
863            */
864           page_node = priv->pages;
865
866           while (page_node && !GTK_WIDGET_VISIBLE (((GtkAssistantPage *) page_node->data)->page))
867             page_node = page_node->next;
868
869           if (page_node)
870             priv->current_page = page_node->data;
871           else
872             priv->current_page = NULL;
873         }
874     }
875
876   priv->pages = g_list_remove_link (priv->pages, element);
877   priv->visited_pages = g_slist_remove_all (priv->visited_pages, page_info);
878
879   g_signal_handlers_disconnect_by_func (page_info->page, on_page_notify_visibility, assistant);
880   gtk_widget_unparent (page_info->page);
881
882   if (page_info->header_image)
883     g_object_unref (page_info->header_image);
884
885   if (page_info->sidebar_image)
886     g_object_unref (page_info->sidebar_image);
887
888   gtk_widget_destroy (page_info->title);
889   g_free (page_info);
890   g_list_free_1 (element);
891 }
892
893 static void
894 gtk_assistant_destroy (GtkObject *object)
895 {
896   GtkAssistant *assistant = GTK_ASSISTANT (object);
897   GtkAssistantPrivate *priv = assistant->priv;
898
899   if (priv->header_image)
900     {
901       gtk_widget_destroy (priv->header_image);
902       priv->header_image = NULL;
903     }
904
905   if (priv->sidebar_image)
906     {
907       gtk_widget_destroy (priv->sidebar_image);
908       priv->sidebar_image = NULL;
909     }
910
911   if (priv->action_area)
912     {
913       gtk_widget_destroy (priv->action_area);
914       priv->action_area = NULL;
915     }
916
917   if (priv->size_group)
918     {
919       g_object_unref (priv->size_group);
920       priv->size_group = NULL;
921     }
922
923   if (priv->forward_function)
924     {
925       if (priv->forward_function_data &&
926           priv->forward_data_destroy)
927         priv->forward_data_destroy (priv->forward_function_data);
928
929       priv->forward_function = NULL;
930       priv->forward_function_data = NULL;
931       priv->forward_data_destroy = NULL;
932     }
933
934   if (priv->visited_pages)
935     {
936       g_slist_free (priv->visited_pages);
937       priv->visited_pages = NULL;
938     }
939
940   /* We set current to NULL so that the remove code doesn't try
941    * to do anything funny */
942   priv->current_page = NULL;
943
944   while (priv->pages)
945     remove_page (GTK_ASSISTANT (object), priv->pages);
946       
947   GTK_OBJECT_CLASS (gtk_assistant_parent_class)->destroy (object);
948 }
949
950 static GList*
951 find_page (GtkAssistant  *assistant,
952            GtkWidget     *page)
953 {
954   GtkAssistantPrivate *priv = assistant->priv;
955   GList *child = priv->pages;
956   
957   while (child)
958     {
959       GtkAssistantPage *page_info = child->data;
960       if (page_info->page == page)
961         return child;
962
963       child = child->next;
964     }
965   
966   return NULL;
967 }
968
969 static void
970 set_title_colors (GtkWidget *assistant,
971                   GtkWidget *title_label)
972 {
973   GtkStyle *style;
974
975   gtk_widget_ensure_style (assistant);
976   style = gtk_widget_get_style (assistant);
977
978   /* change colors schema, for making the header text visible */
979   gtk_widget_modify_bg (title_label, GTK_STATE_NORMAL, &style->bg[GTK_STATE_SELECTED]);
980   gtk_widget_modify_fg (title_label, GTK_STATE_NORMAL, &style->fg[GTK_STATE_SELECTED]);
981 }
982
983 static void
984 set_title_font (GtkWidget *assistant,
985                 GtkWidget *title_label)
986 {
987   PangoFontDescription *desc;
988   gint size;
989
990   desc = pango_font_description_new ();
991   size = pango_font_description_get_size (assistant->style->font_desc);
992
993   pango_font_description_set_weight (desc, PANGO_WEIGHT_ULTRABOLD);
994   pango_font_description_set_size   (desc, size * PANGO_SCALE_XX_LARGE);
995
996   gtk_widget_modify_font (title_label, desc);
997   pango_font_description_free (desc);
998 }
999
1000 static void
1001 gtk_assistant_style_set (GtkWidget *widget,
1002                          GtkStyle  *old_style)
1003 {
1004   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1005   GtkAssistantPrivate *priv = assistant->priv;
1006   GList *list;
1007
1008   list = priv->pages;
1009
1010   while (list)
1011     {
1012       GtkAssistantPage *page = list->data;
1013
1014       set_title_colors (widget, page->title);
1015       set_title_font (widget, page->title);
1016
1017       list = list->next;
1018     }
1019 }
1020
1021 static void
1022 gtk_assistant_size_request (GtkWidget      *widget,
1023                             GtkRequisition *requisition)
1024 {
1025   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1026   GtkAssistantPrivate *priv = assistant->priv;
1027   GtkRequisition child_requisition;
1028   gint header_padding, content_padding;
1029   gint width, height, header_width, header_height;
1030   GList *list;
1031
1032   gtk_widget_style_get (widget,
1033                         "header-padding", &header_padding,
1034                         "content-padding", &content_padding,
1035                         NULL);
1036   width = height = 0;
1037   header_width = header_height = 0;
1038   list  = priv->pages;
1039
1040   while (list)
1041     {
1042       GtkAssistantPage *page = list->data;
1043       gint w, h;
1044
1045       gtk_widget_size_request (page->page, &child_requisition);
1046       width  = MAX (width,  child_requisition.width);
1047       height = MAX (height, child_requisition.height);
1048
1049       gtk_widget_size_request (page->title, &child_requisition);
1050       w = child_requisition.width;
1051       h = child_requisition.height;
1052
1053       if (page->header_image)
1054         {
1055           w += gdk_pixbuf_get_width (page->header_image) + HEADER_SPACING;
1056           h  = MAX (h, gdk_pixbuf_get_height (page->header_image));
1057         }
1058
1059       header_width  = MAX (header_width, w);
1060       header_height = MAX (header_height, h);
1061
1062       list = list->next;
1063     }
1064
1065   gtk_widget_size_request (priv->sidebar_image, &child_requisition);
1066   width  += child_requisition.width;
1067   height  = MAX (height, child_requisition.height);
1068
1069   gtk_widget_set_size_request (priv->header_image, header_width, header_height);
1070   gtk_widget_size_request (priv->header_image, &child_requisition);
1071   width   = MAX (width, header_width) + 2 * header_padding;
1072   height += header_height + 2 * header_padding;
1073
1074   gtk_widget_size_request (priv->action_area, &child_requisition);
1075   width   = MAX (width, child_requisition.width);
1076   height += child_requisition.height + ACTION_AREA_SPACING;
1077
1078   width += GTK_CONTAINER (widget)->border_width * 2 + content_padding * 2;
1079   height += GTK_CONTAINER (widget)->border_width * 2 + content_padding * 2;
1080
1081   requisition->width = width;
1082   requisition->height = height;
1083 }
1084
1085 static void
1086 gtk_assistant_size_allocate (GtkWidget      *widget,
1087                              GtkAllocation  *allocation)
1088 {
1089   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1090   GtkAssistantPrivate *priv = assistant->priv;
1091   GtkRequisition header_requisition;
1092   GtkAllocation child_allocation, header_allocation;
1093   gint header_padding, content_padding;
1094   gboolean rtl;
1095   GList *pages;
1096
1097   rtl   = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1098   pages = priv->pages;
1099
1100   gtk_widget_style_get (widget,
1101                         "header-padding", &header_padding,
1102                         "content-padding", &content_padding,
1103                         NULL);
1104
1105   widget->allocation = *allocation;
1106
1107   /* Header */
1108   gtk_widget_get_child_requisition (priv->header_image, &header_requisition);
1109
1110   header_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width + header_padding;
1111   header_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width + header_padding;
1112   header_allocation.width  = allocation->width - 2 * GTK_CONTAINER (widget)->border_width - 2 * header_padding;
1113   header_allocation.height = header_requisition.height;
1114
1115   gtk_widget_size_allocate (priv->header_image, &header_allocation);
1116
1117   /* Action area */
1118   child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width;
1119   child_allocation.y = allocation->y + allocation->height -
1120     GTK_CONTAINER (widget)->border_width - priv->action_area->requisition.height;
1121   child_allocation.width  = allocation->width - 2 * GTK_CONTAINER (widget)->border_width;
1122   child_allocation.height = priv->action_area->requisition.height;
1123
1124   gtk_widget_size_allocate (priv->action_area, &child_allocation);
1125
1126   /* Sidebar */
1127   if (rtl)
1128     child_allocation.x = allocation->x + allocation->width -
1129       GTK_CONTAINER (widget)->border_width - priv->sidebar_image->requisition.width;
1130   else
1131     child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width;
1132
1133   child_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width +
1134     priv->header_image->allocation.height + 2 * header_padding;
1135   child_allocation.width = priv->sidebar_image->requisition.width;
1136   child_allocation.height = allocation->height - 2 * GTK_CONTAINER (widget)->border_width -
1137     priv->header_image->allocation.height - 2 * header_padding - priv->action_area->allocation.height;
1138
1139   gtk_widget_size_allocate (priv->sidebar_image, &child_allocation);
1140
1141   /* Pages */
1142   child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width + content_padding;
1143   child_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width +
1144     priv->header_image->allocation.height + 2 * header_padding + content_padding;
1145   child_allocation.width  = allocation->width - 2 * GTK_CONTAINER (widget)->border_width - 2 * content_padding;
1146   child_allocation.height = allocation->height - 2 * GTK_CONTAINER (widget)->border_width -
1147     priv->header_image->allocation.height - 2 * header_padding - ACTION_AREA_SPACING - priv->action_area->allocation.height - 2 * content_padding;
1148
1149   if (GTK_WIDGET_VISIBLE (priv->sidebar_image))
1150     {
1151       if (!rtl)
1152         child_allocation.x += priv->sidebar_image->allocation.width;
1153
1154       child_allocation.width -= priv->sidebar_image->allocation.width;
1155     }
1156
1157   while (pages)
1158     {
1159       GtkAssistantPage *page = pages->data;
1160
1161       gtk_widget_size_allocate (page->page, &child_allocation);
1162       gtk_widget_size_allocate (page->title, &header_allocation);
1163       pages = pages->next;
1164     }
1165 }
1166
1167 static void
1168 gtk_assistant_map (GtkWidget *widget)
1169 {
1170   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1171   GtkAssistantPrivate *priv = assistant->priv;
1172   GList *page_node;
1173
1174   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
1175
1176   gtk_widget_map (priv->header_image);
1177   gtk_widget_map (priv->action_area);
1178
1179   if (GTK_WIDGET_VISIBLE (priv->sidebar_image) &&
1180       !GTK_WIDGET_MAPPED (priv->sidebar_image))
1181     gtk_widget_map (priv->sidebar_image);
1182
1183   /* if there's no default page, pick the first one */
1184   if (!priv->current_page && priv->pages)
1185     {
1186       page_node = priv->pages;
1187
1188       while (page_node && !GTK_WIDGET_VISIBLE (((GtkAssistantPage *) page_node->data)->page))
1189         page_node = page_node->next;
1190
1191       if (page_node)
1192         priv->current_page = page_node->data;
1193     }
1194
1195   if (priv->current_page &&
1196       GTK_WIDGET_VISIBLE (priv->current_page->page) &&
1197       !GTK_WIDGET_MAPPED (priv->current_page->page))
1198     {
1199       set_assistant_buttons_state ((GtkAssistant *) widget);
1200       set_assistant_header_image ((GtkAssistant*) widget);
1201       set_assistant_sidebar_image ((GtkAssistant*) widget);
1202
1203       g_signal_emit (widget, signals [PREPARE], 0, priv->current_page->page);
1204       gtk_widget_set_child_visible (priv->current_page->page, TRUE);
1205       gtk_widget_map (priv->current_page->page);
1206       gtk_widget_map (priv->current_page->title);
1207     }
1208
1209   GTK_WIDGET_CLASS (gtk_assistant_parent_class)->map (widget);
1210 }
1211
1212 static void
1213 gtk_assistant_unmap (GtkWidget *widget)
1214 {
1215   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1216   GtkAssistantPrivate *priv = assistant->priv;
1217
1218   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
1219
1220   gtk_widget_unmap (priv->header_image);
1221   gtk_widget_unmap (priv->action_area);
1222
1223   if (GTK_WIDGET_DRAWABLE (priv->sidebar_image))
1224     gtk_widget_unmap (priv->sidebar_image);
1225
1226   if (priv->current_page &&
1227       GTK_WIDGET_DRAWABLE (priv->current_page->page))
1228     gtk_widget_unmap (priv->current_page->page);
1229
1230   g_slist_free (priv->visited_pages);
1231   priv->visited_pages = NULL;
1232   priv->current_page  = NULL;
1233
1234   GTK_WIDGET_CLASS (gtk_assistant_parent_class)->unmap (widget);
1235 }
1236
1237 static gboolean
1238 gtk_assistant_delete_event (GtkWidget   *widget,
1239                             GdkEventAny *event)
1240 {
1241   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1242   GtkAssistantPrivate *priv = assistant->priv;
1243
1244   /* Do not allow cancelling in the middle of a progress page */
1245   if (priv->current_page &&
1246       (priv->current_page->type != GTK_ASSISTANT_PAGE_PROGRESS ||
1247        priv->current_page->complete))
1248     g_signal_emit (widget, signals [CANCEL], 0, NULL);
1249
1250   return TRUE;
1251 }
1252
1253 static void
1254 assistant_paint_colored_box (GtkWidget *widget)
1255 {
1256   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1257   GtkAssistantPrivate *priv = assistant->priv;
1258   gint border_width, header_padding, content_padding;
1259   cairo_t *cr;
1260   gint content_x, content_width;
1261   gboolean rtl;
1262
1263   cr   = gdk_cairo_create (widget->window);
1264   rtl  = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1265   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1266
1267   gtk_widget_style_get (widget,
1268                         "header-padding",  &header_padding,
1269                         "content-padding", &content_padding,
1270                         NULL);
1271
1272   /* colored box */
1273   gdk_cairo_set_source_color (cr, &widget->style->bg[GTK_STATE_SELECTED]);
1274   cairo_rectangle (cr,
1275                    border_width,
1276                    border_width,
1277                    widget->allocation.width - 2 * border_width,
1278                    widget->allocation.height - priv->action_area->allocation.height - 2 * border_width - ACTION_AREA_SPACING);
1279   cairo_fill (cr);
1280
1281   /* content box */
1282   content_x = content_padding + border_width;
1283   content_width = widget->allocation.width - 2 * content_padding - 2 * border_width;
1284
1285   if (GTK_WIDGET_VISIBLE (priv->sidebar_image))
1286     {
1287       if (!rtl)
1288         content_x += priv->sidebar_image->allocation.width;
1289       content_width -= priv->sidebar_image->allocation.width;
1290     }
1291   
1292   gdk_cairo_set_source_color (cr, &widget->style->bg[GTK_STATE_NORMAL]);
1293
1294   cairo_rectangle (cr,
1295                    content_x,
1296                    priv->header_image->allocation.height + content_padding + 2 * header_padding + border_width,
1297                    content_width,
1298                    widget->allocation.height - 2 * border_width - priv->action_area->allocation.height -
1299                    priv->header_image->allocation.height - 2 * content_padding - 2 * header_padding - ACTION_AREA_SPACING);
1300   cairo_fill (cr);
1301
1302   cairo_destroy (cr);
1303 }
1304
1305 static gboolean
1306 gtk_assistant_expose (GtkWidget      *widget,
1307                       GdkEventExpose *event)
1308 {
1309   GtkAssistant *assistant = GTK_ASSISTANT (widget);
1310   GtkAssistantPrivate *priv = assistant->priv;
1311   GtkContainer *container;
1312
1313   if (GTK_WIDGET_DRAWABLE (widget))
1314     {
1315       container = GTK_CONTAINER (widget);
1316
1317       assistant_paint_colored_box (widget);
1318
1319       gtk_container_propagate_expose (container, priv->header_image, event);
1320       gtk_container_propagate_expose (container, priv->sidebar_image, event);
1321       gtk_container_propagate_expose (container, priv->action_area, event);
1322
1323       if (priv->current_page)
1324         {
1325           gtk_container_propagate_expose (container, priv->current_page->page, event);
1326           gtk_container_propagate_expose (container, priv->current_page->title, event);
1327         }
1328     }
1329
1330   return FALSE;
1331 }
1332
1333 static gboolean
1334 gtk_assistant_focus (GtkWidget        *widget,
1335                      GtkDirectionType  direction)
1336 {
1337   GtkAssistantPrivate *priv;
1338   GtkContainer *container;
1339
1340   container = GTK_CONTAINER (widget);
1341   priv = GTK_ASSISTANT (widget)->priv;
1342
1343   /* we only have to care about 2 widgets, action area and the current page */
1344   if (container->focus_child == priv->action_area)
1345     {
1346       if (!gtk_widget_child_focus (priv->action_area, direction) &&
1347           (priv->current_page == NULL ||
1348            !gtk_widget_child_focus (priv->current_page->page, direction)))
1349         {
1350           /* if we're leaving the action area and the current page hasn't
1351              any focusable widget, clear focus and go back to the action area */
1352           gtk_container_set_focus_child (GTK_CONTAINER (priv->action_area), NULL);
1353           gtk_widget_child_focus (priv->action_area, direction);
1354         }
1355     }
1356   else
1357     {
1358       if ((priv->current_page ==  NULL ||
1359            !gtk_widget_child_focus (priv->current_page->page, direction)) &&
1360           !gtk_widget_child_focus (priv->action_area, direction))
1361         {
1362           /* if we're leaving the current page and there isn't nothing focusable
1363              in the action area, try to clear focus and go back to the page */
1364           gtk_window_set_focus (GTK_WINDOW (widget), NULL);
1365           if (priv->current_page != NULL)
1366             gtk_widget_child_focus (priv->current_page->page, direction);
1367         }
1368     }
1369
1370   return TRUE;
1371 }
1372
1373 static void
1374 gtk_assistant_add (GtkContainer *container,
1375                    GtkWidget    *page)
1376 {
1377   g_return_if_fail (GTK_IS_WIDGET (page));
1378
1379   gtk_assistant_append_page (GTK_ASSISTANT (container), page);
1380 }
1381
1382 static void
1383 gtk_assistant_remove (GtkContainer *container,
1384                       GtkWidget    *page)
1385 {
1386   GtkAssistant *assistant;
1387   GList *element;
1388
1389   assistant = (GtkAssistant*) container;
1390
1391   element = find_page (assistant, page);
1392
1393   if (element)
1394     {
1395       remove_page (assistant, element);
1396       gtk_widget_queue_resize ((GtkWidget *) container);
1397     }
1398 }
1399
1400 static void
1401 gtk_assistant_forall (GtkContainer *container,
1402                       gboolean      include_internals,
1403                       GtkCallback   callback,
1404                       gpointer      callback_data)
1405 {
1406   GtkAssistant *assistant = (GtkAssistant*) container;
1407   GtkAssistantPrivate *priv = assistant->priv;
1408   GList *pages;
1409
1410   if (include_internals)
1411     {
1412       (*callback) (priv->header_image, callback_data);
1413       (*callback) (priv->sidebar_image, callback_data);
1414       (*callback) (priv->action_area, callback_data);
1415     }
1416
1417   pages = priv->pages;
1418
1419   while (pages)
1420     {
1421       GtkAssistantPage *page = (GtkAssistantPage *) pages->data;
1422
1423       (*callback) (page->page, callback_data);
1424
1425       if (include_internals)
1426         (*callback) (page->title, callback_data);
1427
1428       pages = pages->next;
1429     }
1430 }
1431
1432 /**
1433  * gtk_assistant_new:
1434  * 
1435  * Creates a new #GtkAssistant.
1436  *
1437  * Return value: a newly created #GtkAssistant
1438  *
1439  * Since: 2.10
1440  **/
1441 GtkWidget*
1442 gtk_assistant_new (void)
1443 {
1444   GtkWidget *assistant;
1445
1446   assistant = g_object_new (GTK_TYPE_ASSISTANT,
1447                             "border-width", 12,
1448                             NULL);
1449   return assistant;
1450 }
1451
1452 /**
1453  * gtk_assistant_get_current_page:
1454  * @assistant: a #GtkAssistant
1455  *
1456  * Returns the page number of the current page
1457  *
1458  * Return value: The index (starting from 0) of the current page in
1459  * the @assistant, if the @assistant has no pages, -1 will be returned
1460  *
1461  * Since: 2.10
1462  **/
1463 gint
1464 gtk_assistant_get_current_page (GtkAssistant *assistant)
1465 {
1466   GtkAssistantPrivate *priv;
1467
1468   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), -1);
1469
1470   priv = assistant->priv;
1471
1472   if (!priv->pages || !priv->current_page)
1473     return -1;
1474
1475   return g_list_index (priv->pages, priv->current_page);
1476 }
1477
1478 /**
1479  * gtk_assistant_set_current_page:
1480  * @assistant: a #GtkAssistant
1481  * @page_num: index of the page to switch to, starting from 0.
1482  *            If negative, the last page will be used. If greater
1483  *            than the number of pages in the @assistant, nothing
1484  *            will be done.
1485  *
1486  * Switches the page to @page_num. Note that this will only be necessary
1487  * in custom buttons, as the @assistant flow can be set with
1488  * gtk_assistant_set_forward_page_func().
1489  *
1490  * Since: 2.10
1491  **/
1492 void
1493 gtk_assistant_set_current_page (GtkAssistant *assistant,
1494                                 gint          page_num)
1495 {
1496   GtkAssistantPrivate *priv;
1497   GtkAssistantPage *page;
1498
1499   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1500
1501   priv = assistant->priv;
1502
1503   if (page_num >= 0)
1504     page = (GtkAssistantPage *) g_list_nth_data (priv->pages, page_num);
1505   else
1506     page = (GtkAssistantPage *) g_list_last (priv->pages);
1507
1508   g_return_if_fail (page != NULL);
1509
1510   if (priv->current_page == page)
1511     return;
1512
1513   /* only add the page to the visited list if the
1514    * assistant is mapped, if not, just use it as an
1515    * initial page setting, for the cases where the
1516    * initial page is != to 0
1517    */
1518   if (GTK_WIDGET_MAPPED (assistant))
1519     priv->visited_pages = g_slist_prepend (priv->visited_pages, page);
1520
1521   set_current_page (assistant, page);
1522 }
1523
1524 /**
1525  * gtk_assistant_get_n_pages:
1526  * @assistant: a #GtkAssistant
1527  *
1528  * Returns the number of pages in the @assistant
1529  *
1530  * Return value: The number of pages in the @assistant.
1531  *
1532  * Since: 2.10
1533  **/
1534 gint
1535 gtk_assistant_get_n_pages (GtkAssistant *assistant)
1536 {
1537   GtkAssistantPrivate *priv;
1538
1539   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1540
1541   priv = assistant->priv;
1542
1543   return g_list_length (priv->pages);
1544 }
1545
1546 /**
1547  * gtk_assistant_get_nth_page:
1548  * @assistant: a #GtkAssistant
1549  * @page_num: The index of a page in the @assistant, or -1 to get the last page;
1550  *
1551  * Returns the child widget contained in page number @page_num.
1552  *
1553  * Return value: The child widget, or %NULL if @page_num is out of bounds.
1554  *
1555  * Since: 2.10
1556  **/
1557 GtkWidget*
1558 gtk_assistant_get_nth_page (GtkAssistant *assistant,
1559                             gint          page_num)
1560 {
1561   GtkAssistantPrivate *priv;
1562   GtkAssistantPage *page;
1563   GList *elem;
1564
1565   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1566
1567   priv = assistant->priv;
1568
1569   elem = g_list_nth (priv->pages, page_num);
1570
1571   if (!elem)
1572     return NULL;
1573
1574   page = (GtkAssistantPage *) elem->data;
1575
1576   return page->page;
1577 }
1578
1579 /**
1580  * gtk_assistant_prepend_page:
1581  * @assistant: a #GtkAssistant
1582  * @page: a #GtkWidget
1583  *
1584  * Prepends a page to the @assistant.
1585  *
1586  * Return value: the index (starting at 0) of the inserted page
1587  *
1588  * Since: 2.10
1589  **/
1590 gint
1591 gtk_assistant_prepend_page (GtkAssistant *assistant,
1592                             GtkWidget    *page)
1593 {
1594   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1595   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1596
1597   return gtk_assistant_insert_page (assistant, page, 0);
1598 }
1599
1600 /**
1601  * gtk_assistant_append_page:
1602  * @assistant: a #GtkAssistant
1603  * @page: a #GtkWidget
1604  *
1605  * Appends a page to the @assistant.
1606  *
1607  * Return value: the index (starting at 0) of the inserted page
1608  *
1609  * Since: 2.10
1610  **/
1611 gint
1612 gtk_assistant_append_page (GtkAssistant *assistant,
1613                            GtkWidget    *page)
1614 {
1615   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1616   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1617
1618   return gtk_assistant_insert_page (assistant, page, -1);
1619 }
1620
1621 /**
1622  * gtk_assistant_insert_page:
1623  * @assistant: a #GtkAssistant
1624  * @page: a #GtkWidget
1625  * @position: the index (starting at 0) at which to insert the page,
1626  *            or -1 to append the page to the @assistant
1627  *
1628  * Inserts a page in the @assistant at a given position.
1629  *
1630  * Return value: the index (starting from 0) of the inserted page
1631  *
1632  * Since: 2.10
1633  **/
1634 gint
1635 gtk_assistant_insert_page (GtkAssistant *assistant,
1636                            GtkWidget    *page,
1637                            gint          position)
1638 {
1639   GtkAssistantPrivate *priv;
1640   GtkAssistantPage *page_info;
1641   gint n_pages;
1642
1643   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1644   g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1645   g_return_val_if_fail (page->parent == NULL, 0);
1646   g_return_val_if_fail (!GTK_WIDGET_TOPLEVEL (page), 0);
1647
1648   priv = assistant->priv;
1649
1650   page_info = g_new0 (GtkAssistantPage, 1);
1651   page_info->page  = page;
1652   page_info->title = gtk_label_new (NULL);
1653
1654   g_signal_connect (G_OBJECT (page), "notify::visible",
1655                     G_CALLBACK (on_page_notify_visibility), assistant);
1656
1657   gtk_misc_set_alignment (GTK_MISC (page_info->title), 0.,0.5);
1658   set_title_colors (GTK_WIDGET (assistant), page_info->title);
1659   set_title_font   (GTK_WIDGET (assistant), page_info->title);
1660   gtk_widget_show  (page_info->title);
1661
1662   n_pages = g_list_length (priv->pages);
1663
1664   if (position < 0 || position > n_pages)
1665     position = n_pages;
1666
1667   priv->pages = g_list_insert (priv->pages, page_info, position);
1668
1669   gtk_widget_set_child_visible (page_info->page, FALSE);
1670   gtk_widget_set_parent (page_info->page,  GTK_WIDGET (assistant));
1671   gtk_widget_set_parent (page_info->title, GTK_WIDGET (assistant));
1672
1673   if (GTK_WIDGET_REALIZED (GTK_WIDGET (assistant)))
1674     {
1675       gtk_widget_realize (page_info->page);
1676       gtk_widget_realize (page_info->title);
1677     }
1678
1679   gtk_widget_queue_resize (GTK_WIDGET (assistant));
1680
1681   return position;
1682 }
1683
1684 /**
1685  * gtk_assistant_set_forward_page_func:
1686  * @assistant: a #GtkAssistant
1687  * @page_func: the #GtkAssistantPageFunc, or %NULL to use the default one
1688  * @data: user data for @page_func
1689  * @destroy: destroy notifier for @data
1690  *
1691  * Sets the page forwarding function to be @page_func, this function will
1692  * be used to determine what will be the next page when the user presses
1693  * the forward button. Setting @page_func to %NULL will make the assistant
1694  * to use the default forward function, which just goes to the next visible 
1695  * page.
1696  *
1697  * Since: 2.10
1698  **/
1699 void
1700 gtk_assistant_set_forward_page_func (GtkAssistant         *assistant,
1701                                      GtkAssistantPageFunc  page_func,
1702                                      gpointer              data,
1703                                      GDestroyNotify        destroy)
1704 {
1705   GtkAssistantPrivate *priv;
1706
1707   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1708
1709   priv = assistant->priv;
1710
1711   if (priv->forward_data_destroy &&
1712       priv->forward_function_data)
1713     (*priv->forward_data_destroy) (priv->forward_function_data);
1714
1715   if (page_func)
1716     {
1717       priv->forward_function = page_func;
1718       priv->forward_function_data = data;
1719       priv->forward_data_destroy = destroy;
1720     }
1721   else
1722     {
1723       priv->forward_function = default_forward_function;
1724       priv->forward_function_data = assistant;
1725       priv->forward_data_destroy = NULL;
1726     }
1727
1728   /* Page flow has possibly changed, so the
1729      buttons state might need to change too */
1730   if (priv->current_page)
1731     set_assistant_buttons_state (assistant);
1732 }
1733
1734 /**
1735  * gtk_assistant_add_action_widget:
1736  * @assistant: a #GtkAssistant
1737  * @child: a #GtkWidget
1738  * 
1739  * Adds a widget to the action area of a #GtkAssistant.
1740  *
1741  * Since: 2.10
1742  **/
1743 void
1744 gtk_assistant_add_action_widget (GtkAssistant *assistant,
1745                                  GtkWidget    *child)
1746 {
1747   GtkAssistantPrivate *priv;
1748
1749   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1750   g_return_if_fail (GTK_IS_WIDGET (child));
1751
1752   priv = assistant->priv;
1753
1754   if (GTK_IS_BUTTON (child))
1755     gtk_size_group_add_widget (priv->size_group, child);
1756
1757   gtk_box_pack_end (GTK_BOX (priv->action_area), child, FALSE, FALSE, 0);
1758 }
1759
1760 /**
1761  * gtk_assistant_remove_action_widget:
1762  * @assistant: a #GtkAssistant
1763  * @child: a #GtkWidget
1764  *
1765  * Removes a widget from the action area of a #GtkAssistant.
1766  *
1767  * Since: 2.10
1768  **/
1769 void
1770 gtk_assistant_remove_action_widget (GtkAssistant *assistant,
1771                                     GtkWidget    *child)
1772 {
1773   GtkAssistantPrivate *priv;
1774
1775   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1776   g_return_if_fail (GTK_IS_WIDGET (child));
1777
1778   priv = assistant->priv;
1779
1780   if (GTK_IS_BUTTON (child))
1781     gtk_size_group_remove_widget (priv->size_group, child);
1782
1783   gtk_container_remove (GTK_CONTAINER (priv->action_area), child);
1784 }
1785
1786 /**
1787  * gtk_assistant_set_page_title:
1788  * @assistant: a #GtkAssistant
1789  * @page: a page of @assistant
1790  * @title: the new title for @page
1791  * 
1792  * Sets a title for @page. The title is displayed in the header
1793  * area of the assistant when @page is the current page.
1794  *
1795  * Since: 2.10
1796  **/
1797 void
1798 gtk_assistant_set_page_title (GtkAssistant *assistant,
1799                               GtkWidget    *page,
1800                               const gchar  *title)
1801 {
1802   GtkAssistantPage *page_info;
1803   GList *child;
1804
1805   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1806   g_return_if_fail (GTK_IS_WIDGET (page));
1807
1808   child = find_page (assistant, page);
1809
1810   g_return_if_fail (child != NULL);
1811
1812   page_info = (GtkAssistantPage*) child->data;
1813
1814   gtk_label_set_text ((GtkLabel*) page_info->title, title);
1815   gtk_widget_queue_resize (GTK_WIDGET (assistant));
1816   gtk_widget_child_notify (page, "title");
1817 }
1818
1819 /**
1820  * gtk_assistant_get_page_title:
1821  * @assistant: a #GtkAssistant
1822  * @page: a page of @assistant
1823  * 
1824  * Gets the title for @page. 
1825  * 
1826  * Return value: the title for @page.
1827  *
1828  * Since: 2.10
1829  **/
1830 G_CONST_RETURN gchar*
1831 gtk_assistant_get_page_title (GtkAssistant *assistant,
1832                               GtkWidget    *page)
1833 {
1834   GtkAssistantPage *page_info;
1835   GList *child;
1836
1837   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1838   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
1839
1840   child = find_page (assistant, page);
1841
1842   g_return_val_if_fail (child != NULL, NULL);
1843
1844   page_info = (GtkAssistantPage*) child->data;
1845
1846   return gtk_label_get_text ((GtkLabel*) page_info->title);
1847 }
1848
1849 /**
1850  * gtk_assistant_set_page_type:
1851  * @assistant: a #GtkAssistant
1852  * @page: a page of @assistant
1853  * @type: the new type for @page
1854  * 
1855  * Sets the page type for @page. The page type determines the page
1856  * behavior in the @assistant.
1857  *
1858  * Since: 2.10
1859  **/
1860 void
1861 gtk_assistant_set_page_type (GtkAssistant         *assistant,
1862                              GtkWidget            *page,
1863                              GtkAssistantPageType  type)
1864 {
1865   GtkAssistantPrivate *priv;
1866   GtkAssistantPage *page_info;
1867   GList *child;
1868
1869   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1870   g_return_if_fail (GTK_IS_WIDGET (page));
1871
1872   priv = assistant->priv;
1873   child = find_page (assistant, page);
1874
1875   g_return_if_fail (child != NULL);
1876
1877   page_info = (GtkAssistantPage*) child->data;
1878
1879   if (type != page_info->type)
1880     {
1881       page_info->type = type;
1882
1883       /* Always set buttons state, a change in a future page
1884          might change current page buttons */
1885       if (priv->current_page)
1886         set_assistant_buttons_state (assistant);
1887
1888       gtk_widget_child_notify (page, "page-type");
1889     }
1890 }
1891
1892 /**
1893  * gtk_assistant_get_page_type:
1894  * @assistant: a #GtkAssistant
1895  * @page: a page of @assistant
1896  *
1897  * Gets the page type of @page.
1898  *
1899  * Return value: the page type of @page.
1900  *
1901  * Since: 2.10
1902  **/
1903 GtkAssistantPageType
1904 gtk_assistant_get_page_type (GtkAssistant *assistant,
1905                              GtkWidget    *page)
1906 {
1907   GtkAssistantPage *page_info;
1908   GList *child;
1909
1910   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), GTK_ASSISTANT_PAGE_CONTENT);
1911   g_return_val_if_fail (GTK_IS_WIDGET (page), GTK_ASSISTANT_PAGE_CONTENT);
1912
1913   child = find_page (assistant, page);
1914
1915   g_return_val_if_fail (child != NULL, GTK_ASSISTANT_PAGE_CONTENT);
1916
1917   page_info = (GtkAssistantPage*) child->data;
1918
1919   return page_info->type;
1920 }
1921
1922 /**
1923  * gtk_assistant_set_page_header_image:
1924  * @assistant: a #GtkAssistant
1925  * @page: a page of @assistant
1926  * @pixbuf: the new header image @page
1927  * 
1928  * Sets a header image for @page. This image is displayed in the header
1929  * area of the assistant when @page is the current page.
1930  *
1931  * Since: 2.10
1932  **/
1933 void
1934 gtk_assistant_set_page_header_image (GtkAssistant *assistant,
1935                                      GtkWidget    *page,
1936                                      GdkPixbuf    *pixbuf)
1937 {
1938   GtkAssistantPrivate *priv;
1939   GtkAssistantPage *page_info;
1940   GList *child;
1941
1942   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1943   g_return_if_fail (GTK_IS_WIDGET (page));
1944   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
1945
1946   priv = assistant->priv;
1947   child = find_page (assistant, page);
1948
1949   g_return_if_fail (child != NULL);
1950
1951   page_info = (GtkAssistantPage*) child->data;
1952
1953   if (pixbuf != page_info->header_image)
1954     {
1955       if (page_info->header_image)
1956         {
1957           g_object_unref (page_info->header_image);
1958           page_info->header_image = NULL;
1959         }
1960
1961       if (pixbuf)
1962         page_info->header_image = g_object_ref (pixbuf);
1963
1964       if (page_info == priv->current_page)
1965         set_assistant_header_image (assistant);
1966
1967       gtk_widget_child_notify (page, "header-image");
1968     }
1969 }
1970
1971 /**
1972  * gtk_assistant_get_page_header_image:
1973  * @assistant: a #GtkAssistant
1974  * @page: a page of @assistant
1975  * 
1976  * Gets the header image for @page. 
1977  * 
1978  * Return value: the header image for @page, or %NULL
1979  * if there's no header image for the page.
1980  *
1981  * Since: 2.10
1982  **/
1983 GdkPixbuf*
1984 gtk_assistant_get_page_header_image (GtkAssistant *assistant,
1985                                      GtkWidget    *page)
1986 {
1987   GtkAssistantPage *page_info;
1988   GList *child;
1989
1990   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1991   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
1992
1993   child = find_page (assistant, page);
1994
1995   g_return_val_if_fail (child != NULL, NULL);
1996
1997   page_info = (GtkAssistantPage*) child->data;
1998
1999   return page_info->header_image;
2000 }
2001
2002 /**
2003  * gtk_assistant_set_page_side_image:
2004  * @assistant: a #GtkAssistant
2005  * @page: a page of @assistant
2006  * @pixbuf: the new header image @page
2007  * 
2008  * Sets a header image for @page. This image is displayed in the side
2009  * area of the assistant when @page is the current page.
2010  *
2011  * Since: 2.10
2012  **/
2013 void
2014 gtk_assistant_set_page_side_image (GtkAssistant *assistant,
2015                                    GtkWidget    *page,
2016                                    GdkPixbuf    *pixbuf)
2017 {
2018   GtkAssistantPrivate *priv;
2019   GtkAssistantPage *page_info;
2020   GList *child;
2021
2022   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2023   g_return_if_fail (GTK_IS_WIDGET (page));
2024   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
2025
2026   priv = assistant->priv;
2027   child = find_page (assistant, page);
2028
2029   g_return_if_fail (child != NULL);
2030
2031   page_info = (GtkAssistantPage*) child->data;
2032
2033   if (pixbuf != page_info->sidebar_image)
2034     {
2035       if (page_info->sidebar_image)
2036         {
2037           g_object_unref (page_info->sidebar_image);
2038           page_info->sidebar_image = NULL;
2039         }
2040
2041       if (pixbuf)
2042         page_info->sidebar_image = g_object_ref (pixbuf);
2043
2044       if (page_info == priv->current_page)
2045         set_assistant_sidebar_image (assistant);
2046
2047       gtk_widget_child_notify (page, "sidebar-image");
2048     }
2049 }
2050
2051 /**
2052  * gtk_assistant_get_page_side_image:
2053  * @assistant: a #GtkAssistant
2054  * @page: a page of @assistant
2055  * 
2056  * Gets the header image for @page. 
2057  * 
2058  * Return value: the side image for @page, or %NULL
2059  * if there's no side image for the page.
2060  *
2061  * Since: 2.10
2062  **/
2063 GdkPixbuf*
2064 gtk_assistant_get_page_side_image (GtkAssistant *assistant,
2065                                    GtkWidget    *page)
2066 {
2067   GtkAssistantPage *page_info;
2068   GList *child;
2069
2070   g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2071   g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
2072
2073   child = find_page (assistant, page);
2074
2075   g_return_val_if_fail (child != NULL, NULL);
2076
2077   page_info = (GtkAssistantPage*) child->data;
2078
2079   return page_info->sidebar_image;
2080 }
2081
2082 /**
2083  * gtk_assistant_set_page_complete:
2084  * @assistant: a #GtkAssistant
2085  * @page: a page of @assistant
2086  * @complete: the completeness status of the page
2087  * 
2088  * Sets whether @page contents are complete. This will make
2089  * @assistant update the buttons state to be able to continue the task.
2090  *
2091  * Since: 2.10
2092  **/
2093 void
2094 gtk_assistant_set_page_complete (GtkAssistant *assistant,
2095                                  GtkWidget    *page,
2096                                  gboolean      complete)
2097 {
2098   GtkAssistantPrivate *priv;
2099   GtkAssistantPage *page_info;
2100   GList *child;
2101
2102   g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2103   g_return_if_fail (GTK_IS_WIDGET (page));
2104
2105   priv = assistant->priv;
2106   child = find_page (assistant, page);
2107
2108   g_return_if_fail (child != NULL);
2109
2110   page_info = (GtkAssistantPage*) child->data;
2111
2112   if (complete != page_info->complete)
2113     {
2114       page_info->complete = complete;
2115
2116       /* Always set buttons state, a change in a future page
2117          might change current page buttons */
2118       if (priv->current_page)
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                                             "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                                             "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"