Added portrait mode support.
[birthday] / src / birthday.c
1 /*
2  *  Birthday application for Maemo.
3  *  Copyright (C) 2010 Roman Moravcik
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <time.h>
29
30 #include <libosso.h>
31
32 #include <glib.h>
33 #include <glib/gi18n.h>
34
35 #include <gtk/gtk.h>
36 #include <hildon/hildon.h>
37
38 #include <libebook/e-book.h>
39 #include <libosso-abook/osso-abook.h>
40
41 #include <mce/dbus-names.h>
42 #include <mce/mode-names.h>
43
44 #define MCE_MATCH_RULE "type='signal',interface='" MCE_SIGNAL_IF "',member='" MCE_DEVICE_ORIENTATION_SIG "'"
45
46 #define _HL(str) dgettext("hildon-libs",str)
47
48 enum
49 {
50         COLUMN_AVATAR = 0,
51         COLUMN_DISPLAY,
52         COLUMN_FULLNAME,
53         COLUMN_NEXT_BIRTHDAY,
54         NUM_COLS
55 };
56
57 /* Application UI data struct */
58 typedef struct _BirthdayData BirthdayData;
59 struct _BirthdayData {
60         GtkWidget *window;
61         GtkWidget *label;
62         GtkWidget *view;
63         GtkWidget *search;
64
65         GtkTreeViewColumn *display_column;
66
67         GtkWidget *tree_view;
68         GtkTreeModel *sorted;
69         GtkTreeModel *filter;
70
71         gchar *searched_name;
72         gboolean found;
73
74         guint n_contacts;
75 };
76
77 static void set_portrait_mode (BirthdayData *priv, gboolean enable);
78
79 static gboolean
80 is_portrait_mode (osso_context_t *osso_context)
81 {
82         osso_rpc_t ret;
83         gboolean result = FALSE;
84
85         if (osso_rpc_run_system (osso_context, MCE_SERVICE, MCE_REQUEST_PATH,
86                                  MCE_REQUEST_IF, MCE_DEVICE_ORIENTATION_GET,
87                                  &ret, DBUS_TYPE_INVALID) == OSSO_OK) {
88
89                 if (strcmp (ret.value.s, MCE_ORIENTATION_PORTRAIT) == 0) {
90                         result = TRUE;
91                 }
92                 osso_rpc_free_val (&ret);
93         } else {
94                 g_critical ("ERROR: Call do DBus failed\n");
95         }
96         return result;
97 }
98
99 static DBusHandlerResult
100 dbus_handle_mce_message (DBusConnection *connection,
101                          DBusMessage *message,
102                          gpointer data)
103 {
104         DBusMessageIter iter;
105         const gchar *orientation = NULL;
106         BirthdayData *priv;
107
108         g_return_val_if_fail (data, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
109         priv = (BirthdayData *) data;
110
111         if (dbus_message_is_signal (message, MCE_SIGNAL_IF, MCE_DEVICE_ORIENTATION_SIG)) {
112                 if (dbus_message_iter_init (message, &iter)) {
113                         dbus_message_iter_get_basic(&iter, &orientation);
114                         if (orientation) {
115                                 if (!strcmp (orientation, MCE_ORIENTATION_PORTRAIT))
116                                         set_portrait_mode (priv, TRUE);
117                                 else
118                                         set_portrait_mode (priv, FALSE);
119                         }
120                 }
121         }
122         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
123 }
124
125 static gboolean
126 birthday_filered_view_visible_func (GtkTreeModel *model,
127                                     GtkTreeIter *iter,
128                                     gpointer data)
129 {
130         BirthdayData *priv;
131         gchar *fullname = NULL, *ascii_searched_name = NULL, *ascii_fullname = NULL;
132         gboolean found = FALSE;
133
134         g_return_val_if_fail (data, FALSE);
135         priv = (BirthdayData *) data;
136
137         if (priv->searched_name == NULL) {
138                 priv->found = TRUE;
139                 return TRUE;
140         }
141
142         ascii_searched_name = g_ascii_strdown (priv->searched_name, strlen (priv->searched_name));
143
144         gtk_tree_model_get (model, iter, COLUMN_FULLNAME, &fullname, -1);
145         if (fullname) {
146                 ascii_fullname = g_ascii_strdown (fullname,  strlen (fullname));
147                 g_free (fullname);
148         }
149
150         if (g_strstr_len (ascii_fullname, strlen (ascii_fullname), ascii_searched_name) != NULL)
151                 found = TRUE;
152
153         if (ascii_searched_name)
154                 g_free (ascii_searched_name);
155
156         if (ascii_fullname)
157                 g_free (ascii_fullname);
158
159         if (found)
160                 priv->found = TRUE;
161
162         return found;
163 }
164
165 static void
166 sort_by_name_clicked (GtkButton *button,
167                       gpointer data)
168 {
169         BirthdayData *priv;
170
171         g_return_if_fail (data);
172         priv = (BirthdayData *) data;
173
174         if (priv->sorted) {
175                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->sorted),
176                                                       COLUMN_FULLNAME, GTK_SORT_ASCENDING);
177         }
178 }
179
180 static void
181 sort_by_date_clicked (GtkButton *button,
182                       gpointer data)
183 {
184         BirthdayData *priv;
185
186         g_return_if_fail (data);
187         priv = (BirthdayData *) data;
188
189         if (priv->sorted) {
190                 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->sorted),
191                                                       COLUMN_NEXT_BIRTHDAY, GTK_SORT_ASCENDING);
192         }
193 }
194
195 static void
196 search_menu_clicked (GtkButton *button,
197                      gpointer data)
198 {
199         BirthdayData *priv;
200         GtkWidget *entry;
201
202         g_return_if_fail (data);
203         priv = (BirthdayData *) data;
204
205         /* show search bar */
206         gtk_widget_show (priv->search);
207
208         /* focus on search entry */
209         entry  = g_object_get_data (G_OBJECT (priv->search), "entry");
210         gtk_entry_set_text (GTK_ENTRY (entry), "");
211         gtk_widget_grab_focus (GTK_WIDGET (entry));
212
213         /* refilter tree view */
214         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
215 }
216
217 static void
218 on_search_entry_changed (GtkEditable *editable,
219                          gpointer data)
220 {
221         BirthdayData *priv;
222
223         g_return_if_fail (data);
224         priv = (BirthdayData *) data;
225
226         priv->found = FALSE;
227
228         if (priv->searched_name)
229                 g_free (priv->searched_name);
230         priv->searched_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (editable)));
231
232         /* refilter tree view */
233         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
234
235         if (priv->found) {
236                 /* hide label */
237                 gtk_widget_hide (priv->label);
238
239                 /* show tree view */
240                 gtk_widget_show (priv->view);
241         } else {
242                 /* hide label */
243                 gtk_widget_show (priv->label);
244                 gtk_label_set_text (GTK_LABEL (priv->label), _("No search results"));
245
246                 /* show tree view */
247                 gtk_widget_hide (priv->view);
248         }
249
250         /* ugly, but working way how to scroll to the first row */
251         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->tree_view),
252                                       gtk_tree_path_new_from_string ("0"), NULL, FALSE, 0, 0);
253 }
254
255 static void
256 on_search_close_clicked (GtkButton *button,
257                          gpointer data)
258 {
259         BirthdayData *priv;
260
261         g_return_if_fail (data);
262         priv = (BirthdayData *) data;
263
264         /* hide search bar */
265         gtk_widget_hide (priv->search);
266
267         /* hide label */
268         gtk_widget_hide (priv->label);
269
270         /* show tree view */
271         gtk_widget_show (priv->view);
272
273         /* clear searched name */
274         if (priv->searched_name)
275                 g_free (priv->searched_name);
276         priv->searched_name = NULL;
277
278         /* refilter tree view */
279         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
280
281         /* ugly, but working way how to scroll to the first row */
282         gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->tree_view),
283                                       gtk_tree_path_new_from_string ("0"), NULL, FALSE, 0, 0);
284 }
285
286
287 static gboolean
288 on_key_press_event (GtkWidget *widget,
289                     GdkEventKey *event,
290                     gpointer data)
291 {
292         BirthdayData *priv;
293
294         g_return_val_if_fail (data, TRUE);
295         priv = (BirthdayData *) data;
296
297         if (priv->n_contacts == 0)
298                 return FALSE;
299
300         if ((event->keyval > GDK_space) && (event->keyval <= GDK_stricteq) && !GTK_WIDGET_VISIBLE (priv->search)) {
301                 GtkWidget *entry;
302
303                 /* show search bar */
304                 gtk_widget_show (priv->search);
305
306                 /* focus on search entry */
307                 entry  = g_object_get_data (G_OBJECT (priv->search), "entry");
308                 gtk_entry_set_text (GTK_ENTRY (entry), "");
309                 gtk_widget_grab_focus (GTK_WIDGET (entry));
310
311                 /* refilter tree view */
312                 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
313         }
314
315         return FALSE;
316 }
317
318 static unsigned int
319 calc_age (EContactDate *bdate, time_t current_date)
320 {
321         struct tm *current_date_tm;
322         struct tm bday_tm;
323         int age = 0;
324
325         current_date_tm = gmtime (&current_date);
326
327         bday_tm.tm_sec = 0;
328         bday_tm.tm_min = 0;
329         bday_tm.tm_hour = 0;
330         bday_tm.tm_mday = bdate->day;
331         bday_tm.tm_mon = bdate->month - 1;
332         bday_tm.tm_year = current_date_tm->tm_year;
333         bday_tm.tm_isdst = current_date_tm->tm_isdst;
334
335         if (mktime (&bday_tm) > current_date) {
336                 age = (current_date_tm->tm_year + 1900) - bdate->year - 1;
337         } else {
338                 age = (current_date_tm->tm_year + 1900) - bdate->year;
339         }
340
341         if (age < 0)
342                 age = 0;
343
344         return age;
345 }
346
347 static unsigned int
348 calc_next_bday (EContactDate *bdate, time_t current_date)
349 {
350         struct tm current_bday_tm, next_bday_tm;
351         struct tm *current_date_tm;
352         time_t current_bday, next_bday;
353
354         current_date_tm = gmtime (&current_date);
355
356         current_bday_tm.tm_sec = 0;
357         current_bday_tm.tm_min = 0;
358         current_bday_tm.tm_hour = 0;
359         current_bday_tm.tm_mday = bdate->day;
360         current_bday_tm.tm_mon = bdate->month - 1;
361         current_bday_tm.tm_year = current_date_tm->tm_year;
362         current_bday_tm.tm_isdst = current_date_tm->tm_isdst;
363         current_bday = mktime (&current_bday_tm);
364
365         if (current_date > current_bday) {
366                 next_bday_tm.tm_sec = 0;
367                 next_bday_tm.tm_min = 0;
368                 next_bday_tm.tm_hour = 0;
369                 next_bday_tm.tm_mday = bdate->day;
370                 next_bday_tm.tm_mon = bdate->month - 1;
371                 next_bday_tm.tm_year = current_date_tm->tm_year + 1;
372                 next_bday_tm.tm_isdst = current_date_tm->tm_isdst;
373                 next_bday = mktime (&next_bday_tm);
374         } else {
375                 next_bday = current_bday;
376         }
377
378         return (next_bday - current_date) / 86400;
379 }
380
381 static gchar *
382 get_text_font_by_name (const gchar *name)
383 {
384         GtkSettings *settings;
385         GtkStyle *style;
386
387         settings = gtk_settings_get_default ();
388         style = gtk_rc_get_style_by_paths (settings, name, NULL, G_TYPE_NONE);
389         return pango_font_description_to_string (style->font_desc);
390 }
391
392 static gchar *
393 get_text_color_by_name (const gchar *name)
394 {
395         GtkSettings *settings;
396         GtkStyle *style;
397         GdkColor color;
398
399         settings = gtk_settings_get_default ();
400         style = gtk_rc_get_style_by_paths (settings, "GtkButton", "osso-logical-colors", G_OBJECT_TYPE(gtk_button_new()));
401         gtk_style_lookup_color (style, name, &color);
402         return gdk_color_to_string (&color);
403 }
404
405 static void
406 set_portrait_mode (BirthdayData *priv,
407                    gboolean enable)
408 {
409         g_return_if_fail (priv);
410
411         if (enable) {
412                 gtk_tree_view_column_set_fixed_width (priv->display_column, 389);
413                 hildon_gtk_window_set_portrait_flags (GTK_WINDOW (priv->window),
414                                                       HILDON_PORTRAIT_MODE_REQUEST);
415         } else {
416                 gtk_tree_view_column_set_fixed_width (priv->display_column, 709);
417                 hildon_gtk_window_set_portrait_flags (GTK_WINDOW (priv->window),
418                                                       ~HILDON_PORTRAIT_MODE_REQUEST);
419         }
420 }
421
422 static GtkListStore *
423 create_bday_liststore (BirthdayData *priv, GList *contacts)
424 {
425         GtkListStore *store;
426         GtkTreeIter iter;
427         GList *contact;
428         gchar *text_font = NULL;
429         gchar *text_color = NULL;
430         guint n_contacts = 0;
431         time_t current_date;
432         struct tm *current_date_tm;
433
434         g_return_val_if_fail (priv, NULL);
435
436         text_font = get_text_font_by_name ("SmallSystemFont");
437         text_color = get_text_color_by_name ("SecondaryTextColor");
438
439         current_date = time (NULL);
440
441         /* set hour, minute, second to 0 */
442         current_date_tm = gmtime (&current_date);
443         current_date_tm->tm_sec = 0;
444         current_date_tm->tm_min = 0;
445         current_date_tm->tm_hour = 0;
446         current_date = mktime (current_date_tm);
447
448         store = gtk_list_store_new(NUM_COLS,
449                                    GDK_TYPE_PIXBUF,     /* COLUMN_AVATAR */
450                                    G_TYPE_STRING,       /* COLUMN_DISPLAY */
451                                    G_TYPE_STRING,       /* COLUMN_FULLNAME */
452                                    G_TYPE_INT);         /* COLUMN_NEXT_BIRTHDAY */
453
454         for (contact = contacts; contact != NULL; contact = contact->next) {
455                 EContactDate *bdate = NULL;
456
457                 bdate = e_contact_get (E_CONTACT (contact->data), E_CONTACT_BIRTH_DATE);
458                 if (bdate) {
459                         EContactPhoto *photo = NULL;
460                         GError *error = NULL;
461                         GdkPixbuf *avatar = NULL;
462                         gchar *fullname = NULL;
463                         guint age = 0, next_birthday = 0;
464                         gchar *display_column = NULL;
465                         gchar *next_birthday_text = NULL;
466                         struct tm birthday_tm;
467                         gchar birthday_text[11];
468
469                         photo = e_contact_get (E_CONTACT (contact->data), E_CONTACT_PHOTO);
470                         if (photo) {
471                                 if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
472                                         GdkPixbufLoader *loader;
473
474                                         loader = gdk_pixbuf_loader_new ();
475                                         if (gdk_pixbuf_loader_write (loader, (guchar *) photo->data.inlined.data, photo->data.inlined.length, NULL))
476                                                 avatar = gdk_pixbuf_loader_get_pixbuf (loader);
477
478                                 } else {
479                                         gchar *avatar_filename = NULL;
480
481                                         avatar_filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
482                                         if (avatar_filename) {
483                                                 avatar = gdk_pixbuf_new_from_file (avatar_filename, &error);
484                                                 g_free (avatar_filename);
485                                         }
486                                 }
487
488                                 if (avatar) {
489                                         gint height = gdk_pixbuf_get_height (avatar);
490                                         if (height != 48) {
491                                                 gint new_height = 48;
492                                                 gint new_width = new_height * gdk_pixbuf_get_width (avatar) / height;
493                                                 avatar = gdk_pixbuf_scale_simple (avatar, new_width, new_height, GDK_INTERP_BILINEAR);
494                                         }
495                                 }
496                                 e_contact_photo_free (photo);
497                                 photo = NULL;
498                         } else {
499                                 avatar = gdk_pixbuf_new_from_file ("/usr/share/icons/hicolor/48x48/hildon/general_default_avatar.png", &error);
500                         }
501
502                         fullname = e_contact_get (E_CONTACT (contact->data), E_CONTACT_FULL_NAME);
503                         if (!fullname) {
504                                 fullname = e_contact_get (E_CONTACT (contact->data), E_CONTACT_GIVEN_NAME);
505                                 if (!fullname) {
506                                         fullname = e_contact_get (E_CONTACT (contact->data), E_CONTACT_FAMILY_NAME);
507                                         if (!fullname) {
508                                                 fullname = e_contact_get (E_CONTACT (contact->data), E_CONTACT_NICKNAME);
509                                                 if (!fullname) {
510                                                         fullname = e_contact_get (E_CONTACT (contact->data), E_CONTACT_ORG);
511                                                 }
512                                         }
513                                 }
514                         }
515
516                         birthday_tm.tm_sec = 0;
517                         birthday_tm.tm_min = 0;
518                         birthday_tm.tm_hour = 0;
519                         birthday_tm.tm_mday = bdate->day;
520                         birthday_tm.tm_mon = bdate->month - 1;
521                         birthday_tm.tm_year = bdate->year - 1900;
522                         strftime (birthday_text, 11, _HL("wdgt_va_date"), &birthday_tm);
523
524                         age = calc_age(bdate, current_date);
525                         next_birthday = calc_next_bday(bdate, current_date);
526
527                         if (next_birthday == 0)
528                                 next_birthday_text = g_strdup_printf (_("has birthday today"));
529                         else
530                                 next_birthday_text = g_strdup_printf (ngettext ("will have birthday tomorrow",
531                                                                                 "will have birthday in %d days", next_birthday),
532                                                                       next_birthday);
533
534                         display_column = g_strdup_printf ("%s <span font_desc=\"%s\" foreground=\"%s\"><sup>(%d)</sup>\n%s, %s</span>",
535                                                          fullname, text_font, text_color, age, birthday_text, next_birthday_text);
536
537                         gtk_list_store_append (store, &iter);
538                         gtk_list_store_set (store, &iter,
539                                             COLUMN_AVATAR, avatar,
540                                             COLUMN_DISPLAY, display_column,
541                                             COLUMN_FULLNAME, fullname,
542                                             COLUMN_NEXT_BIRTHDAY, next_birthday,
543                                             -1);
544                         n_contacts++;
545
546                         if (display_column)
547                                 g_free (display_column);
548                         display_column = NULL;
549
550                         if (fullname)
551                                 g_free (fullname);
552                         fullname = NULL;
553
554                         if (next_birthday_text)
555                                 g_free (next_birthday_text);
556                         next_birthday_text = NULL;
557
558                         e_contact_date_free (bdate);
559                 }
560                 bdate = NULL;
561         }
562
563         if (text_font)
564                 g_free (text_font);
565
566         if (text_color)
567                 g_free (text_color);
568
569         priv->n_contacts = n_contacts;
570         return store;
571 }
572
573 static void
574 create_search_bar (BirthdayData *priv)
575 {
576         GtkWidget *entry, *button;
577         GtkEntryCompletion *completion;
578
579         g_return_if_fail (priv);
580
581         /* search hbox */
582         priv->search = gtk_hbox_new (FALSE, HILDON_MARGIN_DEFAULT);
583
584         /* search entry */
585         entry = hildon_entry_new (HILDON_SIZE_FINGER_HEIGHT);
586         hildon_gtk_entry_set_input_mode (GTK_ENTRY (entry), HILDON_GTK_INPUT_MODE_FULL);
587         gtk_box_pack_start (GTK_BOX (priv->search), entry, TRUE, TRUE, 0);
588
589         completion = gtk_entry_completion_new ();
590         gtk_entry_completion_set_inline_completion (completion, TRUE);
591         gtk_entry_completion_set_popup_completion (completion, FALSE);
592         gtk_entry_set_completion (GTK_ENTRY (entry), completion);
593
594         /* clear button */
595         button = GTK_WIDGET (gtk_tool_button_new (gtk_image_new_from_icon_name
596                              ("general_close", (GtkIconSize) HILDON_ICON_PIXEL_SIZE_FINGER), "Clear"));
597         gtk_box_pack_end (GTK_BOX (priv->search), button, FALSE, TRUE, 0);
598
599         /* search signals */
600         g_signal_connect (entry, "changed", G_CALLBACK (on_search_entry_changed), priv);
601         g_signal_connect (button, "clicked", G_CALLBACK (on_search_close_clicked), priv);
602
603         g_object_set_data (G_OBJECT (priv->search), "entry", entry);
604 }
605
606 static void
607 create_main_menu (BirthdayData *priv)
608 {
609         HildonAppMenu *menu;
610         GtkWidget *filter, *item;
611
612         g_return_if_fail (priv);
613
614         menu = HILDON_APP_MENU (hildon_app_menu_new ());
615         hildon_window_set_app_menu (HILDON_WINDOW (priv->window), menu);
616
617         filter = hildon_gtk_radio_button_new (HILDON_SIZE_FINGER_HEIGHT , NULL);
618         gtk_button_set_label (GTK_BUTTON (filter), _("Name"));
619         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (filter), FALSE);
620         g_signal_connect_after (filter, "clicked", G_CALLBACK (sort_by_name_clicked), priv);
621         hildon_app_menu_add_filter (menu, GTK_BUTTON (filter));
622
623         filter = hildon_gtk_radio_button_new_from_widget (HILDON_SIZE_FINGER_HEIGHT , GTK_RADIO_BUTTON (filter));
624         gtk_button_set_label (GTK_BUTTON (filter), _("Date"));
625         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (filter), FALSE);
626         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (filter), TRUE);
627         g_signal_connect_after (filter, "clicked", G_CALLBACK (sort_by_date_clicked), priv);
628         hildon_app_menu_add_filter (menu, GTK_BUTTON (filter));
629
630         item = hildon_gtk_button_new (HILDON_SIZE_AUTO);
631         gtk_button_set_label (GTK_BUTTON (item), _("Search"));
632         hildon_app_menu_append (menu, GTK_BUTTON (item));
633         g_signal_connect (item, "clicked", G_CALLBACK (search_menu_clicked), priv);
634
635         if (priv->n_contacts > 0)
636                 gtk_widget_show_all (GTK_WIDGET (menu));
637 }
638
639 static void
640 create_main_window (BirthdayData *priv, GtkListStore *store)
641 {
642         HildonProgram *program = NULL;
643         GtkWidget *main_vbox, *alignment, *pannable, *tree_view;
644         GtkTreeModel *filter;
645         GtkTreeViewColumn *column;
646         GtkCellRenderer *renderer;
647
648         g_return_if_fail (priv);
649
650         program = hildon_program_get_instance ();
651         g_set_application_name (_("Birthday"));
652
653         /* main window */
654         priv->window = hildon_stackable_window_new ();
655         hildon_program_add_window (program, HILDON_WINDOW (priv->window));
656
657         /* create main menu */
658         create_main_menu (priv);
659
660         /* aligment */
661         alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
662         gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
663                                    HILDON_MARGIN_HALF, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
664         gtk_container_add (GTK_CONTAINER (priv->window), alignment);
665
666         /* main vbox */
667         main_vbox = gtk_vbox_new (FALSE, 0);
668         gtk_container_add (GTK_CONTAINER (alignment), main_vbox);
669
670         /* no_search_result label */
671         priv->label = gtk_label_new (_("No contacts with birthday"));
672         hildon_helper_set_logical_color (priv->label, GTK_RC_FG,
673                                          GTK_STATE_NORMAL, "SecondaryTextColor");
674         hildon_helper_set_logical_font (priv->label, "LargeSystemFont");
675         gtk_box_pack_start (GTK_BOX (main_vbox), priv->label, TRUE, TRUE, 0);
676
677         /* alignment for pannable area */
678         priv->view = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
679         gtk_alignment_set_padding (GTK_ALIGNMENT (priv->view),
680                                    0, 0, HILDON_MARGIN_DEFAULT, 0);
681         gtk_box_pack_start (GTK_BOX (main_vbox), priv->view, TRUE, TRUE, 0);
682
683         /* pannable for tree view */
684         pannable = hildon_pannable_area_new ();
685         g_object_set (G_OBJECT (pannable), "mov-mode", HILDON_MOVEMENT_MODE_VERT, NULL);
686         gtk_container_add (GTK_CONTAINER (priv->view), pannable);
687
688         /* sort list by next birthdays */
689         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
690                                               COLUMN_NEXT_BIRTHDAY, GTK_SORT_ASCENDING);
691         priv->sorted = GTK_TREE_MODEL (store);
692
693         /* filtered view */
694         filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
695         gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
696                                                 birthday_filered_view_visible_func,
697                                                 priv,
698                                                 NULL);
699         gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter));
700         priv->filter = GTK_TREE_MODEL (filter);
701
702         /* tree view */
703         priv->tree_view = hildon_gtk_tree_view_new_with_model (HILDON_UI_MODE_NORMAL, filter);
704         gtk_container_add (GTK_CONTAINER (pannable), priv->tree_view);
705
706         /* display column */
707         column = gtk_tree_view_column_new ();
708         gtk_tree_view_column_set_fixed_width (column, 709);
709         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
710         renderer = gtk_cell_renderer_text_new ();
711         gtk_tree_view_column_pack_start (column, renderer, TRUE);
712         gtk_tree_view_column_set_attributes (column, renderer,
713                                              "markup", COLUMN_DISPLAY,
714                                              NULL);
715         g_object_set (G_OBJECT (renderer), "xpad", 10, NULL);
716         gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
717         priv->display_column = column;
718
719         /* avatar column */
720         column = gtk_tree_view_column_new ();
721         gtk_tree_view_column_set_fixed_width (column, 48);
722         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
723         renderer = gtk_cell_renderer_pixbuf_new ();
724         gtk_tree_view_column_pack_end (column, renderer, FALSE);
725         gtk_tree_view_column_set_attributes (column, renderer,
726                                              "pixbuf", COLUMN_AVATAR,
727                                              NULL);
728         gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
729
730         /* search bar */
731         create_search_bar(priv);
732         gtk_box_pack_end (GTK_BOX (main_vbox), priv->search, FALSE, FALSE, 0);
733
734         /* window signals */
735         g_signal_connect (G_OBJECT (priv->window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
736         g_signal_connect (G_OBJECT (priv->window), "key-press-event", G_CALLBACK (on_key_press_event), priv);
737
738         gtk_widget_show_all (GTK_WIDGET (priv->window));
739         gtk_widget_hide (GTK_WIDGET (priv->search));
740
741         if (priv->n_contacts > 0) {
742                 gtk_widget_hide (GTK_WIDGET (priv->label));
743                 gtk_widget_show (GTK_WIDGET (priv->view));
744         } else {
745                 gtk_widget_show (GTK_WIDGET (priv->label));
746                 gtk_widget_hide (GTK_WIDGET (priv->view));
747         }
748
749         /* enable portrait mode support */
750         hildon_gtk_window_set_portrait_flags (GTK_WINDOW (priv->window),
751                                               HILDON_PORTRAIT_MODE_SUPPORT);
752 }
753
754 static GList *
755 get_all_contacts (EBook *ebook)
756 {
757         GError *error = NULL;
758         EBookQuery *query;
759         GList *contacts;
760
761         ebook = e_book_new_system_addressbook (&error);
762         if (!ebook) {
763                 g_warning ("Error opening system address book: %s", error->message);
764                 g_error_free (error);
765                 return NULL;
766         }
767
768         if (!e_book_open (ebook, TRUE, &error)) {
769                 g_warning ("Error opening system address book: %s", error->message);
770                 g_error_free (error);
771                 return NULL;
772         }
773
774         query = e_book_query_any_field_contains ("");
775
776         if (!e_book_get_contacts (ebook, query, &contacts, &error)) {
777                 g_warning ("Error getting contacts: %s", error->message);
778                 g_error_free (error);
779                 return NULL;
780         }
781
782         return contacts;
783 }
784
785 int main (int argc, char **argv)
786 {
787         BirthdayData *data;
788         osso_context_t *osso_context;
789         EBook *ebook;
790         GtkWidget *window;
791         GtkListStore *store;
792         GList *contacts;
793
794         hildon_gtk_init (&argc, &argv);
795
796         /* create application data */
797         data = g_new0 (BirthdayData, 1);
798         data->searched_name = NULL;
799         data->found = TRUE;
800         data->n_contacts = 0;
801
802         /* initialize localization */
803         setlocale(LC_ALL, "");
804         bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
805         bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
806         textdomain(GETTEXT_PACKAGE);
807
808         /* initialize osso */
809         osso_context = osso_initialize ("birthday", "0.1", TRUE, NULL);
810         if (osso_context == NULL) {
811                 g_critical ("Error initializing osso");
812                 return 1;
813         }
814
815         /* init abook */
816         if (!osso_abook_init (&argc, &argv, osso_context)) {
817                 g_critical ("Error initializing libosso-abook");
818                 goto exit;
819         }
820
821         contacts = get_all_contacts (ebook);
822         store = create_bday_liststore (data, contacts);
823
824         /* create main widow */
825         create_main_window (data, store);
826
827         /* get the system dbus connection */
828         DBusConnection *dbus_connection = osso_get_sys_dbus_connection (osso_context);
829
830         /* add the callback, which should be called, once the device is rotated */
831         dbus_bus_add_match (dbus_connection, MCE_MATCH_RULE, NULL);
832         dbus_connection_add_filter (dbus_connection, dbus_handle_mce_message, data, NULL);
833
834         /* check if device is not already in portrait orientation */
835         if (is_portrait_mode (osso_context))
836                 set_portrait_mode (data, TRUE);
837
838         gtk_main ();
839
840 exit:
841         osso_deinitialize (osso_context);
842         return 0;
843 }