diff --git a/src/Makefile.am b/src/Makefile.am index 077106cb..c5374b40 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,6 +39,8 @@ gnome_calendar_SOURCES = \ gcal-week-view.h \ gcal-year-view.c \ gcal-year-view.h \ + gcal-search-view.h \ + gcal-search-view.c \ gcal-event-widget.c \ gcal-event-widget.h \ gcal-new-event-widget.c \ diff --git a/src/gcal-manager.c b/src/gcal-manager.c index 6cb665c8..13ecbd65 100644 --- a/src/gcal-manager.c +++ b/src/gcal-manager.c @@ -506,6 +506,26 @@ gcal_manager_set_subscriber (GcalManager *manager, range_start, range_end); } +/** + * gcal_manager_set_query: + * @manager: A #GcalManager instance + * @query: (nullable): query terms or %NULL + * + * Set the query terms of the #ECalDataModel or clear it if %NULL is + * passed + * + **/ +void +gcal_manager_set_query (GcalManager *manager, + const gchar *query) +{ + GcalManagerPrivate *priv; + + priv = gcal_manager_get_instance_private (manager); + e_cal_data_model_set_filter (priv->e_data_model, + query != NULL ? query : "#t"); +} + /** * gcal_manager_add_source: * @manager: a #GcalManager diff --git a/src/gcal-manager.h b/src/gcal-manager.h index d38f307a..51375137 100644 --- a/src/gcal-manager.h +++ b/src/gcal-manager.h @@ -71,6 +71,9 @@ void gcal_manager_set_subscriber (GcalManager *manager time_t range_start, time_t range_end); +void gcal_manager_set_query (GcalManager *manager, + const gchar *query); + gchar* gcal_manager_add_source (GcalManager *manager, const gchar *name, const gchar *backend, diff --git a/src/gcal-search-view.c b/src/gcal-search-view.c new file mode 100644 index 00000000..7178d4c5 --- /dev/null +++ b/src/gcal-search-view.c @@ -0,0 +1,315 @@ +/* -*- mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * gcal-search-view.c + * + * Copyright (C) 2014 - Erick Pérez Castellanos + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gcal-search-view.h" + +#include "gcal-event-widget.h" +#include "gcal-utils.h" + +#include + +typedef struct +{ + GtkWidget *listbox; + + GtkSizeGroup *desc_size_group; + + /* property */ + icaltimetype *date; + GcalManager *manager; /* weak reference */ +} GcalSearchViewPrivate; + +enum +{ + PROP_0, + PROP_DATE, /* active-date inherited property */ + PROP_MANAGER /* manager inherited property */ +}; + +static void gcal_data_model_subscriber_interface_init (ECalDataModelSubscriberInterface *iface); + +static void gcal_search_view_constructed (GObject *object); + +static void gcal_search_view_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void gcal_search_view_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gcal_search_view_finalize (GObject *object); + +static void gcal_search_view_component_added (ECalDataModelSubscriber *subscriber, + ECalClient *client, + ECalComponent *comp); + +static void gcal_search_view_component_modified (ECalDataModelSubscriber *subscriber, + ECalClient *client, + ECalComponent *comp); + +static void gcal_search_view_component_removed (ECalDataModelSubscriber *subscriber, + ECalClient *client, + const gchar *uid, + const gchar *rid); + +static void gcal_search_view_freeze (ECalDataModelSubscriber *subscriber); + +static void gcal_search_view_thaw (ECalDataModelSubscriber *subscriber); + +G_DEFINE_TYPE_WITH_CODE (GcalSearchView, + gcal_search_view, + GTK_TYPE_SCROLLED_WINDOW, + G_ADD_PRIVATE (GcalSearchView) + G_IMPLEMENT_INTERFACE (E_TYPE_CAL_DATA_MODEL_SUBSCRIBER, + gcal_data_model_subscriber_interface_init)); + +static void +gcal_search_view_class_init (GcalSearchViewClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->constructed = gcal_search_view_constructed; + object_class->set_property = gcal_search_view_set_property; + object_class->get_property = gcal_search_view_get_property; + object_class->finalize = gcal_search_view_finalize; + + g_object_class_install_property ( + object_class, + PROP_DATE, + g_param_spec_boxed ("active-date", + "The active date", + "The active/selecetd date in the view", + ICAL_TIME_TYPE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_MANAGER, + g_param_spec_pointer ("manager", + "The manager object", + "A weak reference to the app manager object", + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE)); +} + +static void +gcal_search_view_init (GcalSearchView *self) +{ +} + +static void +gcal_data_model_subscriber_interface_init (ECalDataModelSubscriberInterface *iface) +{ + iface->component_added = gcal_search_view_component_added; + iface->component_modified = gcal_search_view_component_modified; + iface->component_removed = gcal_search_view_component_removed; + iface->freeze = gcal_search_view_freeze; + iface->thaw = gcal_search_view_thaw; +} + +static void +gcal_search_view_constructed (GObject *object) +{ + GcalSearchViewPrivate *priv; + + priv = + gcal_search_view_get_instance_private (GCAL_SEARCH_VIEW (object)); + + priv->listbox = gtk_list_box_new (); + gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->listbox), + GTK_SELECTION_NONE); + gtk_widget_show (priv->listbox); + + gtk_container_add (GTK_CONTAINER (object), priv->listbox); + + priv->desc_size_group = + gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); +} + +static void +gcal_search_view_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GcalSearchViewPrivate *priv; + + priv = + gcal_search_view_get_instance_private (GCAL_SEARCH_VIEW (object)); + + switch (property_id) + { + case PROP_DATE: + { + time_t range_start, range_end; + icaltimetype *date; + icaltimezone* default_zone; + + if (priv->date != NULL) + g_free (priv->date); + + priv->date = g_value_dup_boxed (value); + + default_zone = + gcal_manager_get_system_timezone (priv->manager); + + /* FIXME: using fixed interval */ + date = gcal_dup_icaltime (priv->date); + icaltime_adjust (date, -25, 0, 0, 0); + range_start = icaltime_as_timet_with_zone (*date, + default_zone); + g_free (date); + date = gcal_dup_icaltime (priv->date); + icaltime_adjust (date, 25, 0, 0, 0); + range_end = icaltime_as_timet_with_zone (*date, + default_zone); + g_free (date); + + gcal_manager_set_subscriber (priv->manager, + E_CAL_DATA_MODEL_SUBSCRIBER (object), + range_start, + range_end); + break; + } + case PROP_MANAGER: + { + priv->manager = g_value_get_pointer (value); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gcal_search_view_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GcalSearchViewPrivate *priv; + + priv = gcal_search_view_get_instance_private (GCAL_SEARCH_VIEW (object)); + + switch (property_id) + { + case PROP_DATE: + g_value_set_boxed (value, priv->date); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gcal_search_view_finalize (GObject *object) +{ + GcalSearchViewPrivate *priv; + priv = gcal_search_view_get_instance_private (GCAL_SEARCH_VIEW (object)); + + if (priv->date != NULL) + g_free (priv->date); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (gcal_search_view_parent_class)->finalize (object); +} + +/* ECalDataModelSubscriber interface API */ +static void +gcal_search_view_component_added (ECalDataModelSubscriber *subscriber, + ECalClient *client, + ECalComponent *comp) +{ + GcalSearchViewPrivate *priv; + + GtkWidget *event; + GcalEventData *data; + + priv = + gcal_search_view_get_instance_private (GCAL_SEARCH_VIEW (subscriber)); + + data = g_new0 (GcalEventData, 1); + data->source = e_client_get_source (E_CLIENT (client)); + data->event_component = e_cal_component_clone (comp); + + event = gcal_event_widget_new_from_data (data); + g_free (data); + + gtk_widget_set_margin_start (event, 96); + gtk_widget_set_margin_end (event, 96); + gtk_widget_show (event); + gtk_container_add (GTK_CONTAINER (priv->listbox), event); +} + +static void +gcal_search_view_component_modified (ECalDataModelSubscriber *subscriber, + ECalClient *client, + ECalComponent *comp) +{ + ; +} + +static void +gcal_search_view_component_removed (ECalDataModelSubscriber *subscriber, + ECalClient *client, + const gchar *uid, + const gchar *rid) +{ + GcalSearchViewPrivate *priv; + + priv = + gcal_search_view_get_instance_private (GCAL_SEARCH_VIEW (subscriber)); + + gtk_container_foreach (GTK_CONTAINER (priv->listbox), + (GtkCallback) gtk_widget_destroy, NULL); +} + +static void +gcal_search_view_freeze (ECalDataModelSubscriber *subscriber) +{ + ; +} + +static void +gcal_search_view_thaw (ECalDataModelSubscriber *subscriber) +{ +} + +/* Public API */ +/** + * gcal_search_view_new: + * @manager: App singleton #GcalManager instance + * + * Since: 0.1 + * Create a new month view widget + * + * Returns: (transfer full): + **/ +GtkWidget* +gcal_search_view_new (GcalManager *manager) +{ + return g_object_new (GCAL_TYPE_SEARCH_VIEW, "manager", manager, NULL); +} diff --git a/src/gcal-search-view.h b/src/gcal-search-view.h new file mode 100644 index 00000000..3c2a62ca --- /dev/null +++ b/src/gcal-search-view.h @@ -0,0 +1,55 @@ +/* + * gcal-search-view.h + * + * Copyright (C) 2014 - Erick Pérez Castellanos + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GCAL_SEARCH_VIEW_H__ +#define __GCAL_SEARCH_VIEW_H__ + +#include "gcal-manager.h" + +#include + +G_BEGIN_DECLS + +#define GCAL_TYPE_SEARCH_VIEW (gcal_search_view_get_type ()) +#define GCAL_SEARCH_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GCAL_TYPE_SEARCH_VIEW, GcalSearchView)) +#define GCAL_SEARCH_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GCAL_TYPE_SEARCH_VIEW, GcalSearchViewClass)) +#define GCAL_IS_SEARCH_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GCAL_TYPE_SEARCH_VIEW)) +#define GCAL_IS_SEARCH_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GCAL_TYPE_SEARCH_VIEW)) +#define GCAL_SEARCH_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GCAL_TYPE_SEARCH_VIEW, GcalSearchViewClass)) + +typedef struct _GcalSearchView GcalSearchView; +typedef struct _GcalSearchViewClass GcalSearchViewClass; + +struct _GcalSearchView +{ + GtkScrolledWindow parent; +}; + +struct _GcalSearchViewClass +{ + GtkScrolledWindowClass parent_class; +}; + +GType gcal_search_view_get_type (void); + +GtkWidget* gcal_search_view_new (GcalManager *manager); + +G_END_DECLS + +#endif /* __GCAL_SEARCH_VIEW_H__ */ diff --git a/src/gcal-window.c b/src/gcal-window.c index e50323bb..a9298211 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -26,6 +26,7 @@ #include "gcal-month-view.h" #include "gcal-week-view.h" #include "gcal-year-view.h" +#include "gcal-search-view.h" #include "gcal-event-widget.h" #include "gcal-edit-dialog.h" #include "gcal-enum-types.h" @@ -70,7 +71,8 @@ typedef struct GtkWidget *search_entry; GtkWidget *views_switcher; - GtkWidget *views [5]; /* day, week, month, year, list */ + /* day, week, month, year, list, search */ + GtkWidget *views [6]; GtkWidget *edit_dialog; GcalManager *manager; @@ -156,6 +158,13 @@ static void edit_dialog_closed (GtkDialog *d gint response, gpointer user_data); +static void search_toggled (GObject *object, + GParamSpec *pspec, + gpointer user_data); + +static void search_changed (GtkEditable *editable, + gpointer user_data); + static void gcal_window_constructed (GObject *object); static void gcal_window_finalize (GObject *object); @@ -176,13 +185,6 @@ static gboolean gcal_window_configure_event (GtkWidget *w static gboolean gcal_window_state_event (GtkWidget *widget, GdkEventWindowState *event); -static void gcal_window_search_toggled (GObject *object, - GParamSpec *pspec, - gpointer user_data); - -static void gcal_window_search_changed (GtkEditable *editable, - gpointer user_data); - /* GcalManager signal handling */ static void gcal_window_event_created (GcalManager *manager, const gchar *source_uid, @@ -468,6 +470,9 @@ view_changed (GObject *object, g_type_class_unref (eklass); + if (view_type == GCAL_WINDOW_VIEW_SEARCH) + return; + priv->active_view = view_type; g_object_notify (G_OBJECT (user_data), "active-view"); @@ -810,6 +815,95 @@ edit_dialog_closed (GtkDialog *dialog, } } +static void +search_toggled (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GcalWindowPrivate *priv; + + priv = gcal_window_get_instance_private (GCAL_WINDOW (user_data)); + + if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (priv->search_bar))) + { + g_debug ("Entering search mode"); + + /* update header_bar widget */ + gtk_header_bar_set_custom_title (GTK_HEADER_BAR (priv->header_bar), + NULL); + gtk_widget_hide (priv->today_button); + gtk_widget_hide (priv->nav_bar); + + gtk_stack_add_named (GTK_STACK (priv->views_stack), + priv->views[GCAL_WINDOW_VIEW_SEARCH], + "search"); + + if (gtk_stack_get_visible_child (GTK_STACK (priv->views_stack)) != + priv->views[GCAL_WINDOW_VIEW_SEARCH]) + { + gtk_stack_set_visible_child (GTK_STACK (priv->views_stack), + priv->views[GCAL_WINDOW_VIEW_SEARCH]); + } + } + else + { + g_debug ("Leaving search mode"); + /* update header_bar */ + gtk_widget_show (priv->nav_bar); + gtk_widget_show (priv->today_button); + + gtk_stack_set_visible_child (GTK_STACK (priv->views_stack), + priv->views[priv->active_view]); + + gtk_container_remove (GTK_CONTAINER (priv->views_stack), + priv->views[GCAL_WINDOW_VIEW_SEARCH]); + + gtk_header_bar_set_custom_title (GTK_HEADER_BAR (priv->header_bar), + priv->views_switcher); + /* Reset manager filter */ + gcal_manager_set_query (priv->manager, NULL); + } +} + +static void +search_changed (GtkEditable *editable, + gpointer user_data) +{ + GcalWindowPrivate *priv; + + priv = gcal_window_get_instance_private (GCAL_WINDOW (user_data)); + + if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (priv->search_bar))) + { + + if (gtk_entry_get_text_length (GTK_ENTRY (priv->search_entry)) != 0) + { + gchar *title; + gchar *query; + + title = + g_strdup_printf ( + "Results for \"%s\"", + gtk_entry_get_text (GTK_ENTRY (priv->search_entry))); + gtk_header_bar_set_title (GTK_HEADER_BAR (priv->header_bar), + title); + g_free (title); + + /**/ + query = + g_strdup_printf ("(contains? \"summary\" \"%s\")", + gtk_entry_get_text (GTK_ENTRY (priv->search_entry))); + gcal_manager_set_query (priv->manager, query); + g_free (query); + } + else + { + gtk_header_bar_set_title (GTK_HEADER_BAR (priv->header_bar), + ""); + } + } +} + static void gcal_window_class_init(GcalWindowClass *klass) { @@ -1010,28 +1104,33 @@ gcal_window_constructed (GObject *object) gtk_stack_add_titled (GTK_STACK (priv->views_stack), priv->views[GCAL_WINDOW_VIEW_WEEK], "week", _("Week")); - g_object_bind_property (GCAL_WINDOW (object), "active-date", - priv->views[GCAL_WINDOW_VIEW_WEEK], "active-date", - G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); priv->views[GCAL_WINDOW_VIEW_MONTH] = gcal_month_view_new (priv->manager); gtk_stack_add_titled (GTK_STACK (priv->views_stack), priv->views[GCAL_WINDOW_VIEW_MONTH], "month", _("Month")); - g_object_bind_property (GCAL_WINDOW (object), "active-date", - priv->views[GCAL_WINDOW_VIEW_MONTH], "active-date", - G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); priv->views[GCAL_WINDOW_VIEW_YEAR] = gcal_year_view_new (priv->manager); gtk_stack_add_titled (GTK_STACK (priv->views_stack), priv->views[GCAL_WINDOW_VIEW_YEAR], "year", _("Year")); + + priv->views[GCAL_WINDOW_VIEW_SEARCH] = + gcal_search_view_new (priv->manager); + g_object_ref_sink (priv->views[GCAL_WINDOW_VIEW_SEARCH]); + gtk_widget_show (priv->views[GCAL_WINDOW_VIEW_SEARCH]); + g_object_bind_property (GCAL_WINDOW (object), "active-date", - priv->views[GCAL_WINDOW_VIEW_YEAR], "active-date", + priv->views[GCAL_WINDOW_VIEW_SEARCH], + "active-date", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + g_signal_connect_swapped (priv->views_stack, "destroy", + G_CALLBACK (gtk_widget_destroy), + priv->views[GCAL_WINDOW_VIEW_SEARCH]); + gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (priv->views_switcher), GTK_STACK (priv->views_stack)); @@ -1054,10 +1153,16 @@ gcal_window_constructed (GObject *object) g_signal_connect (object, "key-press-event", G_CALLBACK (key_pressed), object); + /* only GcalView implementations */ for (i = 0; i < 4; ++i) { if (priv->views[i] != NULL) { + g_object_bind_property (GCAL_WINDOW (object), "active-date", + priv->views[i], "active-date", + G_BINDING_DEFAULT | + G_BINDING_SYNC_CREATE); + g_signal_connect (priv->views[i], "create-event", G_CALLBACK (show_new_event_widget), object); g_signal_connect (priv->views[i], "event-activated", @@ -1066,9 +1171,9 @@ gcal_window_constructed (GObject *object) } g_signal_connect (priv->search_bar, "notify::search-mode-enabled", - G_CALLBACK (gcal_window_search_toggled), object); - g_signal_connect (priv->search_entry, "changed", - G_CALLBACK (gcal_window_search_changed), object); + G_CALLBACK (search_toggled), object); + g_signal_connect (priv->search_entry, "search-changed", + G_CALLBACK (search_changed), object); g_signal_connect (priv->views_stack, "notify::visible-child", G_CALLBACK (view_changed), object); @@ -1092,6 +1197,12 @@ gcal_window_finalize (GObject *object) if (priv->active_date != NULL) g_free (priv->active_date); + if (priv->views_switcher != NULL) + g_object_unref (priv->views_switcher); + + if (priv->views[GCAL_WINDOW_VIEW_SEARCH] != NULL) + g_object_unref (priv->views[GCAL_WINDOW_VIEW_SEARCH]); + G_OBJECT_CLASS (gcal_window_parent_class)->finalize (object); } @@ -1209,68 +1320,6 @@ gcal_window_state_event (GtkWidget *widget, return retval; } -static void -gcal_window_search_toggled (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - GcalWindowPrivate *priv; - - priv = gcal_window_get_instance_private (GCAL_WINDOW (user_data)); - - if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (priv->search_bar))) - { - g_debug ("Entering search mode"); - - /* update headder_bar widget */ - gtk_widget_hide (priv->today_button); - gtk_header_bar_set_custom_title (GTK_HEADER_BAR (priv->header_bar), - NULL); - /* _prepare_for_search */ - } - else - { - g_debug ("Leaving search mode"); - /* update header_bar */ - gtk_widget_show (priv->today_button); - gtk_header_bar_set_custom_title (GTK_HEADER_BAR (priv->header_bar), - priv->views_switcher); - /* return to last active_view */ - gtk_stack_set_visible_child (GTK_STACK (priv->views_stack), - priv->views[priv->active_view]); - } -} - -static void -gcal_window_search_changed (GtkEditable *editable, - gpointer user_data) -{ - GcalWindowPrivate *priv; - - priv = gcal_window_get_instance_private (GCAL_WINDOW (user_data)); - - if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (priv->search_bar))) - { - gchar *title; - - if (gtk_entry_get_text_length (GTK_ENTRY (priv->search_entry)) != 0) - { - title = - g_strdup_printf ( - "Results for \"%s\"", - gtk_entry_get_text (GTK_ENTRY (priv->search_entry))); - gtk_header_bar_set_title (GTK_HEADER_BAR (priv->header_bar), - title); - g_free (title); - } - else - { - gtk_header_bar_set_title (GTK_HEADER_BAR (priv->header_bar), - ""); - } - } -} - static void gcal_window_event_created (GcalManager *manager, const gchar *source_uid, @@ -1453,6 +1502,7 @@ gcal_window_set_search_mode (GcalWindow *window, GcalWindowPrivate *priv; priv = gcal_window_get_instance_private (window); + priv->search_mode = enabled; gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->search_bar), enabled); }