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