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