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