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