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