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