List bug number.
[hildon] / hildon-widgets / hildon-wizard-dialog.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2005 Nokia Corporation.
5  *
6  * Contact: Luc Pionchon <luc.pionchon@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-wizard-dialog
27  * @short_description: A widget to create a guided installation
28  * process wizard
29  *
30  * #HildonWizardDialog is a widget to create a guided installation
31  * process. The dialog has four standard buttons, previous, next,
32  * finish, cancel, and contains several pages with optional icons.
33  * Response buttons are dimmed/undimmed automatically and the standard
34  * icon is shown/hidden in response to page navigation. The notebook
35  * widget provided by users contains the actual wizard pages.
36  */
37
38 #include <gtk/gtkdialog.h>
39 #include <gtk/gtknotebook.h>
40 #include <gtk/gtkimage.h>
41 #include <gtk/gtkbox.h>
42 #include <gtk/gtkhbox.h>
43 #include <gtk/gtkvbox.h>
44 #include <gtk/gtkbutton.h>
45 #include <hildon-widgets/hildon-defines.h>
46
47 #include "hildon-wizard-dialog.h"
48
49 #include <libintl.h>
50
51 #ifdef HAVE_CONFIG_H
52 #include <config.h>
53 #endif
54
55 #define _(String) dgettext(PACKAGE, String)
56
57 static GtkDialogClass *parent_class;
58
59 static void hildon_wizard_dialog_class_init  (HildonWizardDialogClass * wizard_dialog_class);
60 static void hildon_wizard_dialog_init        (HildonWizardDialog      * wizard_dialog);
61 static void hildon_wizard_dialog_create_title(HildonWizardDialog      * wizard_dialog);
62
63 static void hildon_wizard_dialog_set_property(GObject      * object,
64                                               guint          property_id,
65                                               const GValue * value,
66                                               GParamSpec   * pspec);
67
68 static void hildon_wizard_dialog_get_property(GObject      * object,
69                                               guint          property_id,
70                                               GValue       * value,
71                                               GParamSpec   * pspec);
72
73 static void hildon_wizard_dialog_finalize    (GObject      * object);
74
75 static void hildon_wizard_dialog_response    (HildonWizardDialog * wizard, 
76                                               gint                 response_id,
77                                               gpointer             unused);
78
79 enum {
80     PROP_WIZARD_NAME = 1,
81     PROP_WIZARD_NOTEBOOK
82 };
83
84 struct _HildonWizardDialogPrivate {
85     gchar       *wizard_name;
86     GtkNotebook *notebook;
87     GtkBox      *box;
88     GtkWidget   *image;
89 };
90
91
92 GType hildon_wizard_dialog_get_type(void)
93 {
94     static GType wizard_dialog_type = 0;
95
96     if (!wizard_dialog_type) {
97
98         static const GTypeInfo wizard_dialog_info = {
99             sizeof(HildonWizardDialogClass),
100             NULL,       /* base_init      */
101             NULL,       /* base_finalize  */
102             (GClassInitFunc) hildon_wizard_dialog_class_init,
103             NULL,       /* class_finalize */
104             NULL,       /* class_data     */
105             sizeof(HildonWizardDialog),
106             0,          /* n_preallocs    */
107             (GInstanceInitFunc) hildon_wizard_dialog_init,
108         };
109
110         wizard_dialog_type = g_type_register_static(GTK_TYPE_DIALOG,
111                                                     "HildonWizardDialog",
112                                                     &wizard_dialog_info,
113                                                     0);
114     }
115     return wizard_dialog_type;
116 }
117
118
119 static void
120 hildon_wizard_dialog_class_init(HildonWizardDialogClass * wizard_dialog_class)
121 {
122     GObjectClass *object_class = G_OBJECT_CLASS(wizard_dialog_class);
123
124     parent_class = g_type_class_peek_parent(wizard_dialog_class);
125
126     g_type_class_add_private(wizard_dialog_class,
127                              sizeof(HildonWizardDialogPrivate));
128
129     /* Override virtual methods */
130     object_class->set_property = hildon_wizard_dialog_set_property;
131     object_class->get_property = hildon_wizard_dialog_get_property;
132     object_class->finalize     = hildon_wizard_dialog_finalize;
133
134     /**
135      * HildonWizardDialog:wizard-name:
136      *
137      * The name of the wizard.
138      */
139     g_object_class_install_property(object_class, PROP_WIZARD_NAME,
140         g_param_spec_string("wizard-name",
141                             "Wizard Name",
142                             "The name of the HildonWizardDialog",
143                             NULL,
144                             G_PARAM_READWRITE));
145
146     /**
147      * HildonWizardDialog:wizard-notebook:
148      *
149      * The notebook object, which is used by the HildonWizardDialog.
150      */
151     g_object_class_install_property(object_class, PROP_WIZARD_NOTEBOOK,
152         g_param_spec_object("wizard-notebook",
153                             "Wizard Notebook",
154                             "GtkNotebook object to be used in the "
155                               "HildonWizardDialog",
156                             GTK_TYPE_NOTEBOOK, G_PARAM_READWRITE));
157 }
158
159 static void hildon_wizard_dialog_finalize(GObject * object)
160 {
161     g_free(HILDON_WIZARD_DIALOG(object)->priv->wizard_name);
162     if (G_OBJECT_CLASS(parent_class)->finalize)
163         G_OBJECT_CLASS(parent_class)->finalize(object);
164 }
165
166 /* Disable/enable the Previous, Next and Finish buttons */
167 static void
168 hildon_wizard_dialog_buttons_sensitive(HildonWizardDialog * wizard_dialog,
169                                        gboolean previous,
170                                        gboolean finish,
171                                        gboolean next)
172 {
173     gtk_dialog_set_response_sensitive(GTK_DIALOG(wizard_dialog),
174                                       HILDON_WIZARD_DIALOG_PREVIOUS,
175                                       previous);
176
177     gtk_dialog_set_response_sensitive(GTK_DIALOG(wizard_dialog),
178                                       HILDON_WIZARD_DIALOG_FINISH,
179                                       finish);
180
181     gtk_dialog_set_response_sensitive(GTK_DIALOG(wizard_dialog),
182                                       HILDON_WIZARD_DIALOG_NEXT,
183                                       next);
184 }
185
186 static void hildon_wizard_dialog_init(HildonWizardDialog * wizard_dialog)
187 {
188     /* Initialize private structure for faster member access */
189     HildonWizardDialogPrivate *priv =
190         G_TYPE_INSTANCE_GET_PRIVATE(wizard_dialog,
191                                     HILDON_TYPE_WIZARD_DIALOG,
192                                     HildonWizardDialogPrivate);
193     GtkDialog *dialog = GTK_DIALOG(wizard_dialog);
194
195     /* Init internal widgets */
196     GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
197     gtk_dialog_set_has_separator(dialog, FALSE);
198     wizard_dialog->priv = priv;
199     priv->box = GTK_BOX(gtk_hbox_new(FALSE, 0));
200     priv->image = gtk_image_new_from_icon_name("qgn_widg_wizard",
201                                                HILDON_ICON_SIZE_WIDG_WIZARD);
202
203     /* Default values for user provided properties */
204     priv->notebook = NULL;
205     priv->wizard_name = NULL;
206
207     /* Build wizard layout */
208     gtk_box_pack_start_defaults(GTK_BOX(dialog->vbox), GTK_WIDGET(priv->box));
209     gtk_box_pack_start_defaults(GTK_BOX(priv->box),    GTK_WIDGET(vbox));
210     gtk_box_pack_start(GTK_BOX(vbox),GTK_WIDGET(priv->image), FALSE, FALSE, 0);
211
212     /* Add response buttons: cancel, previous, next, finish */
213     gtk_dialog_add_button(dialog, _("ecdg_bd_wizard_cancel"),   HILDON_WIZARD_DIALOG_CANCEL);
214     gtk_dialog_add_button(dialog, _("ecdg_bd_wizard_previous"), HILDON_WIZARD_DIALOG_PREVIOUS);
215     gtk_dialog_add_button(dialog, _("ecdg_bd_wizard_next"),     HILDON_WIZARD_DIALOG_NEXT);
216     gtk_dialog_add_button(dialog, _("ecdg_bd_wizard_finish"),   HILDON_WIZARD_DIALOG_FINISH);
217
218     /* Set initial button states: previous and finish buttons are disabled */
219     hildon_wizard_dialog_buttons_sensitive(wizard_dialog, FALSE, FALSE, TRUE);
220
221     /* connect to dialog's response signal */
222     g_signal_connect(G_OBJECT(dialog), "response",
223                      G_CALLBACK(hildon_wizard_dialog_response), NULL);
224 }
225
226
227 static void
228 hildon_wizard_dialog_set_property(GObject * object, guint property_id,
229                                   const GValue * value, GParamSpec * pspec)
230 {
231     HildonWizardDialogPrivate *priv = HILDON_WIZARD_DIALOG(object)->priv;
232
233     switch (property_id) {
234     case PROP_WIZARD_NAME:
235
236         /* Set new wizard name. This name will appear in titlebar */
237         if (priv->wizard_name)
238             g_free(priv->wizard_name);
239         priv->wizard_name = g_strdup((gchar *) g_value_get_string(value));
240
241         /* We need notebook in order to create title, since page information
242            is used in title generation */
243         if (priv->notebook)
244             hildon_wizard_dialog_create_title(HILDON_WIZARD_DIALOG(object));
245         break;
246     case PROP_WIZARD_NOTEBOOK:
247         priv->notebook = GTK_NOTEBOOK(g_value_get_object(value));
248
249         /* Set the default properties for the notebook (disable tabs,
250          * and remove borders) to make it look like a nice wizard widget */
251         gtk_notebook_set_show_tabs   (priv->notebook, FALSE);
252         gtk_notebook_set_show_border (priv->notebook, FALSE);
253         gtk_box_pack_start_defaults  (GTK_BOX(priv->box), GTK_WIDGET(priv->notebook));
254
255         /* Update dialog title to reflect current page stats etc */        
256         if (priv->wizard_name)
257             hildon_wizard_dialog_create_title(HILDON_WIZARD_DIALOG
258                                               (object));
259         break;
260     default:
261         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
262         break;
263     }
264 }
265
266 /*
267  * Creates the title of the dialog taking into account the current 
268  * page of the notebook.
269  */
270 static void
271 hildon_wizard_dialog_get_property(GObject    * object,
272                                   guint        property_id,
273                                   GValue     * value,
274                                   GParamSpec * pspec)
275 {
276     HildonWizardDialogPrivate *priv = HILDON_WIZARD_DIALOG(object)->priv;
277
278     switch (property_id) {
279     case PROP_WIZARD_NAME:
280         g_value_set_string(value, priv->wizard_name);
281         break;
282     case PROP_WIZARD_NOTEBOOK:
283         g_value_set_object(value, priv->notebook);
284         break;
285     default:
286         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
287         break;
288     }
289 }
290
291 /*
292  * Creates the title of the dialog taking into account the current 
293  * page of the notebook.
294  */
295 static void
296 hildon_wizard_dialog_create_title(HildonWizardDialog * wizard_dialog)
297 {
298     gint pages, current;
299     gchar *str = NULL;
300     HildonWizardDialogPrivate *priv = wizard_dialog->priv;
301     GtkNotebook *notebook = priv->notebook;
302
303     if (!notebook)
304         return;
305
306     /* Get page information, we'll need that when creating title */
307     pages = gtk_notebook_get_n_pages(notebook);
308     current = gtk_notebook_get_current_page(priv->notebook);
309     if (current < 0)
310         current = 0;
311
312     /* the welcome title on the initial page */
313     if (current == 0) {
314         str = g_strdup_printf(_("ecdg_ti_wizard_welcome"), 
315                               priv->wizard_name, pages);
316     } else {
317         const gchar *steps = gtk_notebook_get_tab_label_text(notebook,
318             gtk_notebook_get_nth_page(notebook, current));
319         str = g_strdup_printf(_("ecdg_ti_wizard_step"), 
320                               priv->wizard_name, current + 1, pages, steps);
321     }
322
323     /* Update the dialog to display the generated title */
324     gtk_window_set_title(GTK_WINDOW(wizard_dialog), str);
325     g_free(str);
326 }
327
328 /*
329  * Response signal handler. This function is needed because GtkDialog's 
330  * handler for this signal closes the dialog and we don't want that, we 
331  * want to change pages and, dimm certain response buttons. Overriding the 
332  * virtual function would not work because that would be called after the 
333  * signal handler implemented by GtkDialog.
334  */
335
336 static void hildon_wizard_dialog_response(HildonWizardDialog *wizard_dialog,
337                                           gint                response_id,
338                                           gpointer            unused)
339 {
340     HildonWizardDialogPrivate *priv = wizard_dialog->priv;
341     GtkNotebook *notebook = priv->notebook;
342     gint current = 0;
343     gint last = gtk_notebook_get_n_pages(notebook) - 1;
344     gboolean is_first, is_last;
345     
346     switch (response_id)
347     {
348         case HILDON_WIZARD_DIALOG_PREVIOUS:
349             gtk_notebook_prev_page(notebook); /* go to previous page */
350             break;
351         case HILDON_WIZARD_DIALOG_NEXT:
352             gtk_notebook_next_page(notebook); /* go to next page */
353             break;
354         case HILDON_WIZARD_DIALOG_CANCEL:      
355         case HILDON_WIZARD_DIALOG_FINISH:      
356             return;
357     }
358
359     current = gtk_notebook_get_current_page(notebook);
360     is_last = current == last;
361     is_first = current == 0;
362     
363     /* If first page, previous and finish are disabled, 
364        if last page, next is disabled */
365     hildon_wizard_dialog_buttons_sensitive(wizard_dialog,
366                                            !is_first, !is_first, !is_last);
367     
368     /* Don't let the dialog close */
369     g_signal_stop_emission_by_name(wizard_dialog, "response");
370
371     /* We show the default image on first and last pages */
372     if (current == last || current == 0)
373         gtk_widget_show(GTK_WIDGET(priv->image));
374     else
375         gtk_widget_hide(GTK_WIDGET(priv->image));
376
377     /* New page number may appear in the title, update it */
378     hildon_wizard_dialog_create_title(wizard_dialog);
379 }
380
381
382 /**
383  * hildon_wizard_dialog_new:
384  * @parent: a #GtkWindow
385  * @wizard_name: the name of dialog
386  * @notebook: the notebook to be shown on the dialog
387  *
388  * Creates a new #HildonWizardDialog.
389  *
390  * Returns: a new #HildonWizardDialog
391  */
392 GtkWidget *hildon_wizard_dialog_new(GtkWindow   * parent,
393                                     const char  * wizard_name,
394                                     GtkNotebook * notebook)
395 {
396     GtkWidget *widget;
397
398     g_return_val_if_fail(GTK_IS_NOTEBOOK(notebook), NULL);
399
400     widget = GTK_WIDGET(g_object_new(HILDON_TYPE_WIZARD_DIALOG,
401                                      "wizard-name", wizard_name,
402                                      "wizard-notebook", notebook, NULL));
403     if (parent)
404         gtk_window_set_transient_for(GTK_WINDOW(widget), parent);
405
406     return widget;
407 }