Add HildonAppMenu::changed signal
[hildon] / hildon / hildon-wizard-dialog.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
7  *   Fixes: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA
23  *
24  */
25
26 /**
27  * SECTION:hildon-wizard-dialog
28  * @short_description: A widget to create a guided installation
29  * process wizard.
30  *
31  * #HildonWizardDialog is a widget to create a guided installation
32  * process. The dialog has three standard buttons, previous, next,
33  * finish, and contains several pages.
34  *
35  * Response buttons are dimmed/undimmed automatically. The notebook
36  * widget provided by users contains the actual wizard pages.
37  * 
38  * Usage of the API is very simple, it has only one function to create it
39  * and the rest of it is handled by developers notebook.
40  * Also, the response is returned, either cancel or finish.
41  * Next and previous buttons are handled by the wizard dialog it self, by
42  * switching the page either forward or backward in the notebook.
43  *
44  * It is possible to determinate whether users can go to the next page
45  * by setting a #HildonWizardDialogPageFunc function with
46  * hildon_wizard_dialog_set_forward_page_func()
47  */
48
49 #ifdef                                          HAVE_CONFIG_H
50 #include                                        <config.h>
51 #endif
52
53 #include                                        <libintl.h>
54
55 #include                                        "hildon-wizard-dialog.h"
56 #include                                        "hildon-defines.h"
57 #include                                        "hildon-wizard-dialog-private.h"
58
59 #define                                         _(String) dgettext("hildon-libs", String)
60
61 static GtkDialogClass*                          parent_class;
62
63 static void 
64 hildon_wizard_dialog_class_init                 (HildonWizardDialogClass *wizard_dialog_class);
65
66 static void 
67 hildon_wizard_dialog_init                       (HildonWizardDialog *wizard_dialog);
68
69 static void 
70 create_title                                    (HildonWizardDialog *wizard_dialog);
71
72 static void 
73 hildon_wizard_dialog_set_property               (GObject *object,
74                                                  guint property_id,
75                                                  const GValue *value,
76                                                  GParamSpec *pspec);
77
78 static void 
79 hildon_wizard_dialog_get_property               (GObject *object,
80                                                  guint property_id,
81                                                  GValue *value,
82                                                  GParamSpec *pspec);
83
84 static void 
85 finalize                                        (GObject *object);
86
87 static void
88 destroy                                         (GtkObject *object);
89
90 static void 
91 response                                        (HildonWizardDialog *wizard, 
92                                                  gint response_id,
93                                                  gpointer unused);
94
95 static void 
96 make_buttons_sensitive                          (HildonWizardDialog *wizard_dialog,
97                                                  gboolean previous,
98                                                  gboolean finish,
99                                                  gboolean next);
100
101 enum 
102 {
103     PROP_0,
104     PROP_NAME,
105     PROP_NOTEBOOK,
106     PROP_AUTOTITLE
107 };
108
109 /**
110  * hildon_wizard_dialog_get_type:
111  *
112  * Initializes and returns the type of a hildon wizard dialog.
113  *
114  * Returns: GType of #HildonWzardDialog
115  */
116 GType G_GNUC_CONST
117 hildon_wizard_dialog_get_type                   (void)
118 {
119     static GType wizard_dialog_type = 0;
120
121     if (! wizard_dialog_type) {
122
123         static const GTypeInfo wizard_dialog_info = {
124             sizeof (HildonWizardDialogClass),
125             NULL,       /* base_init      */
126             NULL,       /* base_finalize  */
127             (GClassInitFunc) hildon_wizard_dialog_class_init,
128             NULL,       /* class_finalize */
129             NULL,       /* class_data     */
130             sizeof (HildonWizardDialog),
131             0,          /* n_preallocs    */
132             (GInstanceInitFunc) hildon_wizard_dialog_init,
133         };
134
135         wizard_dialog_type = g_type_register_static (GTK_TYPE_DIALOG,
136                 "HildonWizardDialog",
137                 &wizard_dialog_info,
138                 0);
139     }
140
141     return wizard_dialog_type;
142 }
143
144 static void
145 hildon_wizard_dialog_class_init                 (HildonWizardDialogClass *wizard_dialog_class)
146 {
147     GObjectClass *object_class = G_OBJECT_CLASS (wizard_dialog_class);
148     GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (wizard_dialog_class);
149     parent_class = g_type_class_peek_parent (wizard_dialog_class);
150
151     g_type_class_add_private (wizard_dialog_class, sizeof (HildonWizardDialogPrivate));
152
153     /* Override virtual methods */
154     object_class->set_property = hildon_wizard_dialog_set_property;
155     object_class->get_property = hildon_wizard_dialog_get_property;
156     object_class->finalize     = finalize;
157     gtk_object_class->destroy  = destroy;
158
159     /**
160      * HildonWizardDialog:wizard-name:
161      *
162      * The name of the wizard.
163      */
164     g_object_class_install_property (object_class, PROP_NAME,
165             g_param_spec_string 
166             ("wizard-name",
167              "Wizard Name",
168              "The name of the HildonWizardDialog",
169              NULL,
170              G_PARAM_READWRITE));
171
172     /**
173      * HildonWizardDialog:wizard-notebook:
174      *
175      * The notebook object, which is used by the HildonWizardDialog.
176      */
177     g_object_class_install_property (object_class, PROP_NOTEBOOK,
178             g_param_spec_object 
179             ("wizard-notebook",
180              "Wizard Notebook",
181              "GtkNotebook object to be used in the "
182              "HildonWizardDialog",
183              GTK_TYPE_NOTEBOOK, G_PARAM_READWRITE));
184
185     /**
186      * HildonWizardDialog:autotitle
187      *
188      * If the wizard should automatically try to change the window title when changing steps. 
189      * Set to FALSE if you'd like to override the default behaviour. 
190      *
191      * Since: 0.14.5 
192      */
193     g_object_class_install_property (object_class, PROP_AUTOTITLE,
194             g_param_spec_boolean 
195             ("autotitle",
196              "AutoTitle",
197              "If the wizard should autotitle itself",
198              TRUE, 
199              G_PARAM_READWRITE));
200 }
201
202 static void 
203 finalize                                        (GObject *object)
204 {
205     HildonWizardDialogPrivate *priv = HILDON_WIZARD_DIALOG_GET_PRIVATE (object);
206
207     g_assert (priv);
208
209     if (priv->wizard_name != NULL)
210         g_free (priv->wizard_name);
211
212     if (G_OBJECT_CLASS (parent_class)->finalize)
213         G_OBJECT_CLASS (parent_class)->finalize (object);
214 }
215
216 static void
217 destroy                                         (GtkObject *object)
218 {
219     HildonWizardDialogPrivate *priv = HILDON_WIZARD_DIALOG_GET_PRIVATE (object);
220
221     g_assert (priv);
222
223     if (priv->forward_function)
224     {
225       if (priv->forward_function_data &&
226           priv->forward_data_destroy)
227         priv->forward_data_destroy (priv->forward_function_data);
228
229       priv->forward_function = NULL;
230       priv->forward_function_data = NULL;
231       priv->forward_data_destroy = NULL;
232     }
233 }
234
235 /* Disable or enable the Previous, Next and Finish buttons */
236 static void
237 make_buttons_sensitive                          (HildonWizardDialog *wizard_dialog,
238                                                  gboolean previous,
239                                                  gboolean finish,
240                                                  gboolean next)
241 {
242     gtk_dialog_set_response_sensitive (GTK_DIALOG (wizard_dialog),
243             HILDON_WIZARD_DIALOG_PREVIOUS,
244             previous);
245
246     gtk_dialog_set_response_sensitive (GTK_DIALOG (wizard_dialog),
247             HILDON_WIZARD_DIALOG_FINISH,
248             finish);
249
250     gtk_dialog_set_response_sensitive (GTK_DIALOG (wizard_dialog),
251             HILDON_WIZARD_DIALOG_NEXT,
252             next);
253 }
254
255 static void 
256 hildon_wizard_dialog_init                       (HildonWizardDialog *wizard_dialog)
257 {
258     /* Initialize private structure for faster member access */
259     HildonWizardDialogPrivate *priv = HILDON_WIZARD_DIALOG_GET_PRIVATE (wizard_dialog);
260     g_assert (priv);
261
262     GtkDialog *dialog = GTK_DIALOG (wizard_dialog);
263
264     /* Init internal widgets */
265     gtk_dialog_set_has_separator (dialog, FALSE);
266
267     /* Default values for user provided properties */
268     priv->notebook = NULL;
269     priv->wizard_name = NULL;
270     priv->autotitle = TRUE;
271
272     priv->forward_function = NULL;
273     priv->forward_function_data = NULL;
274     priv->forward_data_destroy = NULL;
275
276     /* Add response buttons: finish, previous, next */
277     gtk_dialog_add_button (dialog, _("wdgt_bd_finish"), HILDON_WIZARD_DIALOG_FINISH);
278     gtk_dialog_add_button (dialog, _("wdgt_bd_previous"), HILDON_WIZARD_DIALOG_PREVIOUS);
279     gtk_dialog_add_button (dialog, _("wdgt_bd_next"), HILDON_WIZARD_DIALOG_NEXT);
280
281     /* Set initial button states: previous and finish buttons are disabled */
282     make_buttons_sensitive (wizard_dialog, FALSE, FALSE, TRUE);
283
284     /* Show all the internal widgets */
285     gtk_widget_show_all (GTK_WIDGET (dialog->vbox));
286
287     /* connect to dialog's response signal */
288     g_signal_connect (G_OBJECT (dialog), "response",
289             G_CALLBACK (response), NULL);
290 }
291
292 static void
293 hildon_wizard_dialog_set_property               (GObject *object, 
294                                                  guint property_id,
295                                                  const GValue *value, 
296                                                  GParamSpec *pspec)
297 {
298     HildonWizardDialogPrivate *priv = HILDON_WIZARD_DIALOG_GET_PRIVATE (object);
299     GtkDialog *dialog = GTK_DIALOG (object);
300
301     g_assert (priv);
302
303     switch (property_id) {
304
305         case PROP_AUTOTITLE:
306
307             priv->autotitle = g_value_get_boolean (value);
308
309             if (priv->autotitle && 
310                     priv->wizard_name && 
311                     priv->notebook) 
312                 create_title (HILDON_WIZARD_DIALOG (object));
313             else if (priv->wizard_name)
314                 gtk_window_set_title (GTK_WINDOW (object), priv->wizard_name);
315             break;
316
317         case PROP_NAME: 
318
319             /* Set new wizard name. This name will appear in titlebar */
320             if (priv->wizard_name)
321                 g_free (priv->wizard_name);
322
323             gchar *str = (gchar *) g_value_get_string (value);
324             g_return_if_fail (str != NULL);
325
326             priv->wizard_name = g_strdup (str);
327
328             /* We need notebook in order to create title, since page information
329                is used in title generation */
330
331             if (priv->notebook && priv->autotitle)
332                 create_title (HILDON_WIZARD_DIALOG (object));
333             break;
334
335         case PROP_NOTEBOOK: {
336
337             GtkNotebook *book = GTK_NOTEBOOK (g_value_get_object (value));
338             g_return_if_fail (book != NULL);
339
340             priv->notebook = book;
341
342             /* Set the default properties for the notebook (disable tabs,
343              * and remove borders) to make it look like a nice wizard widget */
344             gtk_notebook_set_show_tabs (priv->notebook, FALSE);
345             gtk_notebook_set_show_border (priv->notebook, FALSE);
346             gtk_box_pack_start_defaults (GTK_BOX (dialog->vbox), GTK_WIDGET (priv->notebook));
347
348             /* Show the notebook so that a gtk_widget_show on the dialog is
349              * all that is required to display the dialog correctly */
350             gtk_widget_show (GTK_WIDGET (priv->notebook));
351
352             /* Update dialog title to reflect current page stats etc */        
353             if (priv->wizard_name && priv->autotitle)
354                 create_title (HILDON_WIZARD_DIALOG (object));
355
356         } break;
357
358         default:
359             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
360             break;
361     }
362 }
363
364 static void
365 hildon_wizard_dialog_get_property               (GObject *object,
366                                                  guint property_id,
367                                                  GValue *value,
368                                                  GParamSpec *pspec)
369 {
370     HildonWizardDialogPrivate *priv = HILDON_WIZARD_DIALOG_GET_PRIVATE (object);
371     g_assert (priv);
372
373     switch (property_id) {
374
375         case PROP_NAME:
376             g_value_set_string (value, priv->wizard_name);
377             break;
378
379         case PROP_NOTEBOOK:
380             g_value_set_object (value, priv->notebook);
381             break;
382
383         default:
384             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
385             break;
386     }
387 }
388
389 /* Creates the title of the dialog taking into account the current 
390  * page of the notebook. */
391 static void
392 create_title                                    (HildonWizardDialog *wizard_dialog)
393 {
394     gint pages, current;
395     gchar *str = NULL;
396     HildonWizardDialogPrivate *priv = HILDON_WIZARD_DIALOG_GET_PRIVATE (wizard_dialog);
397     GtkNotebook *notebook;
398
399     g_assert (priv);
400     notebook = priv->notebook;
401
402     if (! notebook)
403         return;
404
405     /* Get page information, we'll need that when creating title */
406     pages = gtk_notebook_get_n_pages (notebook);
407     current = gtk_notebook_get_current_page (priv->notebook);
408     if (current < 0)
409         current = 0;
410
411     /* the welcome title on the initial page */
412     if (current == 0) {
413         str = g_strdup_printf (_("ecdg_ti_wizard_welcome"),
414                 priv->wizard_name);
415     } else {
416         const gchar *steps = gtk_notebook_get_tab_label_text (notebook,
417                 gtk_notebook_get_nth_page (notebook, current));
418
419         if (steps) {
420           str = g_strdup_printf ("%s%s %s", priv->wizard_name, _("ecdg_ti_caption_separator"), steps);
421         } else {
422           str = g_strdup (priv->wizard_name);
423         }
424     }
425
426     /* Update the dialog to display the generated title */
427     gtk_window_set_title (GTK_WINDOW (wizard_dialog), str);
428     g_free (str);
429 }
430
431 /* Response signal handler. This function is needed because GtkDialog's 
432  * handler for this signal closes the dialog and we don't want that, we 
433  * want to change pages and, dimm certain response buttons. Overriding the 
434  * virtual function would not work because that would be called after the 
435  * signal handler implemented by GtkDialog.
436  * FIXME: There is a much saner way to do that [MDK] */
437 static void 
438 response                                        (HildonWizardDialog *wizard_dialog,
439                                                  gint response_id,
440                                                  gpointer unused)
441 {
442     HildonWizardDialogPrivate *priv = HILDON_WIZARD_DIALOG_GET_PRIVATE (wizard_dialog);
443     GtkNotebook *notebook = priv->notebook;
444     gint current = 0;
445     gint last = gtk_notebook_get_n_pages (notebook) - 1;
446     gboolean is_first, is_last;
447
448     g_assert (priv);
449
450     current = gtk_notebook_current_page (notebook);
451
452     switch (response_id) {
453
454         case HILDON_WIZARD_DIALOG_PREVIOUS:
455             --current;
456             is_last = (current == last);
457             is_first = (current == 0);
458             make_buttons_sensitive (wizard_dialog,
459                                     !is_first, !is_first, !is_last); 
460             gtk_notebook_prev_page (notebook); /* go to previous page */
461             break;
462
463         case HILDON_WIZARD_DIALOG_NEXT:
464
465           if (!priv->forward_function ||
466                 (*priv->forward_function) (priv->notebook, current, priv->forward_function_data)) {
467               ++current;
468               is_last = (current == last);
469               is_first = (current == 0);
470               make_buttons_sensitive (wizard_dialog,
471                                       !is_first, !is_first, !is_last);
472               gtk_notebook_next_page (notebook); /* go to next page */
473             }
474             break;
475
476         case HILDON_WIZARD_DIALOG_FINISH:
477             return;
478
479     }
480
481     current = gtk_notebook_get_current_page (notebook);
482     is_last = current == last;
483     is_first = current == 0;
484
485     /* Don't let the dialog close */
486     g_signal_stop_emission_by_name (wizard_dialog, "response");
487
488     /* New page number may appear in the title, update it */
489     if (priv->autotitle) 
490         create_title (wizard_dialog);
491 }
492
493 /**
494  * hildon_wizard_dialog_new:
495  * @parent: a #GtkWindow
496  * @wizard_name: the name of dialog
497  * @notebook: the notebook to be shown on the dialog
498  *
499  * Creates a new #HildonWizardDialog.
500  *
501  * Returns: a new #HildonWizardDialog
502  */
503 GtkWidget*
504 hildon_wizard_dialog_new                        (GtkWindow *parent,
505                                                  const char *wizard_name,
506                                                  GtkNotebook *notebook)
507 {
508     GtkWidget *widget;
509
510     g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
511
512     widget = GTK_WIDGET (g_object_new
513             (HILDON_TYPE_WIZARD_DIALOG,
514              "wizard-name", wizard_name,
515              "wizard-notebook", notebook, NULL));
516
517     if (parent)
518         gtk_window_set_transient_for (GTK_WINDOW (widget), parent);
519
520     return widget;
521 }
522
523 /**
524  * hildon_wizard_dialog_set_forward_page_func:
525  * @wizard_dialog: a #HildonWizardDialog
526  * @page_func: the #HildonWizardDialogPageFunc
527  * @data: user data for @page_func
528  * @destroy: destroy notifier for @data
529  *
530  * Sets the page forwarding function to be @page_func. This function
531  * will be used to determine whether it is possible to go to the next page
532  * when the user presses the forward button. Setting @page_func to %NULL
533  * wil make the wizard to simply go always to the next page.
534  *
535  * Since: 2.2
536  **/
537 void
538 hildon_wizard_dialog_set_forward_page_func      (HildonWizardDialog *wizard_dialog,
539                                                  HildonWizardDialogPageFunc page_func,
540                                                  gpointer data,
541                                                  GDestroyNotify destroy)
542 {
543   g_return_if_fail (HILDON_IS_WIZARD_DIALOG (wizard_dialog));
544
545   HildonWizardDialogPrivate *priv = HILDON_WIZARD_DIALOG_GET_PRIVATE (wizard_dialog);
546
547   if (priv->forward_data_destroy &&
548       priv->forward_function_data) {
549     (*priv->forward_data_destroy) (priv->forward_function_data);
550   }
551
552   priv->forward_function = page_func;
553   priv->forward_function_data = data;
554   priv->forward_data_destroy = destroy;
555 }