Use an app menu for the actions instead of a row of buttons
[milk] / src / milk-main-window.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License as
4  * published by the Free Software Foundation; either version 2 of the
5  * License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  * General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public
13  * License along with this program; if not, write to the
14  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
15  * Boston, MA  02110-1301  USA
16  *
17  * Authors: Travis Reitter <treitter@gmail.com>
18  */
19
20 #include <config.h>
21
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <gtk/gtk.h>
25 #include <hildon/hildon.h>
26
27 #include "milk-main-window.h"
28
29 G_DEFINE_TYPE (MilkMainWindow, milk_main_window, HILDON_TYPE_WINDOW)
30
31 #define MILK_MAIN_WINDOW_PRIVATE(o) \
32                 (G_TYPE_INSTANCE_GET_PRIVATE ((o), MILK_TYPE_MAIN_WINDOW, MilkMainWindowPrivate))
33
34 struct _MilkMainWindowPrivate
35 {
36         GtkWidget *main_vbox;
37
38         GtkWidget *new_task_entry;
39         GtkWidget *task_view;
40 };
41
42 /* FIXME: cut this */
43 enum {
44         TASK_MODEL_COLUMN_ID,
45         TASK_MODEL_COLUMN_PRIORITY,
46         TASK_MODEL_COLUMN_TITLE,
47         N_MODEL_COLUMNS
48 };
49
50 /* FIXME: cut this */
51 enum {
52         TASK_VIEW_COLUMN_TITLE,
53         N_VIEW_COLUMNS
54 };
55
56 /* FIXME: relocate this */
57 typedef struct {
58         gint id;
59         gint priority;
60         char *title;
61 } MilkTask;
62
63 /* FIXME: don't hard-code this */
64 static MilkTask tasks[] = {
65         { 0, 1, "Remember the milk"},
66         { 2, 1, "File taxes"},
67         { 6, 3, "Get a haircut"},
68         { 9, 2, "Keep it real"},
69         { 5, 3, "Invent smellovision"},
70 };
71
72 static void
73 milk_main_window_get_property (GObject    *object,
74                                guint       property_id,
75                                GValue     *value,
76                                GParamSpec *pspec)
77 {
78         switch (property_id)
79         {
80                 default:
81                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
82                                         pspec);
83         }
84 }
85
86 static void
87 milk_main_window_set_property (GObject      *object,
88                                guint         property_id,
89                                const GValue *value,
90                                GParamSpec   *pspec)
91 {
92         switch (property_id)
93         {
94                 default:
95                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id,
96                                         pspec);
97         }
98 }
99
100 static void
101 milk_main_window_dispose (GObject *object)
102 {
103         G_OBJECT_CLASS (milk_main_window_parent_class)->dispose (object);
104 }
105
106 static void
107 complete_clicked_cb (GtkButton      *button,
108                      MilkMainWindow *window)
109 {
110         g_debug ("FIXME: implement 'complete' action");
111 }
112
113 static void
114 delete_clicked_cb (GtkButton      *button,
115                    MilkMainWindow *window)
116 {
117         g_debug ("FIXME: implement 'delete' action");
118 }
119
120 static void
121 priority_plus_clicked_cb (GtkButton      *button,
122                           MilkMainWindow *window)
123 {
124         g_debug ("FIXME: implement 'priority plus' action");
125 }
126
127 static void
128 priority_minus_clicked_cb (GtkButton      *button,
129                            MilkMainWindow *window)
130 {
131         g_debug ("FIXME: implement 'priority minus' action");
132 }
133
134 static void
135 edit_clicked_cb (GtkButton      *button,
136                  MilkMainWindow *window)
137 {
138         g_debug ("FIXME: implement 'edit' action");
139 }
140
141 static MenuItem menu_items_always_shown[] = {
142         {"New Task",   "menu-item-new-task",       new_task_clicked_cb},
143 };
144
145 static MenuItem menu_items_selection_required[] = {
146         {"Edit",       "menu-item-edit",           edit_clicked_cb},
147         {"Priority +", "menu-item-priority_plus",  priority_plus_clicked_cb},
148         {"Priority -", "menu-item-priority_minus", priority_minus_clicked_cb},
149         {"Complete",   "menu-item-complete",       complete_clicked_cb},
150         {"Delete",     "menu-item-delete",         delete_clicked_cb},
151 };
152
153 static void
154 task_view_selection_changed_cb (HildonTouchSelector *view,
155                                 gint                 column,
156                                 MilkMainWindow      *window)
157 {
158         MilkMainWindowPrivate *priv;
159         GList *rows;
160         gboolean show = FALSE;
161         gint i;
162
163         priv = MILK_MAIN_WINDOW_PRIVATE (window);
164
165         rows = hildon_touch_selector_get_selected_rows (view, column);
166         show = (g_list_length (rows) > 0);
167
168         for (i = 0; i < G_N_ELEMENTS (menu_items_selection_required); i++) {
169                 GtkWidget *w;
170
171                 w = g_object_get_data (
172                                 G_OBJECT (priv->app_menu),
173                                 menu_items_selection_required[i].id);
174
175                 if (show)
176                         gtk_widget_show (w);
177                 else
178                         gtk_widget_hide (w);
179         }
180 }
181
182 static GtkWidget*
183 create_menu (gpointer user_data)
184 {
185         HildonAppMenu *menu;
186         MenuItem *menu_array;
187         gint i, length;
188         GtkWidget *w;
189
190         menu = HILDON_APP_MENU (hildon_app_menu_new ());
191
192         menu_array = menu_items_always_shown;
193         length = G_N_ELEMENTS (menu_items_always_shown);
194         for (i = 0; i < length; i++) {
195                 w = hildon_button_new_with_text (
196                                 HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
197                                 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
198                                 _(menu_array[i].display_name), "");
199                 g_signal_connect (w, "clicked",
200                                 G_CALLBACK (menu_array[i].callback), user_data);
201                 g_object_set_data (G_OBJECT (menu), menu_array[i].id, w);
202                 hildon_app_menu_append (menu, GTK_BUTTON (w));
203                 gtk_widget_show (w);
204         }
205
206         menu_array = menu_items_selection_required;
207         length = G_N_ELEMENTS (menu_items_selection_required);
208         for (i = 0; i < length; i++) {
209                 w = hildon_button_new_with_text (
210                                 HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
211                                 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
212                                 menu_array[i].display_name, "");
213                 g_signal_connect (w, "clicked",
214                                 G_CALLBACK (menu_array[i].callback), user_data);
215                 g_object_set_data (G_OBJECT (menu), menu_array[i].id, w);
216                 hildon_app_menu_append (menu, GTK_BUTTON (w));
217                 gtk_widget_hide (w);
218         }
219
220         gtk_widget_show (GTK_WIDGET (menu));
221
222         return GTK_WIDGET (menu);
223 }
224
225 static MenuItem menu_items_always_shown[] = {
226         {"New Task",   "menu-item-new-task",       new_task_clicked_cb},
227 };
228
229 static MenuItem menu_items_selection_required[] = {
230         {"Edit",       "menu-item-edit",           edit_clicked_cb},
231         {"Priority +", "menu-item-priority_plus",  priority_plus_clicked_cb},
232         {"Priority -", "menu-item-priority_minus", priority_minus_clicked_cb},
233         {"Complete",   "menu-item-complete",       complete_clicked_cb},
234         {"Delete",     "menu-item-delete",         delete_clicked_cb},
235 };
236
237 static void
238 task_view_selection_changed_cb (HildonTouchSelector *view,
239                                 gint                 column,
240                                 MilkMainWindow      *window)
241 {
242         MilkMainWindowPrivate *priv;
243         GList *rows;
244         gboolean show = FALSE;
245         gint i;
246
247         priv = MILK_MAIN_WINDOW_PRIVATE (window);
248
249         rows = hildon_touch_selector_get_selected_rows (view, column);
250         show = (g_list_length (rows) > 0);
251
252         for (i = 0; i < G_N_ELEMENTS (menu_items_selection_required); i++) {
253                 GtkWidget *w;
254
255                 w = g_object_get_data (
256                                 G_OBJECT (priv->app_menu),
257                                 menu_items_selection_required[i].id);
258
259                 if (show)
260                         gtk_widget_show (w);
261                 else
262                         gtk_widget_hide (w);
263         }
264 }
265
266 static GtkWidget*
267 create_menu (gpointer user_data)
268 {
269         HildonAppMenu *menu;
270         MenuItem *menu_array;
271         gint i, length;
272         GtkWidget *w;
273
274         menu = HILDON_APP_MENU (hildon_app_menu_new ());
275
276         menu_array = menu_items_always_shown;
277         length = G_N_ELEMENTS (menu_items_always_shown);
278         for (i = 0; i < length; i++) {
279                 w = hildon_button_new_with_text (
280                                 HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
281                                 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
282                                 _(menu_array[i].display_name), "");
283                 g_signal_connect (w, "clicked",
284                                 G_CALLBACK (menu_array[i].callback), user_data);
285                 g_object_set_data (G_OBJECT (menu), menu_array[i].id, w);
286                 hildon_app_menu_append (menu, GTK_BUTTON (w));
287                 gtk_widget_show (w);
288         }
289
290         menu_array = menu_items_selection_required;
291         length = G_N_ELEMENTS (menu_items_selection_required);
292         for (i = 0; i < length; i++) {
293                 w = hildon_button_new_with_text (
294                                 HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
295                                 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
296                                 menu_array[i].display_name, "");
297                 g_signal_connect (w, "clicked",
298                                 G_CALLBACK (menu_array[i].callback), user_data);
299                 g_object_set_data (G_OBJECT (menu), menu_array[i].id, w);
300                 hildon_app_menu_append (menu, GTK_BUTTON (w));
301                 gtk_widget_hide (w);
302         }
303
304         gtk_widget_show (GTK_WIDGET (menu));
305
306         return GTK_WIDGET (menu);
307 }
308
309 static void
310 contact_column_render_func (GtkCellLayout   *cell_layout,
311                             GtkCellRenderer *renderer,
312                             GtkTreeModel    *model,
313                             GtkTreeIter     *iter,
314                             gpointer         user_data)
315 {
316         MilkTask *task;
317         char *title;
318
319         gtk_tree_model_get (
320                         model, iter, MILK_TASK_MODEL_COLUMN_TASK, &task, -1);
321
322         g_object_get (task, "title", &title, NULL);
323         g_object_set (renderer, "text", title, NULL);
324
325         g_free (title);
326         g_object_unref (task);
327 }
328
329 /* FIXME: set up a MilkListStore instead */
330 static GtkTreeModel*
331 create_task_model ()
332 {
333         GtkListStore *list_store;
334         GtkTreeIter iter;
335         gint i;
336
337         list_store = gtk_list_store_new (N_MODEL_COLUMNS,
338                                          G_TYPE_INT,
339                                          G_TYPE_INT,
340                                          G_TYPE_STRING);
341
342         /* Populate model */
343         for (i = 0; i < G_N_ELEMENTS (tasks); i++) {
344                 gtk_list_store_append (list_store, &iter);
345                 gtk_list_store_set (list_store, &iter,
346                                     TASK_MODEL_COLUMN_ID, tasks[i].id,
347                                     TASK_MODEL_COLUMN_PRIORITY, tasks[i].priority,
348                                     TASK_MODEL_COLUMN_TITLE, tasks[i].title,
349                                    -1);
350         }
351
352         return GTK_TREE_MODEL (list_store);
353 }
354
355 static HildonAppMenu*
356 create_menu (gpointer user_data)
357 {
358         HildonAppMenu *menu;
359         GtkWidget *w;
360
361         menu = HILDON_APP_MENU (hildon_app_menu_new ());
362
363         w = hildon_button_new_with_text (
364                         HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
365                         HILDON_BUTTON_ARRANGEMENT_VERTICAL,
366                         _("Complete"), "");
367         g_signal_connect (w, "clicked", G_CALLBACK (complete_clicked_cb),
368                         user_data);
369         hildon_app_menu_append (menu, GTK_BUTTON (w));
370
371         w = hildon_button_new_with_text (
372                         HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
373                         HILDON_BUTTON_ARRANGEMENT_VERTICAL,
374                         _("Edit"), "");
375         g_signal_connect (w, "clicked", G_CALLBACK (edit_clicked_cb),
376                         user_data);
377         hildon_app_menu_append (menu, GTK_BUTTON (w));
378
379         w = hildon_button_new_with_text (
380                         HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
381                         HILDON_BUTTON_ARRANGEMENT_VERTICAL,
382                         _("Priority +"), "");
383         g_signal_connect (w, "clicked", G_CALLBACK (priority_plus_clicked_cb),
384                         user_data);
385         hildon_app_menu_append (menu, GTK_BUTTON (w));
386
387         w = hildon_button_new_with_text (
388                         HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
389                         HILDON_BUTTON_ARRANGEMENT_VERTICAL,
390                         _("Priority -"), "");
391         g_signal_connect (w, "clicked", G_CALLBACK (priority_minus_clicked_cb),
392                         user_data);
393         hildon_app_menu_append (menu, GTK_BUTTON (w));
394
395         w = hildon_button_new_with_text (
396                         HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
397                         HILDON_BUTTON_ARRANGEMENT_VERTICAL,
398                         _("Delete"), "");
399         g_signal_connect (w, "clicked", G_CALLBACK (delete_clicked_cb),
400                         user_data);
401         hildon_app_menu_append (menu, GTK_BUTTON (w));
402
403         gtk_widget_show_all (menu);
404
405         return menu;
406 }
407
408 static void
409 milk_main_window_constructed (GObject* object)
410 {
411         MilkMainWindow *self = MILK_MAIN_WINDOW (object);
412         MilkMainWindowPrivate *priv = MILK_MAIN_WINDOW_PRIVATE (object);
413         GtkWidget *w;
414         GtkTreeModel *model;
415         GtkCellRenderer *renderer;
416         HildonTouchSelectorColumn *col;
417
418         w = gtk_vbox_new (FALSE, HILDON_MARGIN_DEFAULT);
419         gtk_container_add (GTK_CONTAINER (self), w);
420         priv->main_vbox = w;
421
422         /*
423          * New Task entry
424          */
425         w = hildon_entry_new (HILDON_SIZE_FINGER_HEIGHT);
426         gtk_box_pack_start (GTK_BOX (priv->main_vbox), w, FALSE, FALSE, 0);
427
428         /* FIXME: change this to hildon_gtk_entry_set_placeholder_text() is
429          * fixed, since this is deprecated */
430         hildon_entry_set_placeholder (HILDON_ENTRY (w),
431                         _("Enter a new task..."));
432         priv->new_task_entry = w;
433
434         /*
435          * Task List
436          */
437         /* FIXME: create a new MilkTaskModel class that syncs with the cached
438          * tasks */
439         model = create_task_model ();
440         w = hildon_touch_selector_new ();
441
442         renderer = gtk_cell_renderer_text_new ();
443         g_object_set (renderer,
444                         "ellipsize", PANGO_ELLIPSIZE_END,
445                         NULL);
446         col = hildon_touch_selector_append_column (HILDON_TOUCH_SELECTOR (w),
447                         model, renderer,
448                         "text", TASK_MODEL_COLUMN_TITLE,
449                         NULL);
450         g_object_unref (model);
451
452         hildon_touch_selector_column_set_text_column (col,
453                         TASK_MODEL_COLUMN_TITLE);
454         hildon_touch_selector_set_column_selection_mode (
455                         HILDON_TOUCH_SELECTOR (w),
456                         HILDON_TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE);
457         hildon_touch_selector_set_hildon_ui_mode (HILDON_TOUCH_SELECTOR (w),
458                         HILDON_UI_MODE_EDIT);
459         hildon_touch_selector_unselect_all (HILDON_TOUCH_SELECTOR (w),
460                         TASK_VIEW_COLUMN_TITLE);
461
462         gtk_box_pack_start (GTK_BOX (priv->main_vbox), w, TRUE, TRUE, 0);
463         g_object_set (w, "can-focus", TRUE, NULL);
464         gtk_widget_grab_focus (w);
465         priv->task_view = w;
466
467         hildon_window_set_app_menu (HILDON_WINDOW (self), create_menu (self));
468 }
469
470 static void
471 milk_main_window_class_init (MilkMainWindowClass *klass)
472 {
473         GObjectClass *object_class = G_OBJECT_CLASS (klass);
474
475         g_type_class_add_private (klass, sizeof (MilkMainWindowPrivate));
476
477         object_class->get_property = milk_main_window_get_property;
478         object_class->set_property = milk_main_window_set_property;
479         object_class->constructed = milk_main_window_constructed;
480         object_class->dispose = milk_main_window_dispose;
481 }
482
483 static void
484 milk_main_window_init (MilkMainWindow *self)
485 {
486         self->priv = MILK_MAIN_WINDOW_PRIVATE (self);
487 }
488
489 GtkWidget*
490 milk_main_window_new ()
491 {
492         return g_object_new (MILK_TYPE_MAIN_WINDOW,
493                              NULL);
494 }