Add shortcut support to app menu util method (fixes NB#97702).
[modest] / src / hildon2 / modest-hildon2-window.c
1 /* Copyright (c) 2008, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <hildon/hildon-banner.h>
31 #include <modest-platform.h>
32 #include <hildon/hildon-program.h>
33 #include <modest-maemo-utils.h>
34 #include <modest-defs.h>
35 #include <modest-ui-dimming-rules.h>
36 #include <modest-ui-dimming-manager.h>
37 #include <modest-window-priv.h>
38 #include <modest-hildon2-window.h>
39 #include <modest-ui-actions.h>
40 #include <hildon/hildon-edit-toolbar.h>
41
42 typedef struct _EditModeRegister {
43         gchar *description;
44         gchar *button_label;
45         GtkWidget *tree_view;
46         GtkSelectionMode mode;
47         ModestHildon2EditModeCallback action;
48 } EditModeRegister;
49
50 /* 'private'/'protected' functions */
51 static void modest_hildon2_window_class_init  (gpointer klass, gpointer class_data);
52 static void modest_hildon2_window_instance_init (GTypeInstance *instance, gpointer g_class);
53 static void modest_hildon2_window_finalize    (GObject *obj);
54
55 static gboolean on_zoom_minus_plus_not_implemented (ModestWindow *window);
56 static void setup_menu (ModestHildon2Window *self);
57
58 static void modest_hildon2_window_show_toolbar (ModestWindow *self,
59                                                  gboolean show_toolbar);
60 static gboolean modest_hildon2_window_toggle_menu (HildonWindow *window,
61                                                     guint button,
62                                                     guint32 time);
63 static void modest_hildon2_window_pack_toolbar_not_implemented (ModestHildon2Window *self,
64                                                                 GtkPackType pack_type,
65                                                                 GtkWidget *toolbar);
66 static EditModeRegister *edit_mode_register_new (const gchar *description,
67                                                  const gchar *button_label,
68                                                  GtkTreeView *tree_view,
69                                                  GtkSelectionMode mode,
70                                                  ModestHildon2EditModeCallback action);
71 static void edit_mode_register_destroy (gpointer data);
72 static void edit_toolbar_button_clicked (HildonEditToolbar *toolbar,
73                                          ModestHildon2Window *self);
74 static void edit_toolbar_arrow_clicked (HildonEditToolbar *toolbar,
75                                         ModestHildon2Window *self);
76
77 typedef struct _ModestHildon2WindowPrivate ModestHildon2WindowPrivate;
78 struct _ModestHildon2WindowPrivate {
79
80         GtkWidget *app_menu;
81         ModestDimmingRulesGroup *app_menu_dimming_group;
82         GtkAccelGroup *accel_group;
83
84         /* Edit mode support */
85         gboolean edit_mode;
86         gint edit_command;
87         GtkWidget *edit_toolbar;
88         GtkWidget *current_edit_tree_view;
89         GHashTable *edit_mode_registry;
90 };
91 #define MODEST_HILDON2_WINDOW_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE((o), \
92                                                                             MODEST_TYPE_HILDON2_WINDOW, \
93                                                                             ModestHildon2WindowPrivate))
94
95 /* globals */
96 static GtkWindowClass *parent_class = NULL;
97
98 /************************************************************************/
99
100 GType
101 modest_hildon2_window_get_type (void)
102 {
103         static GType my_type = 0;
104         if (!my_type) {
105                 static const GTypeInfo my_info = {
106                         sizeof(ModestHildon2WindowClass),
107                         NULL,           /* base init */
108                         NULL,           /* base finalize */
109                         (GClassInitFunc) modest_hildon2_window_class_init,
110                         NULL,           /* class finalize */
111                         NULL,           /* class data */
112                         sizeof(ModestHildon2Window),
113                         1,              /* n_preallocs */
114                         (GInstanceInitFunc) modest_hildon2_window_instance_init,
115                         NULL
116                 };
117                 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
118                                                   "ModestHildon2Window",
119                                                   &my_info, 0);
120         }
121         return my_type;
122 }
123
124 static void
125 modest_hildon2_window_class_init (gpointer klass, gpointer class_data)
126 {
127         GObjectClass *gobject_class;
128         gobject_class = (GObjectClass*) klass;
129         ModestWindowClass *modest_window_class = (ModestWindowClass *) klass;
130         HildonWindowClass *hildon_window_class = (HildonWindowClass *) klass;
131         ModestHildon2WindowClass *modest_hildon2_window_class = (ModestHildon2WindowClass *) klass;
132
133         parent_class            = g_type_class_peek_parent (klass);
134         gobject_class->finalize = modest_hildon2_window_finalize;
135
136         g_type_class_add_private (gobject_class, sizeof(ModestHildon2WindowPrivate));
137         
138         hildon_window_class->toggle_menu = modest_hildon2_window_toggle_menu;
139
140         modest_window_class->zoom_minus_func = on_zoom_minus_plus_not_implemented;
141         modest_window_class->zoom_plus_func = on_zoom_minus_plus_not_implemented;
142         modest_window_class->show_toolbar_func = modest_hildon2_window_show_toolbar;
143
144         modest_hildon2_window_class->pack_toolbar_func = modest_hildon2_window_pack_toolbar_not_implemented;
145 }
146
147 static void
148 modest_hildon2_window_finalize (GObject *obj)
149 {
150         ModestHildon2WindowPrivate *priv;
151
152         priv = MODEST_HILDON2_WINDOW_GET_PRIVATE(obj);
153
154         g_object_unref (priv->app_menu_dimming_group);
155         priv->app_menu_dimming_group = NULL;
156
157         g_hash_table_destroy (priv->edit_mode_registry);
158         priv->edit_mode_registry = NULL;
159
160         G_OBJECT_CLASS(parent_class)->finalize (obj);
161 }
162
163 static void
164 modest_hildon2_window_instance_init (GTypeInstance *instance, gpointer g_class)
165 {
166         ModestHildon2Window *self = NULL;       
167         ModestWindowPrivate *parent_priv = NULL;
168         ModestHildon2WindowPrivate *priv = NULL;
169
170         self = (ModestHildon2Window *) instance;
171         parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
172         priv = MODEST_HILDON2_WINDOW_GET_PRIVATE (self);
173
174         priv->accel_group = gtk_accel_group_new ();
175
176         priv->edit_mode = FALSE;
177         priv->edit_toolbar = NULL;
178         priv->current_edit_tree_view = NULL;
179         priv->edit_command = -1;
180         priv->edit_mode_registry = g_hash_table_new_full (g_direct_hash, g_direct_equal,
181                                                           NULL, edit_mode_register_destroy);
182
183         parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
184         priv->app_menu_dimming_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
185         gtk_window_add_accel_group (GTK_WINDOW (self), priv->accel_group);
186
187         setup_menu (self);
188
189         modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, 
190                                                       priv->app_menu_dimming_group);
191
192         /* Dont't restore settings here, 
193          * because it requires a gtk_widget_show(), 
194          * and we don't want to do that until later,
195          * so that the UI is not visible for non-menu D-Bus activation.
196          */
197 }
198
199 static gboolean
200 on_zoom_minus_plus_not_implemented (ModestWindow *window)
201 {
202         g_return_val_if_fail (MODEST_IS_HILDON2_WINDOW (window), FALSE);
203
204         hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_cannot_zoom_here"));
205         return FALSE;
206
207 }
208
209 static void 
210 modest_hildon2_window_pack_toolbar_not_implemented (ModestHildon2Window *self,
211                                                     GtkPackType pack_type,
212                                                     GtkWidget *toolbar)
213 {
214         g_return_if_fail (MODEST_IS_HILDON2_WINDOW (self));
215
216         g_warning ("%s not implemented", __FUNCTION__);
217 }
218
219 void
220 modest_hildon2_window_pack_toolbar (ModestHildon2Window *self,
221                                     GtkPackType pack_type,
222                                     GtkWidget *toolbar)
223 {
224         g_return_if_fail (MODEST_IS_HILDON2_WINDOW (self));
225
226         MODEST_HILDON2_WINDOW_GET_CLASS (self)->pack_toolbar_func (self, pack_type, toolbar);
227 }
228
229 void 
230 modest_hildon2_window_add_button_to_menu (ModestHildon2Window *self,
231                                           GtkButton *button,
232                                           ModestDimmingCallback dimming_callback)
233 {
234         ModestHildon2WindowPrivate *priv;
235
236         g_return_if_fail (MODEST_IS_HILDON2_WINDOW(self));
237         g_return_if_fail (GTK_IS_BUTTON (button));
238         priv = MODEST_HILDON2_WINDOW_GET_PRIVATE (self);
239
240         if (dimming_callback)
241                 modest_dimming_rules_group_add_widget_rule (priv->app_menu_dimming_group,
242                                                             GTK_WIDGET (button),
243                                                             (GCallback) dimming_callback,
244                                                             MODEST_WINDOW (self));
245         hildon_app_menu_append (HILDON_APP_MENU (priv->app_menu), GTK_BUTTON (button));
246 }
247
248 void 
249 modest_hildon2_window_add_to_menu (ModestHildon2Window *self,
250                                    gchar *label,
251                                    const gchar *accelerator,
252                                    ModestHildon2AppMenuCallback callback,
253                                    ModestDimmingCallback dimming_callback)
254 {
255         ModestHildon2WindowPrivate *priv = NULL;
256         GtkWidget *button;
257
258         g_return_if_fail (MODEST_IS_HILDON2_WINDOW(self));
259         g_return_if_fail (label && label[0] != '\0');
260         g_return_if_fail (callback != NULL);
261
262         priv = MODEST_HILDON2_WINDOW_GET_PRIVATE (self);
263
264         button = gtk_button_new_with_label (label);
265         g_signal_connect_after (G_OBJECT (button), "clicked",
266                                 G_CALLBACK (callback), (gpointer) self);
267
268         if (accelerator != NULL) {
269                 guint accel_key;
270                 GdkModifierType accel_mods;
271
272                 gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
273                 gtk_widget_add_accelerator (button, "clicked", priv->accel_group,
274                                             accel_key, accel_mods, 0);
275         }
276
277         modest_hildon2_window_add_button_to_menu (self, GTK_BUTTON (button), dimming_callback);
278 }
279
280 static void setup_menu (ModestHildon2Window *self)
281 {
282         ModestHildon2WindowPrivate *priv = NULL;
283
284         g_return_if_fail (MODEST_IS_HILDON2_WINDOW(self));
285
286         priv = MODEST_HILDON2_WINDOW_GET_PRIVATE (self);
287
288         priv->app_menu = hildon_app_menu_new ();
289
290         /* we expect that the app menu is filled in children using the expected 
291          * add_to_menu methods */
292
293         hildon_stackable_window_set_main_menu (HILDON_STACKABLE_WINDOW (self), 
294                                                HILDON_APP_MENU (priv->app_menu));
295 }
296
297 static gboolean 
298 modest_hildon2_window_toggle_menu (HildonWindow *window,
299                                     guint button,
300                                     guint32 time)
301 {
302         ModestHildon2WindowPrivate *priv = NULL;
303
304         priv = MODEST_HILDON2_WINDOW_GET_PRIVATE (window);
305
306         modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
307
308         gtk_widget_queue_resize (priv->app_menu);
309
310         return HILDON_WINDOW_CLASS (parent_class)->toggle_menu (window, button, time);
311 }
312
313 static void
314 modest_hildon2_window_show_toolbar (ModestWindow *self,
315                                     gboolean show_toolbar)
316 {
317         /* Empty implementation: Hildon 2.2 implementation
318          * doesn't switch toolbar visibility */
319 }
320
321 void 
322 modest_hildon2_window_register_edit_mode (ModestHildon2Window *self,
323                                           gint edit_mode_id,
324                                           const gchar *description,
325                                           const gchar *button_label,
326                                           GtkTreeView *tree_view,
327                                           GtkSelectionMode mode,
328                                           ModestHildon2EditModeCallback action)
329 {
330         ModestHildon2WindowPrivate *priv = NULL;
331         EditModeRegister *reg;
332
333         g_return_if_fail (MODEST_IS_HILDON2_WINDOW (self));
334         g_return_if_fail (edit_mode_id >= 0);
335         g_return_if_fail (description);
336         g_return_if_fail (button_label);
337         g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
338
339         priv = MODEST_HILDON2_WINDOW_GET_PRIVATE (self);
340
341         reg = (EditModeRegister *) g_hash_table_lookup (priv->edit_mode_registry, GINT_TO_POINTER (edit_mode_id));
342         g_return_if_fail (reg == NULL);
343
344         reg = edit_mode_register_new (description, button_label, tree_view, mode, action);
345         g_hash_table_insert (priv->edit_mode_registry, GINT_TO_POINTER (edit_mode_id), (gpointer) reg);
346 }
347
348 void
349 modest_hildon2_window_set_edit_mode (ModestHildon2Window *self,
350                                      gint edit_mode_id)
351 {
352         ModestHildon2WindowPrivate *priv = NULL;
353         EditModeRegister *reg;
354         GtkTreeSelection *selection;
355
356         g_return_if_fail (MODEST_IS_HILDON2_WINDOW (self));
357         g_return_if_fail (edit_mode_id >= 0);
358
359         priv = MODEST_HILDON2_WINDOW_GET_PRIVATE (self);
360         reg = (EditModeRegister *) g_hash_table_lookup (priv->edit_mode_registry, GINT_TO_POINTER (edit_mode_id));
361         g_return_if_fail (reg != NULL);
362
363         if (priv->edit_mode) {
364                 modest_hildon2_window_unset_edit_mode (self);
365         }
366
367         priv->edit_mode = TRUE;
368         priv->edit_command = edit_mode_id;
369
370         priv->current_edit_tree_view = reg->tree_view;
371         g_object_set (G_OBJECT (priv->current_edit_tree_view),
372                       "hildon-ui-mode", HILDON_UI_MODE_EDIT,
373                       NULL);
374
375         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->current_edit_tree_view));
376         gtk_tree_selection_set_mode (selection, reg->mode);
377         gtk_tree_selection_unselect_all (selection);
378
379         priv->edit_toolbar = hildon_edit_toolbar_new ();
380         hildon_edit_toolbar_set_label (HILDON_EDIT_TOOLBAR (priv->edit_toolbar),
381                                        reg->description);
382         hildon_edit_toolbar_set_button_label (HILDON_EDIT_TOOLBAR (priv->edit_toolbar),
383                                               reg->button_label);
384         modest_hildon2_window_pack_toolbar (self, GTK_PACK_START,
385                                             priv->edit_toolbar);
386
387         g_signal_connect (G_OBJECT (priv->edit_toolbar), "button-clicked",
388                           G_CALLBACK (edit_toolbar_button_clicked), (gpointer) self);
389         g_signal_connect (G_OBJECT (priv->edit_toolbar), "arrow-clicked",
390                           G_CALLBACK (edit_toolbar_arrow_clicked), (gpointer) self);
391
392         gtk_widget_show (priv->edit_toolbar);
393         gtk_widget_queue_resize (priv->current_edit_tree_view);
394         gtk_window_fullscreen (GTK_WINDOW (self));
395
396 }
397
398 void 
399 modest_hildon2_window_unset_edit_mode (ModestHildon2Window *self)
400 {
401         ModestHildon2WindowPrivate *priv = NULL;
402
403         g_return_if_fail (MODEST_IS_HILDON2_WINDOW (self));
404         priv = MODEST_HILDON2_WINDOW_GET_PRIVATE (self);
405
406         if (priv->edit_toolbar) {
407                 gtk_widget_destroy (priv->edit_toolbar);
408                 priv->edit_toolbar = NULL;
409         }
410
411         if (priv->edit_mode) {
412                 priv->edit_mode = FALSE;
413                 priv->edit_command = -1;
414                 if (priv->current_edit_tree_view) {
415                         g_object_set (G_OBJECT (priv->current_edit_tree_view), 
416                                       "hildon-ui-mode", HILDON_UI_MODE_NORMAL, 
417                                       NULL);
418                         gtk_widget_queue_resize (priv->current_edit_tree_view);
419                         priv->current_edit_tree_view = NULL;
420                 }
421                 gtk_window_unfullscreen (GTK_WINDOW (self));
422         }
423 }
424
425 static EditModeRegister *
426 edit_mode_register_new (const gchar *description,
427                         const gchar *button_label,
428                         GtkTreeView *tree_view,
429                         GtkSelectionMode mode,
430                         ModestHildon2EditModeCallback action)
431 {
432         EditModeRegister *reg;
433
434         reg = g_slice_new (EditModeRegister);
435
436         reg->description = g_strdup (description);
437         reg->button_label = g_strdup (button_label);
438         reg->tree_view = g_object_ref (tree_view);
439         reg->mode = mode;
440         reg->action = action;
441
442         return reg;
443 }
444
445 static void 
446 edit_mode_register_destroy (gpointer data)
447 {
448         EditModeRegister *reg = (EditModeRegister *) data;
449
450         g_free (reg->description);
451         g_free (reg->button_label);
452         g_object_unref (reg->tree_view);
453
454         g_slice_free (EditModeRegister, reg);
455 }
456
457 static void
458 edit_toolbar_button_clicked (HildonEditToolbar *toolbar,
459                              ModestHildon2Window *self)
460 {
461         ModestHildon2WindowPrivate *priv = MODEST_HILDON2_WINDOW_GET_PRIVATE (self);
462         EditModeRegister *reg;
463
464         g_return_if_fail (MODEST_IS_HILDON2_WINDOW (self));
465         
466         reg = (EditModeRegister *) g_hash_table_lookup (priv->edit_mode_registry, 
467                                                         GINT_TO_POINTER (priv->edit_command));
468         if (reg == NULL)
469                 modest_hildon2_window_unset_edit_mode (self);
470
471         if ((reg->action == NULL) || reg->action (self))
472                 modest_hildon2_window_unset_edit_mode (self);
473
474 }
475
476 static void
477 edit_toolbar_arrow_clicked (HildonEditToolbar *toolbar,
478                             ModestHildon2Window *self)
479 {
480         g_return_if_fail (MODEST_IS_HILDON2_WINDOW (self));
481         
482         modest_hildon2_window_unset_edit_mode (self);
483 }
484