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