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