Added "Search" menu item. Use date string formating based on locale
[birthday] / src / birthday.c
index e4881f1..8ee41ab 100644 (file)
@@ -38,6 +38,8 @@
 #include <libebook/e-book.h>
 #include <libosso-abook/osso-abook.h>
 
+#define _HL(str) dgettext("hildon-libs",str)
+
 enum
 {
        COLUMN_AVATAR = 0,
@@ -47,62 +49,273 @@ enum
        NUM_COLS
 };
 
+/* Application UI data struct */
+typedef struct _BirthdayData BirthdayData;
+struct _BirthdayData {
+       GtkWidget *window;
+       GtkWidget *label;
+       GtkWidget *view;
+       GtkWidget *search;
+
+       GtkWidget *tree_view;
+       GtkTreeModel *sorted;
+       GtkTreeModel *filter;
+
+       gchar *searched_name;
+       gboolean found;
+
+       guint n_contacts;
+};
+
+static gboolean
+birthday_filered_view_visible_func (GtkTreeModel *model,
+                                   GtkTreeIter *iter,
+                                   gpointer data)
+{
+       BirthdayData *priv;
+       gchar *fullname = NULL, *ascii_searched_name = NULL, *ascii_fullname = NULL;
+       gboolean found = FALSE;
+
+       g_return_val_if_fail (data, FALSE);
+       priv = (BirthdayData *) data;
+
+       if (priv->searched_name == NULL) {
+               priv->found = TRUE;
+               return TRUE;
+       }
+
+       ascii_searched_name = g_ascii_strdown (priv->searched_name, strlen (priv->searched_name));
+
+       gtk_tree_model_get (model, iter, COLUMN_FULLNAME, &fullname, -1);
+       if (fullname) {
+               ascii_fullname = g_ascii_strdown (fullname,  strlen (fullname));
+               g_free (fullname);
+       }
+
+       if (g_strstr_len (ascii_fullname, strlen (ascii_fullname), ascii_searched_name) != NULL)
+               found = TRUE;
+
+       if (ascii_searched_name)
+               g_free (ascii_searched_name);
+
+       if (ascii_fullname)
+               g_free (ascii_fullname);
+
+       if (found)
+               priv->found = TRUE;
+
+       return found;
+}
+
+static void
+sort_by_name_clicked (GtkButton *button,
+                     gpointer data)
+{
+       BirthdayData *priv;
+
+       g_return_if_fail (data);
+       priv = (BirthdayData *) data;
+
+       if (priv->sorted) {
+               gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->sorted),
+                                                     COLUMN_FULLNAME, GTK_SORT_ASCENDING);
+       }
+}
+
+static void
+sort_by_date_clicked (GtkButton *button,
+                     gpointer data)
+{
+       BirthdayData *priv;
+
+       g_return_if_fail (data);
+       priv = (BirthdayData *) data;
+
+       if (priv->sorted) {
+               gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->sorted),
+                                                     COLUMN_NEXT_BIRTHDAY, GTK_SORT_ASCENDING);
+       }
+}
+
+static void
+search_menu_clicked (GtkButton *button,
+                    gpointer data)
+{
+       BirthdayData *priv;
+       GtkWidget *entry;
+
+       g_return_if_fail (data);
+       priv = (BirthdayData *) data;
+
+       /* show search bar */
+       gtk_widget_show (priv->search);
+
+       /* focus on search entry */
+       entry  = g_object_get_data (G_OBJECT (priv->search), "entry");
+       gtk_entry_set_text (GTK_ENTRY (entry), "");
+       gtk_widget_grab_focus (GTK_WIDGET (entry));
+
+       /* refilter tree view */
+       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+}
+
+static void
+on_search_entry_changed (GtkEditable *editable,
+                        gpointer data)
+{
+       BirthdayData *priv;
+
+       g_return_if_fail (data);
+       priv = (BirthdayData *) data;
+
+       priv->found = FALSE;
+
+       if (priv->searched_name)
+               g_free (priv->searched_name);
+       priv->searched_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (editable)));
+
+       /* refilter tree view */
+       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+
+       if (priv->found) {
+               /* hide label */
+               gtk_widget_hide (priv->label);
+
+               /* show tree view */
+               gtk_widget_show (priv->view);
+       } else {
+               /* hide label */
+               gtk_widget_show (priv->label);
+               gtk_label_set_text (GTK_LABEL (priv->label), _("No search results"));
+
+               /* show tree view */
+               gtk_widget_hide (priv->view);
+       }
+
+       /* ugly, but working way how to scroll to the first row */
+       gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->tree_view),
+                                     gtk_tree_path_new_from_string ("0"), NULL, FALSE, 0, 0);
+}
+
+static void
+on_search_close_clicked (GtkButton *button,
+                        gpointer data)
+{
+       BirthdayData *priv;
+
+       g_return_if_fail (data);
+       priv = (BirthdayData *) data;
+
+       /* hide search bar */
+       gtk_widget_hide (priv->search);
+
+       /* hide label */
+       gtk_widget_hide (priv->label);
+
+       /* show tree view */
+       gtk_widget_show (priv->view);
+
+       /* clear searched name */
+       if (priv->searched_name)
+               g_free (priv->searched_name);
+       priv->searched_name = NULL;
+
+       /* refilter tree view */
+       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+
+       /* ugly, but working way how to scroll to the first row */
+       gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->tree_view),
+                                     gtk_tree_path_new_from_string ("0"), NULL, FALSE, 0, 0);
+}
+
+
+static gboolean
+on_key_press_event (GtkWidget *widget,
+                   GdkEventKey *event,
+                   gpointer data)
+{
+       BirthdayData *priv;
+
+       g_return_val_if_fail (data, TRUE);
+       priv = (BirthdayData *) data;
+
+       if (priv->n_contacts == 0)
+               return FALSE;
+
+       if ((event->keyval > GDK_space) && (event->keyval <= GDK_stricteq) && !GTK_WIDGET_VISIBLE (priv->search)) {
+               GtkWidget *entry;
+
+               /* show search bar */
+               gtk_widget_show (priv->search);
+
+               /* focus on search entry */
+               entry  = g_object_get_data (G_OBJECT (priv->search), "entry");
+               gtk_entry_set_text (GTK_ENTRY (entry), "");
+               gtk_widget_grab_focus (GTK_WIDGET (entry));
+
+               /* refilter tree view */
+               gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+       }
+
+       return FALSE;
+}
+
 static unsigned int
 calc_age (EContactDate *bdate)
 {
-    struct tm tm_bday;
-    struct tm *tm_age;
-    time_t t_age;
-    int age = 0;
-
-    tm_bday.tm_sec = 0;
-    tm_bday.tm_min = 0;
-    tm_bday.tm_hour = 0;
-    tm_bday.tm_mday = bdate->day;
-    tm_bday.tm_mon = bdate->month - 1;
-    tm_bday.tm_year = bdate->year - 1900;
-
-    t_age = time (NULL) - mktime (&tm_bday);
-    tm_age = gmtime (&t_age);
-    age = tm_age->tm_year - 70;
-
-    if (age < 0)
-       age = 0;
-
-    return age;
+       struct tm bday_tm;
+       struct tm *age_tm;
+       time_t t_age;
+       int age = 0;
+
+       bday_tm.tm_sec = 0;
+       bday_tm.tm_min = 0;
+       bday_tm.tm_hour = 0;
+       bday_tm.tm_mday = bdate->day;
+       bday_tm.tm_mon = bdate->month - 1;
+       bday_tm.tm_year = bdate->year - 1900;
+
+       t_age = time (NULL) - mktime (&bday_tm);
+       age_tm = gmtime (&t_age);
+       age = age_tm->tm_year - 70;
+
+       if (age < 0)
+               age = 0;
+
+       return age;
 }
 
 static unsigned int
 calc_next_bday (EContactDate *bdate)
 {
-    struct tm tm_current_bday, tm_next_bday;
-    struct tm *tm_current_date;
-    time_t t_current_date, t_current_bday, t_next_bday, t_next_bday_in;
-
-    t_current_date = time (NULL);
-    tm_current_date = localtime (&t_current_date);
-
-    tm_current_bday.tm_sec = 0;
-    tm_current_bday.tm_min = 0;
-    tm_current_bday.tm_hour = 0;
-    tm_current_bday.tm_mday = bdate->day;
-    tm_current_bday.tm_mon = bdate->month - 1;
-    tm_current_bday.tm_year = tm_current_date->tm_year;
-    t_current_bday = mktime (&tm_current_bday);
-
-    if (t_current_date > t_current_bday) {
-       tm_next_bday.tm_sec = 0;
-       tm_next_bday.tm_min = 0;
-       tm_next_bday.tm_hour = 0;
-       tm_next_bday.tm_mday = bdate->day;
-       tm_next_bday.tm_mon = bdate->month - 1;
-       tm_next_bday.tm_year = tm_current_date->tm_year + 1;
-       t_next_bday = mktime (&tm_next_bday);
-    } else {
-       t_next_bday = t_current_bday;
-    }
-    t_next_bday_in = t_next_bday - t_current_date;
-    return (t_next_bday_in / 86400) + 1;
+       struct tm current_bday_tm, next_bday_tm;
+       struct tm *current_date_tm;
+       time_t t_current_date, t_current_bday, t_next_bday;
+
+       t_current_date = time (NULL);
+       current_date_tm = localtime (&t_current_date);
+
+       current_bday_tm.tm_sec = 0;
+       current_bday_tm.tm_min = 0;
+       current_bday_tm.tm_hour = 0;
+       current_bday_tm.tm_mday = bdate->day;
+       current_bday_tm.tm_mon = bdate->month - 1;
+       current_bday_tm.tm_year = current_date_tm->tm_year;
+       t_current_bday = mktime (&current_bday_tm);
+
+       if (t_current_date > t_current_bday) {
+               next_bday_tm.tm_sec = 0;
+               next_bday_tm.tm_min = 0;
+               next_bday_tm.tm_hour = 0;
+               next_bday_tm.tm_mday = bdate->day;
+               next_bday_tm.tm_mon = bdate->month - 1;
+               next_bday_tm.tm_year = current_date_tm->tm_year + 1;
+               t_next_bday = mktime (&next_bday_tm);
+       } else {
+               t_next_bday = t_current_bday;
+       }
+
+       return (t_next_bday - t_current_date) / 86400;
 }
 
 static gchar *
@@ -130,13 +343,16 @@ get_text_color_by_name (const gchar *name)
 }
 
 static GtkListStore *
-create_bday_liststore(GList *contacts)
+create_bday_liststore (BirthdayData *priv, GList *contacts)
 {
        GtkListStore *store;
        GtkTreeIter iter;
        GList *contact;
        gchar *text_font = NULL;
        gchar *text_color = NULL;
+       guint n_contacts = 0;
+
+       g_return_val_if_fail (priv, NULL);
 
        text_font = get_text_font_by_name ("SmallSystemFont");
        text_color = get_text_color_by_name ("SecondaryTextColor");
@@ -148,39 +364,80 @@ create_bday_liststore(GList *contacts)
                                   G_TYPE_INT);         /* COLUMN_NEXT_BIRTHDAY */
 
        for (contact = contacts; contact != NULL; contact = contact->next) {
-               EContactDate *bdate;
+               EContactDate *bdate = NULL;
 
                bdate = e_contact_get (E_CONTACT (contact->data), E_CONTACT_BIRTH_DATE);
                if (bdate) {
-                       EContactPhoto *photo;
+                       EContactPhoto *photo = NULL;
                        GError *error = NULL;
-                       GdkPixbuf *avatar;
-                       gchar *avatar_filename = NULL;
+                       GdkPixbuf *avatar = NULL;
                        gchar *fullname = NULL;
                        guint age = 0, next_birthday = 0;
                        gchar *display_column = NULL;
                        gchar *next_birthday_text = NULL;
+                       struct tm birthday_tm;
+                       gchar birthday_text[11];
 
                        photo = e_contact_get (E_CONTACT (contact->data), E_CONTACT_PHOTO);
-
                        if (photo) {
-                               avatar_filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
-                               if (avatar_filename) {
-                                       avatar = gdk_pixbuf_new_from_file_at_size (avatar_filename, 48, 48, &error);
-                                       g_free (avatar_filename);
-                                       avatar_filename = NULL;
+                               if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
+                                       GdkPixbufLoader *loader;
+
+                                       loader = gdk_pixbuf_loader_new ();
+                                       if (gdk_pixbuf_loader_write (loader, (guchar *) photo->data.inlined.data, photo->data.inlined.length, NULL))
+                                               avatar = gdk_pixbuf_loader_get_pixbuf (loader);
+
+                               } else {
+                                       gchar *avatar_filename = NULL;
+
+                                       avatar_filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
+                                       if (avatar_filename) {
+                                               avatar = gdk_pixbuf_new_from_file (avatar_filename, &error);
+                                               g_free (avatar_filename);
+                                       }
+                               }
+
+                               if (avatar) {
+                                       gint height = gdk_pixbuf_get_height (avatar);
+                                       if (height != 48) {
+                                               gint new_height = 48;
+                                               gint new_width = new_height * gdk_pixbuf_get_width (avatar) / height;
+                                               avatar = gdk_pixbuf_scale_simple (avatar, new_width, new_height, GDK_INTERP_BILINEAR);
+                                       }
                                }
-                               
+                               e_contact_photo_free (photo);
+                               photo = NULL;
                        } else {
                                avatar = gdk_pixbuf_new_from_file ("/usr/share/icons/hicolor/48x48/hildon/general_default_avatar.png", &error);
                        }
 
                        fullname = e_contact_get (E_CONTACT (contact->data), E_CONTACT_FULL_NAME);
+                       if (!fullname) {
+                               fullname = e_contact_get (E_CONTACT (contact->data), E_CONTACT_GIVEN_NAME);
+                               if (!fullname) {
+                                       fullname = e_contact_get (E_CONTACT (contact->data), E_CONTACT_FAMILY_NAME);
+                                       if (!fullname) {
+                                               fullname = e_contact_get (E_CONTACT (contact->data), E_CONTACT_NICKNAME);
+                                               if (!fullname) {
+                                                       fullname = e_contact_get (E_CONTACT (contact->data), E_CONTACT_ORG);
+                                               }
+                                       }
+                               }
+                       }
+
+                       birthday_tm.tm_sec = 0;
+                       birthday_tm.tm_min = 0;
+                       birthday_tm.tm_hour = 0;
+                       birthday_tm.tm_mday = bdate->day;
+                       birthday_tm.tm_mon = bdate->month - 1;
+                       birthday_tm.tm_year = bdate->year - 1900;
+                       strftime (birthday_text, 11, _HL("wdgt_va_date"), &birthday_tm);
+
                        age = calc_age(bdate);
                        next_birthday = calc_next_bday(bdate);
                        next_birthday_text = g_strdup_printf(ngettext ("next birthday in %d day", "next birthday in %d days", next_birthday), next_birthday);
-                       display_column = g_strdup_printf("%s <span font_desc=\"%s\" foreground=\"%s\"><sup>(%d)</sup>\n%02d.%02d.%04d, %s</span>",
-                                                        fullname, text_font, text_color, age, bdate->day, bdate->month, bdate->year, next_birthday_text);
+                       display_column = g_strdup_printf("%s <span font_desc=\"%s\" foreground=\"%s\"><sup>(%d)</sup>\n%s, %s</span>",
+                                                        fullname, text_font, text_color, age, birthday_text, next_birthday_text);
 
                        gtk_list_store_append (store, &iter);
                        gtk_list_store_set (store, &iter,
@@ -189,11 +446,23 @@ create_bday_liststore(GList *contacts)
                                            COLUMN_FULLNAME, fullname,
                                            COLUMN_NEXT_BIRTHDAY, next_birthday,
                                            -1);
+                       n_contacts++;
+
+                       if (display_column)
+                               g_free (display_column);
+                       display_column = NULL;
+
+                       if (fullname)
+                               g_free (fullname);
+                       fullname = NULL;
 
                        if (next_birthday_text)
                                g_free (next_birthday_text);
                        next_birthday_text = NULL;
+
+                       e_contact_date_free (bdate);
                }
+               bdate = NULL;
        }
 
        if (text_font)
@@ -202,60 +471,146 @@ create_bday_liststore(GList *contacts)
        if (text_color)
                g_free (text_color);
 
+       priv->n_contacts = n_contacts;
        return store;
 }
 
-static GtkWidget *
-create_main_window(GtkListStore *store)
+static void
+create_search_bar (BirthdayData *priv)
+{
+       GtkWidget *entry, *button;
+       GtkEntryCompletion *completion;
+
+       g_return_if_fail (priv);
+
+       /* search hbox */
+       priv->search = gtk_hbox_new (FALSE, HILDON_MARGIN_DEFAULT);
+
+       /* search entry */
+       entry = hildon_entry_new (HILDON_SIZE_FINGER_HEIGHT);
+       hildon_gtk_entry_set_input_mode (GTK_ENTRY (entry), HILDON_GTK_INPUT_MODE_FULL);
+       gtk_box_pack_start (GTK_BOX (priv->search), entry, TRUE, TRUE, 0);
+
+       completion = gtk_entry_completion_new ();
+       gtk_entry_completion_set_inline_completion (completion, TRUE);
+       gtk_entry_completion_set_popup_completion (completion, FALSE);
+       gtk_entry_set_completion (GTK_ENTRY (entry), completion);
+
+       /* clear button */
+       button = GTK_WIDGET (gtk_tool_button_new (gtk_image_new_from_icon_name
+                            ("general_close", (GtkIconSize) HILDON_ICON_PIXEL_SIZE_FINGER), "Clear"));
+       gtk_box_pack_end (GTK_BOX (priv->search), button, FALSE, TRUE, 0);
+
+       /* search signals */
+       g_signal_connect (entry, "changed", G_CALLBACK (on_search_entry_changed), priv);
+       g_signal_connect (button, "clicked", G_CALLBACK (on_search_close_clicked), priv);
+
+       g_object_set_data (G_OBJECT (priv->search), "entry", entry);
+}
+
+static void
+create_main_menu (BirthdayData *priv)
+{
+       HildonAppMenu *menu;
+       GtkWidget *filter, *item;
+
+       g_return_if_fail (priv);
+
+       menu = HILDON_APP_MENU (hildon_app_menu_new ());
+       hildon_window_set_app_menu (HILDON_WINDOW (priv->window), menu);
+
+       filter = hildon_gtk_radio_button_new (HILDON_SIZE_FINGER_HEIGHT , NULL);
+       gtk_button_set_label (GTK_BUTTON (filter), _("Name"));
+       gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (filter), FALSE);
+       g_signal_connect_after (filter, "clicked", G_CALLBACK (sort_by_name_clicked), priv);
+       hildon_app_menu_add_filter (menu, GTK_BUTTON (filter));
+
+       filter = hildon_gtk_radio_button_new_from_widget (HILDON_SIZE_FINGER_HEIGHT , GTK_RADIO_BUTTON (filter));
+       gtk_button_set_label (GTK_BUTTON (filter), _("Date"));
+       gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (filter), FALSE);
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (filter), TRUE);
+       g_signal_connect_after (filter, "clicked", G_CALLBACK (sort_by_date_clicked), priv);
+       hildon_app_menu_add_filter (menu, GTK_BUTTON (filter));
+
+       item = hildon_gtk_button_new (HILDON_SIZE_AUTO);
+       gtk_button_set_label (GTK_BUTTON (item), _("Search"));
+       hildon_app_menu_append (menu, GTK_BUTTON (item));
+       g_signal_connect (item, "clicked", G_CALLBACK (search_menu_clicked), priv);
+
+       if (priv->n_contacts > 0)
+               gtk_widget_show_all (GTK_WIDGET (menu));
+}
+
+static void
+create_main_window (BirthdayData *priv, GtkListStore *store)
 {
        HildonProgram *program = NULL;
-       GtkWidget *window, *main_vbox, *alignment, *label, *pannable, *tree_view;
+       GtkWidget *main_vbox, *alignment, *pannable, *tree_view;
+       GtkTreeModel *filter;
        GtkTreeViewColumn *column;
        GtkCellRenderer *renderer;
 
+       g_return_if_fail (priv);
+
        program = hildon_program_get_instance ();
        g_set_application_name (_("Birthday"));
 
        /* main window */
-       window = hildon_stackable_window_new ();
-       hildon_program_add_window (program, HILDON_WINDOW (window));
+       priv->window = hildon_stackable_window_new ();
+       hildon_program_add_window (program, HILDON_WINDOW (priv->window));
+
+       /* create main menu */
+       create_main_menu (priv);
 
        /* aligment */
        alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
                                   HILDON_MARGIN_HALF, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
-       gtk_container_add (GTK_CONTAINER (window), alignment);
+       gtk_container_add (GTK_CONTAINER (priv->window), alignment);
 
        /* main vbox */
        main_vbox = gtk_vbox_new (FALSE, 0);
        gtk_container_add (GTK_CONTAINER (alignment), main_vbox);
 
        /* no_search_result label */
-       label = gtk_label_new (_("No contacts with set birthdate"));
-       hildon_helper_set_logical_color (label, GTK_RC_FG,
+       priv->label = gtk_label_new (_("No contacts with birthday"));
+       hildon_helper_set_logical_color (priv->label, GTK_RC_FG,
                                         GTK_STATE_NORMAL, "SecondaryTextColor");
-       hildon_helper_set_logical_font (label, "LargeSystemFont");
-       gtk_box_pack_start (GTK_BOX (main_vbox), label, TRUE, TRUE, 0);
+       hildon_helper_set_logical_font (priv->label, "LargeSystemFont");
+       gtk_box_pack_start (GTK_BOX (main_vbox), priv->label, TRUE, TRUE, 0);
 
        /* alignment for pannable area */
-       alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
-       gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
-                                  0, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
-       gtk_box_pack_start (GTK_BOX (main_vbox), alignment, TRUE, TRUE, 0);
+       priv->view = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+       gtk_alignment_set_padding (GTK_ALIGNMENT (priv->view),
+                                  0, 0, HILDON_MARGIN_DEFAULT, 0);
+       gtk_box_pack_start (GTK_BOX (main_vbox), priv->view, TRUE, TRUE, 0);
 
        /* pannable for tree view */
        pannable = hildon_pannable_area_new ();
-       gtk_container_add (GTK_CONTAINER (alignment), pannable);
+       g_object_set (G_OBJECT (pannable), "mov-mode", HILDON_MOVEMENT_MODE_VERT, NULL);
+       gtk_container_add (GTK_CONTAINER (priv->view), pannable);
 
-       /* tree view */
-       tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
-       gtk_container_add(GTK_CONTAINER(pannable), tree_view);
+       /* sort list by next birthdays */
        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
                                              COLUMN_NEXT_BIRTHDAY, GTK_SORT_ASCENDING);
+       priv->sorted = GTK_TREE_MODEL (store);
+
+       /* filtered view */
+       filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
+       gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
+                                               birthday_filered_view_visible_func,
+                                               priv,
+                                               NULL);
+       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter));
+       priv->filter = GTK_TREE_MODEL (filter);
+
+       /* tree view */
+       priv->tree_view = hildon_gtk_tree_view_new_with_model (HILDON_UI_MODE_NORMAL, filter);
+       gtk_container_add (GTK_CONTAINER (pannable), priv->tree_view);
 
        /* display column */
        column = gtk_tree_view_column_new ();
-       gtk_tree_view_column_set_fixed_width (column, 704);
+       gtk_tree_view_column_set_fixed_width (column, 709);
        gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
        renderer = gtk_cell_renderer_text_new ();
        gtk_tree_view_column_pack_start (column, renderer, TRUE);
@@ -264,41 +619,87 @@ create_main_window(GtkListStore *store)
                                             NULL);
        g_object_set (G_OBJECT (renderer), "xpad", 10, NULL);
 
-       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+       gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
 
        /* avatar column */
        column = gtk_tree_view_column_new ();
-       gtk_tree_view_column_set_fixed_width (column, 64);
+       gtk_tree_view_column_set_fixed_width (column, 48);
        gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
        renderer = gtk_cell_renderer_pixbuf_new ();
        gtk_tree_view_column_pack_end (column, renderer, FALSE);
        gtk_tree_view_column_set_attributes (column, renderer,
                                             "pixbuf", COLUMN_AVATAR,
                                             NULL);
-       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
-
+       gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
 
+       /* search bar */
+       create_search_bar(priv);
+       gtk_box_pack_end (GTK_BOX (main_vbox), priv->search, FALSE, FALSE, 0);
 
        /* window signals */
-       g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
+       g_signal_connect (G_OBJECT (priv->window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
+       g_signal_connect (G_OBJECT (priv->window), "key-press-event", G_CALLBACK (on_key_press_event), priv);
+
+       gtk_widget_show_all (GTK_WIDGET (priv->window));
+       gtk_widget_hide (GTK_WIDGET (priv->search));
+
+       if (priv->n_contacts > 0) {
+               gtk_widget_hide (GTK_WIDGET (priv->label));
+               gtk_widget_show (GTK_WIDGET (priv->view));
+       } else {
+               gtk_widget_show (GTK_WIDGET (priv->label));
+               gtk_widget_hide (GTK_WIDGET (priv->view));
+       }
+}
+
+static GList *
+get_all_contacts (EBook *ebook)
+{
+       GError *error = NULL;
+       EBookQuery *query;
+       GList *contacts;
+
+       ebook = e_book_new_system_addressbook (&error);
+       if (!ebook) {
+               g_warning ("Error opening system address book: %s", error->message);
+               g_error_free (error);
+               return NULL;
+       }
+
+       if (!e_book_open (ebook, TRUE, &error)) {
+               g_warning ("Error opening system address book: %s", error->message);
+               g_error_free (error);
+               return NULL;
+       }
 
-       gtk_widget_show_all (GTK_WIDGET(window));
-       gtk_widget_hide (GTK_WIDGET(label));
-       return window;
+       query = e_book_query_any_field_contains ("");
+
+       if (!e_book_get_contacts (ebook, query, &contacts, &error)) {
+               g_warning ("Error getting contacts: %s", error->message);
+               g_error_free (error);
+               return NULL;
+       }
+
+       return contacts;
 }
 
 int main (int argc, char **argv)
 {
+       BirthdayData *data;
        osso_context_t *osso_context;
        EBook *ebook;
-       EBookQuery *query;
-       GError *error = NULL;
        GtkWidget *window;
        GtkListStore *store;
        GList *contacts;
 
        hildon_gtk_init (&argc, &argv);
 
+       /* create application data */
+       data = g_new0 (BirthdayData, 1);
+       data->searched_name = NULL;
+       data->found = TRUE;
+       data->n_contacts = 0;
+
        /* initialize localization */
        setlocale(LC_ALL, "");
        bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
@@ -318,31 +719,13 @@ int main (int argc, char **argv)
                goto exit;
        }
 
-       ebook = e_book_new_system_addressbook (&error);
-       if (!ebook) {
-               g_warning ("Error opening system address book: %s", error->message);
-               g_error_free (error);
-               goto exit;
-       }
-
-       if (!e_book_open (ebook, TRUE, &error)) {
-               g_warning ("Error opening system address book: %s", error->message);
-               g_error_free (error);
-               goto exit;
-       }
-
-       query = e_book_query_any_field_contains ("");
-
-       if (!e_book_get_contacts (ebook, query, &contacts, &error)) {
-               g_warning ("Error getting contacts: %s", error->message);
-               g_error_free (error);
-               goto exit;
-       }
+       contacts = get_all_contacts (ebook);
+       store = create_bday_liststore (data, contacts);
 
-       store = create_bday_liststore(contacts);
-       window = create_main_window(store);
+       /* create main widow */
+       create_main_window (data, store);
 
-       gtk_main();
+       gtk_main ();
 
 exit:
        osso_deinitialize (osso_context);