diff --git a/data/calendar.gresource.xml b/data/calendar.gresource.xml index cf2c3d31..07b7c87a 100644 --- a/data/calendar.gresource.xml +++ b/data/calendar.gresource.xml @@ -2,6 +2,7 @@ ui/alarm-row.ui + ui/calendar-popover.ui ui/calendar-row.ui ui/date-chooser.ui ui/date-selector.ui diff --git a/data/ui/calendar-popover.ui b/data/ui/calendar-popover.ui new file mode 100644 index 00000000..2d2b755e --- /dev/null +++ b/data/ui/calendar-popover.ui @@ -0,0 +1,51 @@ + + + + diff --git a/data/ui/window.ui b/data/ui/window.ui index 4e9f247c..d4dcb8d0 100644 --- a/data/ui/window.ui +++ b/data/ui/window.ui @@ -271,54 +271,7 @@ - - 6 - - - True - vertical - 6 - - - True - never - 450 - True - - - True - True - none - - - - - - - - - True - True - none - True - win.show-calendars - - - True - Calendar Settings - 0 - - - - - 1 - - - - - + False views_stack diff --git a/src/gcal-calendar-popover.c b/src/gcal-calendar-popover.c new file mode 100644 index 00000000..2e2a1343 --- /dev/null +++ b/src/gcal-calendar-popover.c @@ -0,0 +1,308 @@ +/* gcal-calendar-popover.c + * + * Copyright 2019 Georges Basile Stavracas Neto + * + * 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 3 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "GcalCalendarPopover" + +#include "gcal-calendar.h" +#include "gcal-calendar-popover.h" +#include "gcal-context.h" +#include "gcal-utils.h" + +struct _GcalCalendarPopover +{ + GtkPopover parent; + + GtkWidget *calendar_listbox; + + GcalContext *context; +}; + +G_DEFINE_TYPE (GcalCalendarPopover, gcal_calendar_popover, GTK_TYPE_POPOVER) + +enum +{ + PROP_0, + PROP_CONTEXT, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + + +/* + * Auxiliary methods + */ + +static GtkWidget* +make_calendar_row (GcalCalendar *calendar) +{ + GtkWidget *label, *icon, *checkbox, *box, *row; + GtkStyleContext *context; + cairo_surface_t *surface; + const GdkRGBA *color; + + row = gtk_list_box_row_new (); + + /* apply some nice styling */ + context = gtk_widget_get_style_context (row); + gtk_style_context_add_class (context, "button"); + gtk_style_context_add_class (context, "flat"); + gtk_style_context_add_class (context, "menuitem"); + + /* main box */ + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (box), 6); + + /* source color icon */ + color = gcal_calendar_get_color (calendar); + surface = get_circle_surface_from_color (color, 16); + icon = gtk_image_new_from_surface (surface); + + gtk_style_context_add_class (gtk_widget_get_style_context (icon), "calendar-color-image"); + + /* source name label */ + label = gtk_label_new (gcal_calendar_get_name (calendar)); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_widget_set_hexpand (label, TRUE); + + /* checkbox */ + checkbox = gtk_check_button_new (); + g_object_bind_property (calendar, + "visible", + checkbox, + "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + gtk_container_add (GTK_CONTAINER (box), icon); + gtk_container_add (GTK_CONTAINER (box), label); + gtk_container_add (GTK_CONTAINER (box), checkbox); + gtk_container_add (GTK_CONTAINER (row), box); + + g_object_set_data (G_OBJECT (row), "check", checkbox); + g_object_set_data (G_OBJECT (row), "calendar", calendar); + + gtk_widget_show_all (row); + + g_clear_pointer (&surface, cairo_surface_destroy); + + return row; +} + +static void +add_calendar (GcalCalendarPopover *self, + GcalCalendar *calendar) +{ + GtkWidget *row; + + row = make_calendar_row (calendar); + gtk_container_add (GTK_CONTAINER (self->calendar_listbox), row); +} + +static void +remove_calendar (GcalCalendarPopover *self, + GcalCalendar *calendar) +{ + g_autoptr (GList) children = NULL; + GList *aux; + + children = gtk_container_get_children (GTK_CONTAINER (self->calendar_listbox)); + + for (aux = children; aux != NULL; aux = aux->next) + { + GcalCalendar *row_calendar = g_object_get_data (G_OBJECT (aux->data), "calendar"); + + if (row_calendar && row_calendar == calendar) + { + gtk_widget_destroy (aux->data); + break; + } + } +} + + +/* + * Callbacks + */ + +static gint +listbox_sort_func (GtkListBoxRow *row1, + GtkListBoxRow *row2, + gpointer user_data) +{ + GcalCalendar *calendar1, *calendar2; + + calendar1 = g_object_get_data (G_OBJECT (row1), "calendar"); + calendar2 = g_object_get_data (G_OBJECT (row2), "calendar"); + + return g_ascii_strcasecmp (gcal_calendar_get_name (calendar1), gcal_calendar_get_name (calendar2)); +} + +static void +on_manager_calendar_added_cb (GcalManager *manager, + GcalCalendar *calendar, + GcalCalendarPopover *self) +{ + add_calendar (self, calendar); +} + +static void +on_manager_calendar_changed_cb (GcalManager *manager, + GcalCalendar *calendar, + GcalCalendarPopover *self) +{ + remove_calendar (self, calendar); + add_calendar (self, calendar); +} + +static void +on_manager_calendar_removed_cb (GcalManager *manager, + GcalCalendar *calendar, + GcalCalendarPopover *self) +{ + remove_calendar (self, calendar); +} + +static void +on_listbox_row_activated_cb (GtkListBox *listbox, + GtkListBoxRow *row, + GcalCalendarPopover *self) +{ + GtkToggleButton *check; + + check = (GtkToggleButton *) g_object_get_data (G_OBJECT (row), "check"); + + gtk_toggle_button_set_active (check, !gtk_toggle_button_get_active (check)); +} + + +/* + * GObject overrides + */ + +static void +gcal_calendar_popover_finalize (GObject *object) +{ + GcalCalendarPopover *self = (GcalCalendarPopover *)object; + + g_clear_object (&self->context); + + G_OBJECT_CLASS (gcal_calendar_popover_parent_class)->finalize (object); +} + +static void +gcal_calendar_popover_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcalCalendarPopover *self = GCAL_CALENDAR_POPOVER (object); + + switch (prop_id) + { + case PROP_CONTEXT: + g_value_set_object (value, self->context); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_calendar_popover_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcalCalendarPopover *self = GCAL_CALENDAR_POPOVER (object); + + switch (prop_id) + { + case PROP_CONTEXT: + { + GcalManager *manager; + + g_assert (self->context == NULL); + self->context = g_value_dup_object (value); + + manager = gcal_context_get_manager (self->context); + if (!gcal_manager_get_loading (manager)) + { + g_autoptr (GList) calendars = NULL; + GList *l; + + calendars = gcal_manager_get_calendars (manager); + + for (l = calendars; l; l = l->next) + add_calendar (self, l->data); + } + + g_signal_connect (manager, "calendar-added", G_CALLBACK (on_manager_calendar_added_cb), object); + g_signal_connect (manager, "calendar-removed", G_CALLBACK (on_manager_calendar_removed_cb), object); + g_signal_connect (manager, "calendar-changed", G_CALLBACK (on_manager_calendar_changed_cb), object); + + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_calendar_popover_class_init (GcalCalendarPopoverClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = gcal_calendar_popover_finalize; + object_class->get_property = gcal_calendar_popover_get_property; + object_class->set_property = gcal_calendar_popover_set_property; + + /** + * GcalCalendarPopover::context: + * + * The #GcalContext of the application. + */ + properties[PROP_CONTEXT] = g_param_spec_object ("context", + "Context of the application", + "The context of the application", + GCAL_TYPE_CONTEXT, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/calendar-popover.ui"); + + gtk_widget_class_bind_template_child (widget_class, GcalCalendarPopover, calendar_listbox); + + gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated_cb); +} + +static void +gcal_calendar_popover_init (GcalCalendarPopover *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_list_box_set_sort_func (GTK_LIST_BOX (self->calendar_listbox), + (GtkListBoxSortFunc) listbox_sort_func, + self, + NULL); +} diff --git a/src/gcal-calendar-popover.h b/src/gcal-calendar-popover.h new file mode 100644 index 00000000..1b80ed37 --- /dev/null +++ b/src/gcal-calendar-popover.h @@ -0,0 +1,30 @@ +/* gcal-calendar-popover.h + * + * Copyright 2019 Georges Basile Stavracas Neto + * + * 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 3 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define GCAL_TYPE_CALENDAR_POPOVER (gcal_calendar_popover_get_type()) +G_DECLARE_FINAL_TYPE (GcalCalendarPopover, gcal_calendar_popover, GCAL, CALENDAR_POPOVER, GtkPopover) + +G_END_DECLS diff --git a/src/gcal-window.c b/src/gcal-window.c index 7bc8d78a..45687b41 100644 --- a/src/gcal-window.c +++ b/src/gcal-window.c @@ -19,6 +19,7 @@ #define G_LOG_DOMAIN "GcalWindow" +#include "gcal-calendar-popover.h" #include "gcal-debug.h" #include "gcal-edit-dialog.h" #include "gcal-event-widget.h" @@ -53,17 +54,6 @@ * and it responds to the #GcalView:create-event signal by positioning * the quick add popover at the requested position. * - * ## Calendar popover - * - * ![The popover](gcal-window_agendas.png) - * - * The calendar popover enables/disables the selected calendars. - * This is simply an UI for gcal_manager_enable_source() and - * gcal_manager_disable_source(). - * - * The calendar popover also contains a button to open the source - * dialog. - * * ## Edit dialog * * When an event is clicked, the views send the `event-activated` @@ -150,7 +140,6 @@ struct _GcalWindow /* calendar management */ GtkWidget *calendar_popover; - GtkWidget *calendar_listbox; GtkWidget *source_dialog; gint open_edit_dialog_timeout_id; @@ -443,22 +432,6 @@ on_view_action_activated (GSimpleAction *action, g_object_notify_by_pspec (G_OBJECT (user_data), properties[PROP_ACTIVE_VIEW]); } -static gint -calendar_listbox_sort_func (GtkListBoxRow *row1, - GtkListBoxRow *row2, - gpointer user_data) -{ - ESource *source1, *source2; - - source1 = g_object_get_data (G_OBJECT (row1), "source"); - source2 = g_object_get_data (G_OBJECT (row2), "source"); - - if (source1 == NULL && source2 == NULL) - return 0; - - return g_ascii_strcasecmp (e_source_get_display_name (source1), e_source_get_display_name (source2)); -} - static void load_geometry (GcalWindow *self) { @@ -666,117 +639,6 @@ hide_notification_scheduled (gpointer window) return FALSE; } -static GtkWidget* -make_calendar_row (GcalWindow *window, - GcalCalendar *calendar) -{ - GtkWidget *label, *icon, *checkbox, *box, *row; - GtkStyleContext *context; - cairo_surface_t *surface; - const GdkRGBA *color; - - row = gtk_list_box_row_new (); - - /* apply some nice styling */ - context = gtk_widget_get_style_context (row); - gtk_style_context_add_class (context, "button"); - gtk_style_context_add_class (context, "flat"); - gtk_style_context_add_class (context, "menuitem"); - - /* main box */ - box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); - gtk_container_set_border_width (GTK_CONTAINER (box), 6); - - /* source color icon */ - color = gcal_calendar_get_color (calendar); - surface = get_circle_surface_from_color (color, 16); - icon = gtk_image_new_from_surface (surface); - - gtk_style_context_add_class (gtk_widget_get_style_context (icon), "calendar-color-image"); - - /* source name label */ - label = gtk_label_new (gcal_calendar_get_name (calendar)); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_widget_set_hexpand (label, TRUE); - - /* checkbox */ - checkbox = gtk_check_button_new (); - g_object_bind_property (calendar, - "visible", - checkbox, - "active", - G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); - - gtk_container_add (GTK_CONTAINER (box), icon); - gtk_container_add (GTK_CONTAINER (box), label); - gtk_container_add (GTK_CONTAINER (box), checkbox); - gtk_container_add (GTK_CONTAINER (row), box); - - g_object_set_data (G_OBJECT (row), "check", checkbox); - g_object_set_data (G_OBJECT (row), "calendar", calendar); - - gtk_widget_show_all (row); - - g_clear_pointer (&surface, cairo_surface_destroy); - - return row; -} - -static void -add_calendar (GcalManager *manager, - GcalCalendar *calendar, - GcalWindow *self) -{ - GtkWidget *row; - - row = make_calendar_row (self, calendar); - gtk_container_add (GTK_CONTAINER (self->calendar_listbox), row); -} - -static void -remove_calendar (GcalManager *manager, - GcalCalendar *calendar, - GcalWindow *self) -{ - GList *children, *aux; - - children = gtk_container_get_children (GTK_CONTAINER (self->calendar_listbox)); - - for (aux = children; aux != NULL; aux = aux->next) - { - GcalCalendar *row_calendar = g_object_get_data (G_OBJECT (aux->data), "calendar"); - - if (row_calendar && row_calendar == calendar) - { - gtk_widget_destroy (aux->data); - break; - } - } - - g_list_free (children); -} - -static void -source_row_activated (GtkListBox *listbox, - GtkListBoxRow *row, - gpointer user_data) -{ - GtkWidget *check; - - check = g_object_get_data (G_OBJECT (row), "check"); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), - !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check))); -} - -static void -calendar_changed (GcalWindow *window, - GcalCalendar *calendar) -{ - remove_calendar (NULL, calendar, window); - add_calendar (NULL, calendar, window); -} - static gboolean window_state_changed (GtkWidget *widget, GdkEvent *event, @@ -1066,6 +928,7 @@ gcal_window_constructed (GObject *object) * FIXME: this is a hack around the issue that happens when trying to bind * these properties using the GtkBuilder .ui file. */ + g_object_bind_property (self, "context", self->calendar_popover, "context", G_BINDING_DEFAULT); g_object_bind_property (self, "context", self->weather_settings, "context", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); g_object_bind_property (self, "context", self->source_dialog, "context", G_BINDING_DEFAULT); g_object_bind_property (self, "context", self->week_view, "context", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); @@ -1107,29 +970,12 @@ gcal_window_set_property (GObject *object, case PROP_CONTEXT: if (g_set_object (&self->context, g_value_get_object (value))) { - GcalManager *manager = gcal_context_get_manager (self->context); - g_settings_bind (gcal_context_get_settings (self->context), "active-view", self, "active-view", G_SETTINGS_BIND_SET | G_SETTINGS_BIND_GET); - if (!gcal_manager_get_loading (manager)) - { - g_autoptr (GList) calendars = NULL; - GList *l; - - calendars = gcal_manager_get_calendars (manager); - - for (l = calendars; l; l = l->next) - add_calendar (manager, l->data, self); - } - - g_signal_connect (manager, "calendar-added", G_CALLBACK (add_calendar), object); - g_signal_connect (manager, "calendar-removed", G_CALLBACK (remove_calendar), object); - g_signal_connect_swapped (manager, "calendar-changed", G_CALLBACK (calendar_changed), object); - g_object_notify_by_pspec (object, properties[PROP_CONTEXT]); } break; @@ -1200,6 +1046,7 @@ gcal_window_class_init (GcalWindowClass *klass) GObjectClass *object_class; GtkWidgetClass *widget_class; + g_type_ensure (GCAL_TYPE_CALENDAR_POPOVER); g_type_ensure (GCAL_TYPE_EDIT_DIALOG); g_type_ensure (GCAL_TYPE_MANAGER); g_type_ensure (GCAL_TYPE_MONTH_VIEW); @@ -1252,7 +1099,7 @@ gcal_window_class_init (GcalWindowClass *klass) /* widgets */ gtk_widget_class_bind_template_child (widget_class, GcalWindow, back_button); gtk_widget_class_bind_template_child (widget_class, GcalWindow, calendars_button); - gtk_widget_class_bind_template_child (widget_class, GcalWindow, calendar_listbox); + gtk_widget_class_bind_template_child (widget_class, GcalWindow, calendar_popover); gtk_widget_class_bind_template_child (widget_class, GcalWindow, edit_dialog); gtk_widget_class_bind_template_child (widget_class, GcalWindow, forward_button); @@ -1276,8 +1123,6 @@ gcal_window_class_init (GcalWindowClass *klass) gtk_widget_class_bind_template_child (widget_class, GcalWindow, notification_action_button); gtk_widget_class_bind_template_child (widget_class, GcalWindow, notification_close_button); - gtk_widget_class_bind_template_callback (widget_class, source_row_activated); - gtk_widget_class_bind_template_callback (widget_class, view_changed); gtk_widget_class_bind_template_callback (widget_class, date_updated); @@ -1317,12 +1162,6 @@ gcal_window_init (GcalWindow *self) self->views[GCAL_WINDOW_VIEW_MONTH] = self->month_view; self->views[GCAL_WINDOW_VIEW_YEAR] = self->year_view; - /* calendars popover */ - gtk_list_box_set_sort_func (GTK_LIST_BOX (self->calendar_listbox), - (GtkListBoxSortFunc) calendar_listbox_sort_func, - self, - NULL); - self->active_date = g_date_time_new_from_unix_local (0); self->rtl = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL; diff --git a/src/meson.build b/src/meson.build index 16e8b332..5994df2f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -47,6 +47,7 @@ sources = files( 'weather/gcal-weather-service.c', 'weather/gcal-weather-settings.c', 'gcal-application.c', + 'gcal-calendar-popover.c', 'gcal-context.c', 'gcal-date-chooser-day.c', 'gcal-date-chooser.c',