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