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