5283e61b89a60992a932148c89a42d4fca1940da
[modest] / src / maemo / easysetup / modest-wizard-dialog.c
1 /*
2  * This is a copy of modest-wizard-dialog.h with a rename and some API additions,
3  * for osso-modest-easysetup.
4  * 
5  * This file was part of modest-libs
6  *
7  * Copyright (C) 2005, 2006, 2007 Nokia Corporation, all rights reserved.
8  *
9  */
10  
11 /**
12  * SECTION:modest-wizard-dialog
13  * @short_description: A widget to create a guided installation
14  * process wizard
15  *
16  * #ModestWizardDialog is a widget to create a guided installation
17  * process. The dialog has four standard buttons, previous, next,
18  * finish, cancel, and contains several pages with optional icons.
19  * Response buttons are dimmed/undimmed automatically and the standard
20  * icon is shown/hidden in response to page navigation. The notebook
21  * widget provided by users contains the actual wizard pages.
22  */
23
24 #include <gtk/gtkdialog.h>
25 #include <gtk/gtknotebook.h>
26 #include <gtk/gtkimage.h>
27 #include <gtk/gtkbox.h>
28 #include <gtk/gtkhbox.h>
29 #include <gtk/gtkvbox.h>
30 #include <gtk/gtkbutton.h>
31 #include <hildon-widgets/hildon-defines.h>
32
33 #include "modest-wizard-dialog.h"
34
35 #include <libintl.h>
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 /* Specify the hildon-libs translation domain,
42  * so we can reuse its translations 
43  * instead of repeating them in our own translations.
44  */
45 /* #define _(String) dgettext(PACKAGE, String) */
46
47 #define _(String) dgettext("hildon-libs", String)
48
49 static GtkDialogClass *parent_class;
50
51 static void class_init              (ModestWizardDialogClass   *wizard_dialog_class);
52
53 static void init                    (ModestWizardDialog        *wizard_dialog);
54
55 static void create_title            (ModestWizardDialog        *wizard_dialog);
56
57 static void set_property            (GObject                   *object,
58                                      guint                     property_id,
59                                      const GValue              *value,
60                                      GParamSpec                *pspec);
61
62 static void get_property            (GObject                   *object,
63                                      guint                     property_id,
64                                      GValue                    *value,
65                                      GParamSpec                *pspec);
66
67 static void finalize                (GObject                   *object);
68
69 static void response                (ModestWizardDialog        *wizard, 
70                                      gint                      response_id,
71                                      gpointer                  unused);
72
73 static void make_buttons_sensitive  (ModestWizardDialog *wizard_dialog,
74                                      gboolean           previous,
75                                      gboolean           finish,
76                                      gboolean next);
77                                      
78 static gboolean invoke_before_next_vfunc (ModestWizardDialog *wizard_dialog);
79 static void invoke_enable_buttons_vfunc (ModestWizardDialog *wizard_dialog);
80
81 enum {
82     PROP_ZERO,
83     PROP_WIZARD_NAME,
84     PROP_WIZARD_NOTEBOOK,
85     PROP_WIZARD_AUTOTITLE
86 };
87
88 struct _ModestWizardDialogPrivate {
89     gchar       *wizard_name;
90     GtkNotebook *notebook;
91     GtkBox      *box;
92     GtkWidget   *image;
93     gboolean    autotitle;
94 };
95
96
97 GType
98 modest_wizard_dialog_get_type (void)
99 {
100     static GType wizard_dialog_type = 0;
101
102     if (!wizard_dialog_type) {
103
104         static const GTypeInfo wizard_dialog_info = {
105             sizeof (ModestWizardDialogClass),
106             NULL,       /* base_init      */
107             NULL,       /* base_finalize  */
108             (GClassInitFunc) class_init,
109             NULL,       /* class_finalize */
110             NULL,       /* class_data     */
111             sizeof (ModestWizardDialog),
112             0,          /* n_preallocs    */
113             (GInstanceInitFunc) init,
114         };
115
116         wizard_dialog_type = g_type_register_static (GTK_TYPE_DIALOG,
117                                                      "ModestWizardDialog",
118                                                      &wizard_dialog_info,
119                                                      0);
120     }
121
122     return wizard_dialog_type;
123 }
124
125 static void
126 class_init (ModestWizardDialogClass *wizard_dialog_class)
127 {
128     GObjectClass *object_class = G_OBJECT_CLASS (wizard_dialog_class);
129
130     parent_class = g_type_class_peek_parent (wizard_dialog_class);
131
132     g_type_class_add_private (wizard_dialog_class,
133                               sizeof(ModestWizardDialogPrivate));
134
135     /* Override virtual methods */
136     object_class->set_property = set_property;
137     object_class->get_property = get_property;
138     object_class->finalize     = finalize;
139
140     /**
141      * ModestWizardDialog:wizard-name:
142      *
143      * The name of the wizard.
144      */
145     g_object_class_install_property (object_class, PROP_WIZARD_NAME,
146             g_param_spec_string 
147             ("wizard-name",
148              "Wizard Name",
149              "The name of the ModestWizardDialog",
150              NULL,
151              G_PARAM_READWRITE));
152
153     /**
154      * ModestWizardDialog:wizard-notebook:
155      *
156      * The notebook object, which is used by the ModestWizardDialog.
157      */
158     g_object_class_install_property(object_class, PROP_WIZARD_NOTEBOOK,
159             g_param_spec_object 
160             ("wizard-notebook",
161              "Wizard Notebook",
162              "GtkNotebook object to be used in the "
163              "ModestWizardDialog",
164              GTK_TYPE_NOTEBOOK, G_PARAM_READWRITE));
165
166     /**
167      * ModestWizardDialog:autotitle
168      *
169      * If the wizard should automatically try to change the window title when changing steps. 
170      * Set to FALSE if you'd like to override the default behaviour. 
171      *
172      * Since: 0.14.5 
173      */
174     g_object_class_install_property(object_class, PROP_WIZARD_AUTOTITLE,
175             g_param_spec_boolean 
176             ("autotitle",
177              "AutoTitle",
178              "If the wizard should autotitle itself",
179              TRUE, 
180              G_PARAM_READWRITE));
181 }
182
183 static void 
184 finalize (GObject *object)
185 {
186     ModestWizardDialog *dialog = MODEST_WIZARD_DIALOG (object);
187     g_return_if_fail (dialog != NULL);
188
189     if (dialog->priv->wizard_name != NULL)
190         g_free (MODEST_WIZARD_DIALOG (object)->priv->wizard_name);
191     
192     if (G_OBJECT_CLASS (parent_class)->finalize)
193         G_OBJECT_CLASS (parent_class)->finalize(object);
194 }
195
196 /* Disable or enable the Previous, Next and Finish buttons */
197 static void
198 make_buttons_sensitive (ModestWizardDialog *wizard_dialog,
199                         gboolean previous,
200                         gboolean finish,
201                         gboolean next)
202 {
203     gtk_dialog_set_response_sensitive (GTK_DIALOG (wizard_dialog),
204                                        MODEST_WIZARD_DIALOG_PREVIOUS,
205                                        previous);
206
207     gtk_dialog_set_response_sensitive (GTK_DIALOG (wizard_dialog),
208                                        MODEST_WIZARD_DIALOG_FINISH,
209                                        finish);
210
211     gtk_dialog_set_response_sensitive (GTK_DIALOG (wizard_dialog),
212                                        MODEST_WIZARD_DIALOG_NEXT,
213                                        next);
214 }
215
216 static void 
217 init (ModestWizardDialog *wizard_dialog)
218 {
219     /* Initialize private structure for faster member access */
220     ModestWizardDialogPrivate *priv =
221         G_TYPE_INSTANCE_GET_PRIVATE (wizard_dialog,
222                 MODEST_TYPE_WIZARD_DIALOG,
223                 ModestWizardDialogPrivate);
224
225     GtkDialog *dialog = GTK_DIALOG (wizard_dialog);
226
227     /* Init internal widgets */
228     GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
229     gtk_dialog_set_has_separator (dialog, FALSE);
230     wizard_dialog->priv = priv;
231     priv->box = GTK_BOX (gtk_hbox_new (FALSE, 0));
232     priv->image = gtk_image_new_from_icon_name ("qgn_widg_wizard",
233             HILDON_ICON_SIZE_WIDG_WIZARD);
234
235     /* Default values for user provided properties */
236     priv->notebook = NULL;
237     priv->wizard_name = NULL;
238     priv->autotitle = TRUE;
239
240     /* Build wizard layout */
241     gtk_box_pack_start_defaults (GTK_BOX (dialog->vbox), GTK_WIDGET (priv->box));
242     gtk_box_pack_start_defaults (GTK_BOX (priv->box), GTK_WIDGET (vbox));
243     gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (priv->image), FALSE, FALSE, 0);
244
245     /* Add response buttons: finish, previous, next, cancel */
246     gtk_dialog_add_button (dialog, _("ecdg_bd_wizard_finish"), MODEST_WIZARD_DIALOG_FINISH);
247     gtk_dialog_add_button (dialog, _("ecdg_bd_wizard_previous"), MODEST_WIZARD_DIALOG_PREVIOUS);
248     gtk_dialog_add_button (dialog, _("ecdg_bd_wizard_next"), MODEST_WIZARD_DIALOG_NEXT);
249     gtk_dialog_add_button (dialog, _("ecdg_bd_wizard_cancel"), MODEST_WIZARD_DIALOG_CANCEL);
250
251     /* Set initial button states: previous and finish buttons are disabled */
252     make_buttons_sensitive (wizard_dialog, FALSE, FALSE, TRUE);
253
254     /* Show all the internal widgets */
255     gtk_widget_show_all (GTK_WIDGET (dialog->vbox));
256
257     /* connect to dialog's response signal */
258     g_signal_connect (G_OBJECT (dialog), "response",
259             G_CALLBACK (response), NULL);
260 }
261
262 static void
263 set_property (GObject      *object, 
264               guint        property_id,
265               const GValue *value, 
266               GParamSpec   *pspec)
267 {
268     ModestWizardDialogPrivate *priv = MODEST_WIZARD_DIALOG(object)->priv;
269
270     switch (property_id) {
271
272         case PROP_WIZARD_AUTOTITLE:
273
274             priv->autotitle = g_value_get_boolean (value);
275
276             if (priv->autotitle && 
277                 priv->wizard_name && 
278                 priv->notebook)
279                 create_title (MODEST_WIZARD_DIALOG (object));
280             else if (priv->wizard_name)
281                 gtk_window_set_title (GTK_WINDOW (object), priv->wizard_name);
282             
283             break;
284
285         case PROP_WIZARD_NAME: 
286
287             /* Set new wizard name. This name will appear in titlebar */
288             if (priv->wizard_name)
289                 g_free (priv->wizard_name);
290
291             gchar *str = (gchar *) g_value_get_string (value);
292             g_return_if_fail (str != NULL);
293
294             priv->wizard_name = g_strdup (str);
295
296             /* We need notebook in order to create title, since page information
297                is used in title generation */
298             
299             if (priv->notebook && priv->autotitle)
300                 create_title (MODEST_WIZARD_DIALOG (object));
301     
302             break;
303
304         case PROP_WIZARD_NOTEBOOK: {
305
306             GtkNotebook *book = GTK_NOTEBOOK (g_value_get_object (value));
307             g_return_if_fail (book != NULL);
308
309             priv->notebook = book;
310
311             /* Set the default properties for the notebook (disable tabs,
312              * and remove borders) to make it look like a nice wizard widget */
313             gtk_notebook_set_show_tabs (priv->notebook, FALSE);
314             gtk_notebook_set_show_border (priv->notebook, FALSE);
315             gtk_box_pack_start_defaults (GTK_BOX( priv->box), GTK_WIDGET (priv->notebook));
316
317             /* Show the notebook so that a gtk_widget_show on the dialog is
318              * all that is required to display the dialog correctly */
319             gtk_widget_show ( GTK_WIDGET (priv->notebook));
320
321             /* Update dialog title to reflect current page stats etc */        
322             if (priv->wizard_name && priv->autotitle)
323                 create_title (MODEST_WIZARD_DIALOG (object));
324             
325             } break;
326
327         default:
328             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
329             break;
330     }
331 }
332
333 static void
334 get_property (GObject      *object,
335               guint        property_id,
336               GValue       *value,
337               GParamSpec   *pspec)
338 {
339     ModestWizardDialogPrivate *priv = MODEST_WIZARD_DIALOG (object)->priv;
340
341     switch (property_id) {
342
343         case PROP_WIZARD_NAME:
344             g_value_set_string (value, priv->wizard_name);
345             break;
346
347         case PROP_WIZARD_NOTEBOOK:
348             g_value_set_object (value, priv->notebook);
349             break;
350
351         default:
352             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
353             break;
354     }
355 }
356
357 /*
358  * Creates the title of the dialog taking into account the current 
359  * page of the notebook.
360  */
361 static void
362 create_title (ModestWizardDialog *wizard_dialog)
363 {
364     gint pages, current;
365     gchar *str = NULL;
366     ModestWizardDialogPrivate *priv = wizard_dialog->priv;
367     GtkNotebook *notebook = priv->notebook;
368
369     if (!notebook)
370         return;
371
372     /* Get page information, we'll need that when creating title */
373     pages = gtk_notebook_get_n_pages (notebook);
374     current = gtk_notebook_get_current_page (priv->notebook);
375     if (current < 0)
376         current = 0;
377
378     /* the welcome title on the initial page */
379     if (current == 0) {
380         str = g_strdup_printf (_("ecdg_ti_wizard_welcome"), 
381                 priv->wizard_name, pages);
382     } else {
383         const gchar *steps = gtk_notebook_get_tab_label_text (notebook,
384                 gtk_notebook_get_nth_page (notebook, current));
385
386         str = g_strdup_printf (_("ecdg_ti_wizard_step"), 
387                 priv->wizard_name, current + 1, pages, steps);
388     }
389
390     /* Update the dialog to display the generated title */
391     gtk_window_set_title (GTK_WINDOW (wizard_dialog), str);
392     g_free (str);
393 }
394
395 /*
396  * Response signal handler. This function is needed because GtkDialog's 
397  * handler for this signal closes the dialog and we don't want that, we 
398  * want to change pages and, dimm certain response buttons. Overriding the 
399  * virtual function would not work because that would be called after the 
400  * signal handler implemented by GtkDialog.
401  * FIXME: There is a much saner way to do that [MDK]
402  */
403 static void 
404 response (ModestWizardDialog   *wizard_dialog,
405           gint                 response_id,
406           gpointer             unused)
407 {
408     ModestWizardDialogPrivate *priv = wizard_dialog->priv;
409     GtkNotebook *notebook = priv->notebook;
410     gint current = 0;
411     gboolean is_first, is_last;
412     
413     switch (response_id) {
414         
415         case MODEST_WIZARD_DIALOG_PREVIOUS:
416             gtk_notebook_prev_page (notebook); /* go to previous page */
417             break;
418
419         case MODEST_WIZARD_DIALOG_NEXT:
420                 if (invoke_before_next_vfunc (wizard_dialog))
421                 gtk_notebook_next_page (notebook); /* go to next page */
422                 
423             break;
424
425         case MODEST_WIZARD_DIALOG_CANCEL:
426                 return;
427                 break;      
428         case MODEST_WIZARD_DIALOG_FINISH:
429                 if (invoke_before_next_vfunc (wizard_dialog))
430                 return;
431             
432             break;
433
434     }
435
436     current = gtk_notebook_get_current_page (notebook);
437     gint last = gtk_notebook_get_n_pages (notebook) - 1;
438     is_last = current == last;
439     is_first = current == 0;
440     
441     /* If first page, previous and finish are disabled, 
442        if last page, next is disabled */
443     make_buttons_sensitive (wizard_dialog,
444             !is_first /* previous */, !is_first /* finish */, !is_last /* next*/);
445             
446     /* Allow derived classes to disable buttons to prevent navigation,
447      * according to their own validation logic: */
448     invoke_enable_buttons_vfunc (wizard_dialog);
449     
450     /* Don't let the dialog close */
451     g_signal_stop_emission_by_name (wizard_dialog, "response");
452
453     /* We show the default image on first and last pages */
454     last = gtk_notebook_get_n_pages (notebook) - 1;
455     if (current == last || current == 0)
456         gtk_widget_show (GTK_WIDGET(priv->image));
457     else
458         gtk_widget_hide (GTK_WIDGET(priv->image));
459
460     /* New page number may appear in the title, update it */
461     if (priv->autotitle) 
462         create_title (wizard_dialog);
463 }
464
465 /**
466  * modest_wizard_dialog_new:
467  * @parent: a #GtkWindow
468  * @wizard_name: the name of dialog
469  * @notebook: the notebook to be shown on the dialog
470  *
471  * Creates a new #ModestWizardDialog.
472  *
473  * Returns: a new #ModestWizardDialog
474  */
475 GtkWidget*
476 modest_wizard_dialog_new (GtkWindow   *parent,
477                           const char  *wizard_name,
478                           GtkNotebook *notebook)
479 {
480     GtkWidget *widget;
481
482     g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
483
484     widget = GTK_WIDGET (g_object_new
485             (MODEST_TYPE_WIZARD_DIALOG,
486              "wizard-name", wizard_name,
487              "wizard-notebook", notebook, NULL));
488
489     if (parent)
490         gtk_window_set_transient_for (GTK_WINDOW (widget), parent);
491
492     return widget;
493 }
494
495 static gboolean
496 invoke_before_next_vfunc (ModestWizardDialog *wizard_dialog)
497 {
498         ModestWizardDialogClass *klass = MODEST_WIZARD_DIALOG_GET_CLASS (wizard_dialog);
499         
500         /* Call the vfunc, which may be overridden by derived classes: */
501         if (klass->before_next) {
502                 ModestWizardDialogPrivate *priv = MODEST_WIZARD_DIALOG(wizard_dialog)->priv;
503         
504                 gint current_page_num = gtk_notebook_get_current_page (priv->notebook);
505                 
506                 /* Get widgets for the two pages: */
507                 GtkWidget* current_page_widget = gtk_notebook_get_nth_page (priv->notebook, current_page_num);
508                 
509                 GtkWidget* next_page_widget = NULL;
510                 if ((current_page_num + 1) < gtk_notebook_get_n_pages (priv->notebook))
511                         next_page_widget = gtk_notebook_get_nth_page (priv->notebook, current_page_num + 1);
512                 
513                 /* Ask the vfunc implementation whether navigation should be allowed: */
514                 return (*(klass->before_next))(wizard_dialog, current_page_widget, next_page_widget);
515         }
516         
517         /* Allow navigation by default if there is no vfunc implementation: */
518         return TRUE;
519 }
520
521 static void
522 invoke_enable_buttons_vfunc (ModestWizardDialog *wizard_dialog)
523 {
524         ModestWizardDialogClass *klass = MODEST_WIZARD_DIALOG_GET_CLASS (wizard_dialog);
525         
526         /* Call the vfunc, which may be overridden by derived classes: */
527         if (klass->enable_buttons) {
528                 ModestWizardDialogPrivate *priv = MODEST_WIZARD_DIALOG(wizard_dialog)->priv;
529         
530                 gint current_page_num = gtk_notebook_get_current_page (priv->notebook);
531                 
532                 GtkWidget* current_page_widget = gtk_notebook_get_nth_page (priv->notebook, current_page_num);
533                         
534                 (*(klass->enable_buttons))(wizard_dialog, current_page_widget);
535         }
536 }