From ad017d1709a323f743eb4704c85759e170479f69 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sun, 2 Jun 2019 12:03:32 -0300 Subject: [PATCH] calendars-page: Introduce GcalCalendarsPage A page dedicated to show a list of calendars and add new ones. Other pages will come in soon. --- data/calendar.gresource.xml | 1 + data/meson.build | 1 + data/ui/calendar-management-dialog.ui | 182 ------- data/ui/calendars-page.ui | 120 +++++ .../gcal-calendar-management-dialog.c | 447 ++-------------- .../calendar-management/gcal-calendars-page.c | 501 ++++++++++++++++++ .../calendar-management/gcal-calendars-page.h | 30 ++ src/meson.build | 1 + 8 files changed, 697 insertions(+), 586 deletions(-) create mode 100644 data/ui/calendars-page.ui create mode 100644 src/gui/calendar-management/gcal-calendars-page.c create mode 100644 src/gui/calendar-management/gcal-calendars-page.h diff --git a/data/calendar.gresource.xml b/data/calendar.gresource.xml index aefd806b..74331d7e 100644 --- a/data/calendar.gresource.xml +++ b/data/calendar.gresource.xml @@ -5,6 +5,7 @@ ui/calendar-management-dialog.ui ui/calendar-popover.ui ui/calendar-row.ui + ui/calendars-page.ui ui/date-chooser.ui ui/date-selector.ui ui/edit-dialog.ui diff --git a/data/meson.build b/data/meson.build index aeb3506b..5befb3e0 100644 --- a/data/meson.build +++ b/data/meson.build @@ -85,6 +85,7 @@ resource_data = files( 'ui/alarm-row.ui', 'ui/calendar-management-dialog.ui', 'ui/calendar-row.ui', + 'ui/calendars-page.ui', 'ui/date-chooser.ui', 'ui/date-selector.ui', 'ui/edit-dialog.ui', diff --git a/data/ui/calendar-management-dialog.ui b/data/ui/calendar-management-dialog.ui index ee90d41b..26d9aff7 100644 --- a/data/ui/calendar-management-dialog.ui +++ b/data/ui/calendar-management-dialog.ui @@ -20,84 +20,6 @@ True - - - True - center - start - 100 - - - - True - - - True - 12 - 12 - 2 - 2 - 12 - - - True - True - - - 0 - 0 - - - - - Undo - True - True - True - True - - - - - 1 - 0 - - - - - True - True - True - none - False - - - - True - window-close-symbolic - 2 - - - - - - 2 - 0 - - - - - - - - - True @@ -106,110 +28,6 @@ False crossfade - - - True - True - never - - - True - none - - - True - True - True - 18 - vertical - 18 - - - True - 12 - 12 - - - True - True - Calendars - 0 - - - - - - 0 - 0 - - - - - True - - - True - none - - - - - - - 0 - 1 - 2 - - - - - True - True - True - - - True - 6 - - - True - Add - - - - - True - pan-down-symbolic - - - 1 - - - - - - - 1 - 0 - - - - - 1 - - - - - - - - - main - Overview - - True diff --git a/data/ui/calendars-page.ui b/data/ui/calendars-page.ui new file mode 100644 index 00000000..6076696e --- /dev/null +++ b/data/ui/calendars-page.ui @@ -0,0 +1,120 @@ + + + + + + + vertical + + + + + diff --git a/src/gui/calendar-management/gcal-calendar-management-dialog.c b/src/gui/calendar-management/gcal-calendar-management-dialog.c index 4bcde41e..37521d36 100644 --- a/src/gui/calendar-management/gcal-calendar-management-dialog.c +++ b/src/gui/calendar-management/gcal-calendar-management-dialog.c @@ -21,6 +21,8 @@ #include "gcal-context.h" #include "gcal-debug.h" #include "gcal-calendar-management-dialog.h" +#include "gcal-calendar-management-page.h" +#include "gcal-calendars-page.h" #include "gcal-utils.h" #include @@ -41,6 +43,12 @@ * online accounts. */ +typedef enum +{ + GCAL_PAGE_CALENDARS, + N_PAGES, +} GcalPageType; + struct _GcalCalendarManagementDialog { GtkDialog parent; @@ -53,17 +61,12 @@ struct _GcalCalendarManagementDialog GtkWidget *default_check; GtkWidget *edit_grid; GtkWidget *headerbar; - GtkWidget *main_scrolledwindow; GtkWidget *name_entry; GtkWidget *notebook; GtkWidget *remove_button; GtkWidget *stack; GtkWidget *web_source_grid; - /* notification */ - GtkWidget *notification; - GtkWidget *notification_label; - /* edit page widgets */ GtkWidget *account_box; GtkWidget *account_label; @@ -88,10 +91,6 @@ struct _GcalCalendarManagementDialog gint validate_url_resource_id; gint notification_timeout_id; - /* overview widgets */ - GtkWidget *add_calendar_menu_button; - GtkWidget *calendars_listbox; - /* flags */ GcalCalendarManagementDialogMode mode; ESource *source; @@ -104,6 +103,8 @@ struct _GcalCalendarManagementDialog /* auxiliary */ GSimpleActionGroup *action_group; + GcalCalendarManagementPage *pages[N_PAGES]; + GcalContext *context; }; @@ -129,14 +130,6 @@ static void back_button_clicked (GtkButton *b static void calendar_file_selected (GtkFileChooser *button, gpointer user_data); -static void calendar_listbox_row_activated (GtkListBox *box, - GtkListBoxRow *row, - gpointer user_data); - -static gint calendar_listbox_sort_func (GtkListBoxRow *row1, - GtkListBoxRow *row2, - gpointer user_data); - static void calendar_visible_check_toggled (GObject *object, GParamSpec *pspec, gpointer user_data); @@ -157,10 +150,6 @@ static gboolean description_label_link_activated (GtkWidget *w gchar *uri, gpointer user_data); -static void display_header_func (GtkListBoxRow *row, - GtkListBoxRow *before, - gpointer user_data); - static gboolean is_goa_source (GcalCalendarManagementDialog *dialog, ESource *source); @@ -184,10 +173,6 @@ static void response_signal (GtkDialog *di gint response_id, gpointer user_data); -static void stack_visible_child_name_changed (GObject *object, - GParamSpec *pspec, - gpointer user_data); - static void settings_button_clicked (GtkWidget *button, gpointer user_data); @@ -270,121 +255,6 @@ add_button_clicked (GtkWidget *button, } } -static void -source_color_changed (GcalCalendar *calendar, - GParamSpec *pspec, - GtkImage *icon) -{ - cairo_surface_t *surface; - const GdkRGBA *color; - - color = gcal_calendar_get_color (calendar); - surface = get_circle_surface_from_color (color, 24); - gtk_image_set_from_surface (GTK_IMAGE (icon), surface); - g_clear_pointer (&surface, cairo_surface_destroy); -} - -static GtkWidget* -make_calendar_row (GcalCalendarManagementDialog *dialog, - GcalCalendar *calendar) -{ - cairo_surface_t *surface; - const GdkRGBA *color; - GcalManager *manager; - GtkBuilder *builder; - GtkWidget *bottom_label; - GtkWidget *top_label; - GtkWidget *icon; - GtkWidget *row; - gchar *parent_name; - - manager = gcal_context_get_manager (dialog->context); - get_source_parent_name_color (manager, gcal_calendar_get_source (calendar), &parent_name, NULL); - - builder = gtk_builder_new_from_resource ("/org/gnome/calendar/calendar-row.ui"); - - /* - * Since we're destroying the builder instance before adding - * the row to the listbox, it should be referenced here so - * it isn't destroyed with the GtkBuilder. - */ - row = g_object_ref (GTK_WIDGET (gtk_builder_get_object (builder, "row"))); - - /* source color icon */ - color = gcal_calendar_get_color (calendar); - surface = get_circle_surface_from_color (color, 24); - icon = GTK_WIDGET (gtk_builder_get_object (builder, "icon")); - gtk_image_set_from_surface (GTK_IMAGE (icon), surface); - - /* source name label */ - top_label = GTK_WIDGET (gtk_builder_get_object (builder, "title")); - gtk_label_set_label (GTK_LABEL (top_label), gcal_calendar_get_name (calendar)); - g_object_bind_property (calendar, "name", top_label, "label", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); - g_signal_connect_swapped (calendar, - "notify::name", - G_CALLBACK (gtk_list_box_invalidate_sort), - dialog->calendars_listbox); - - g_signal_connect (calendar, "notify::color", G_CALLBACK (source_color_changed), icon); - - /* parent source name label */ - bottom_label = GTK_WIDGET (gtk_builder_get_object (builder, "subtitle")); - gtk_label_set_label (GTK_LABEL (bottom_label), parent_name); - - g_clear_pointer (&surface, cairo_surface_destroy); - g_object_unref (builder); - g_free (parent_name); - - return row; -} - -static void -add_calendar (GcalManager *manager, - GcalCalendar *calendar, - GcalCalendarManagementDialog *self) -{ - GList *children, *l; - gboolean contains_source; - - children = gtk_container_get_children (GTK_CONTAINER (self->calendars_listbox)); - contains_source = FALSE; - - for (l = children; l != NULL; l = l->next) - { - if (g_object_get_data (l->data, "calendar") == calendar) - { - contains_source = TRUE; - break; - } - } - - if (!contains_source) - { - GtkWidget *row; - ESource *source; - ESource *parent; - - source = gcal_calendar_get_source (calendar); - parent = gcal_manager_get_source (manager, e_source_get_parent (source)); - - row = make_calendar_row (self, calendar); - g_object_set_data (G_OBJECT (row), "source", source); - - if (e_source_has_extension (parent, E_SOURCE_EXTENSION_GOA)) - { - ESourceGoa *goa = e_source_get_extension (parent, E_SOURCE_EXTENSION_GOA); - - g_object_set_data (G_OBJECT (row), "account-id", (gpointer) e_source_goa_get_account_id (goa)); - } - - gtk_container_add (GTK_CONTAINER (self->calendars_listbox), row); - - g_object_unref (parent); - } - - g_list_free (children); -} - static void action_widget_activated (GtkWidget *widget, gpointer user_data) @@ -418,55 +288,6 @@ back_button_clicked (GtkButton *button, gcal_calendar_management_dialog_set_mode (GCAL_CALENDAR_MANAGEMENT_DIALOG (user_data), GCAL_CALENDAR_MANAGEMENT_MODE_NORMAL); } -static gint -calendar_listbox_sort_func (GtkListBoxRow *row1, - GtkListBoxRow *row2, - gpointer user_data) -{ - GcalCalendarManagementDialog *self; - GcalManager *manager; - ESource *source1, *source2; - gboolean is_goa1, is_goa2; - gint retval; - - self = GCAL_CALENDAR_MANAGEMENT_DIALOG (user_data); - manager = gcal_context_get_manager (self->context); - - /* first source */ - source1 = g_object_get_data (G_OBJECT (row1), "source"); - is_goa1 = is_goa_source (GCAL_CALENDAR_MANAGEMENT_DIALOG (user_data), source1); - - /* second source */ - source2 = g_object_get_data (G_OBJECT (row2), "source"); - is_goa2 = is_goa_source (GCAL_CALENDAR_MANAGEMENT_DIALOG (user_data), source2); - - if (is_goa1 == is_goa2) - { - gchar *parent_name1 = NULL; - gchar *parent_name2 = NULL; - - /* Retrieve parent names */ - get_source_parent_name_color (manager, source1, &parent_name1, NULL); - get_source_parent_name_color (manager, source2, &parent_name2, NULL); - - retval = g_strcmp0 (parent_name1, parent_name2); - - /* If they have the same parent names, compare by the source display names */ - if (retval == 0) - retval = g_ascii_strcasecmp (e_source_get_display_name (source1), e_source_get_display_name (source2)); - - g_free (parent_name1); - g_free (parent_name2); - } - else - { - /* If one is a GOA account and the other isn't, make the GOA one go first */ - retval = is_goa1 ? -1 : 1; - } - - return retval; -} - static void calendar_visible_check_toggled (GObject *object, GParamSpec *pspec, @@ -574,20 +395,6 @@ description_label_link_activated (GtkWidget *widget, return TRUE; } -static void -display_header_func (GtkListBoxRow *row, - GtkListBoxRow *before, - gpointer user_data) -{ - if (before != NULL) - { - GtkWidget *header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); - gtk_widget_show (header); - - gtk_list_box_row_set_header (row, header); - } -} - static gboolean is_goa_source (GcalCalendarManagementDialog *dialog, ESource *source) @@ -725,6 +532,7 @@ settings_button_clicked (GtkWidget *button, NULL); } +#if 0 static void stack_visible_child_name_changed (GObject *object, GParamSpec *pspec, @@ -867,6 +675,7 @@ stack_visible_child_name_changed (GObject *object, g_free (parent_name); } } +#endif /* calendar_path_to_name_suggestion: * @file: a calendar file reference. @@ -945,28 +754,6 @@ calendar_file_selected (GtkFileChooser *button, gtk_widget_set_sensitive (self->add_button, TRUE); } -static void -calendar_listbox_row_activated (GtkListBox *box, - GtkListBoxRow *row, - gpointer user_data) -{ - GcalCalendarManagementDialog *self = GCAL_CALENDAR_MANAGEMENT_DIALOG (user_data); - - g_assert (row != NULL); - - /* - * For non-GOA calendars, show the edit page - * directly. - */ - if (GTK_WIDGET (box) == self->calendars_listbox) - { - ESource *source = g_object_get_data (G_OBJECT (row), "source"); - - gcal_calendar_management_dialog_set_source (GCAL_CALENDAR_MANAGEMENT_DIALOG (user_data), source); - gcal_calendar_management_dialog_set_mode (GCAL_CALENDAR_MANAGEMENT_DIALOG (user_data), GCAL_CALENDAR_MANAGEMENT_MODE_EDIT); - } -} - static gboolean pulse_web_entry (GcalCalendarManagementDialog *dialog) { @@ -1496,147 +1283,7 @@ discover_sources_cb (GObject *source, g_slist_free_full (user_addresses, g_free); } -static void -remove_calendar (GcalManager *manager, - GcalCalendar *calendar, - GcalCalendarManagementDialog *self) -{ - GList *children, *aux; - - children = gtk_container_get_children (GTK_CONTAINER (self->calendars_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); -} - -/** - * notification_child_revealed_changed: - * - * Remove the source after the notification - * is hidden. - * - * Returns: - */ -static void -notification_child_revealed_changed (GtkWidget *notification, - GParamSpec *spec, - gpointer user_data) -{ - GcalCalendarManagementDialog *self = GCAL_CALENDAR_MANAGEMENT_DIALOG (user_data); - GcalManager *manager; - - if (gtk_revealer_get_child_revealed (GTK_REVEALER (notification))) - return; - - manager = gcal_context_get_manager (self->context); - - /* If we have any removed source, delete it */ - if (self->removed_calendar != NULL) - { - g_autoptr (GError) error = NULL; - ESource *removed_source; - - removed_source = gcal_calendar_get_source (self->removed_calendar); - - /* We don't really want to remove non-removable sources */ - if (!e_source_get_removable (removed_source)) - return; - - /* Enable the source again to remove it's name from disabled list */ - gcal_calendar_set_visible (self->removed_calendar, TRUE); - e_source_remove_sync (removed_source, NULL, &error); - - if (error != NULL) - { - g_warning ("[source-dialog] Error removing source: %s", error->message); - add_calendar (manager, self->removed_calendar, self); - } - } -} - -/** - * undo_remove_action: - * - * Readd the removed source. - * - * Returns: - */ -static void -undo_remove_action (GtkButton *button, - gpointer user_data) -{ - GcalCalendarManagementDialog *self = GCAL_CALENDAR_MANAGEMENT_DIALOG (user_data); - GcalManager *manager; - - manager = gcal_context_get_manager (self->context); - - /* if there's any set source, unremove it */ - if (self->removed_calendar != NULL) - { - gcal_calendar_set_visible (self->removed_calendar, TRUE); - add_calendar (manager, self->removed_calendar, self); - - /* - * Don't clear the pointer, since we don't - * want to erase the source at all. - */ - self->removed_calendar = NULL; - - /* Hide notification */ - gtk_revealer_set_reveal_child (GTK_REVEALER (self->notification), FALSE); - } -} - -/** - * hide_notification: - * - * Helper function that hides the - * notification, causing the removal - * of the source (when valid). - * - * Returns: - */ -static void -hide_notification (GcalCalendarManagementDialog *dialog, - GtkWidget *button) -{ - gtk_revealer_set_reveal_child (GTK_REVEALER (dialog->notification), FALSE); - dialog->notification_timeout_id = 0; -} - -/** - * hide_notification_scheduled: - * - * Limit the ammount of time that - * the notification is shown. - * - * Returns: %FALSE - */ -static gboolean -hide_notification_scheduled (gpointer dialog) -{ - hide_notification (GCAL_CALENDAR_MANAGEMENT_DIALOG (dialog), NULL); - return FALSE; -} - -/** - * remove_button_clicked: - * - * Trigger the source removal - * logic. - * - * Returns: - */ +#if 0 static void remove_button_clicked (GtkWidget *button, gpointer user_data) @@ -1687,6 +1334,7 @@ remove_button_clicked (GtkWidget *button, gcal_calendar_management_dialog_set_mode (GCAL_CALENDAR_MANAGEMENT_DIALOG (user_data), GCAL_CALENDAR_MANAGEMENT_MODE_NORMAL); } +#endif /* * Callbacks @@ -1695,16 +1343,32 @@ remove_button_clicked (GtkWidget *button, static void setup_context (GcalCalendarManagementDialog *self) { - GList *accounts = NULL; - GcalManager *manager; + const struct { + GcalPageType page_type; + GType gtype; + } pages[] = { + { GCAL_PAGE_CALENDARS, GCAL_TYPE_CALENDARS_PAGE }, + }; + gint i; GCAL_ENTRY; - manager = gcal_context_get_manager (self->context); - g_signal_connect (manager, "calendar-added", G_CALLBACK (add_calendar), self); - g_signal_connect (manager, "calendar-removed", G_CALLBACK (remove_calendar), self); + for (i = 0; i < G_N_ELEMENTS (pages); i++) + { + GcalCalendarManagementPage *page; - g_list_free_full (accounts, g_object_unref); + page = g_object_new (pages[i].gtype, + "context", self->context, + NULL); + gtk_widget_show (GTK_WIDGET (page)); + + gtk_stack_add_titled (GTK_STACK (self->stack), + GTK_WIDGET (page), + gcal_calendar_management_page_get_name (page), + gcal_calendar_management_page_get_title (page)); + + self->pages[pages[i].page_type] = page; + } GCAL_EXIT; } @@ -1713,8 +1377,6 @@ static void gcal_calendar_management_dialog_constructed (GObject *object) { GcalCalendarManagementDialog *self; - GtkBuilder *builder; - GMenuModel *menu; self = GCAL_CALENDAR_MANAGEMENT_DIALOG (object); @@ -1725,25 +1387,12 @@ gcal_calendar_management_dialog_constructed (GObject *object) g_object_set_data (G_OBJECT (self->remove_button), "response", GINT_TO_POINTER (GCAL_RESPONSE_REMOVE_SOURCE)); - /* Setup listbox header functions */ - gtk_list_box_set_header_func (GTK_LIST_BOX (self->calendars_listbox), display_header_func, NULL, NULL); - gtk_list_box_set_sort_func (GTK_LIST_BOX (self->calendars_listbox), (GtkListBoxSortFunc) calendar_listbox_sort_func, - object, NULL); - /* Action group */ self->action_group = g_simple_action_group_new (); gtk_widget_insert_action_group (GTK_WIDGET (object), "source", G_ACTION_GROUP (self->action_group)); g_action_map_add_action_entries (G_ACTION_MAP (self->action_group), actions, G_N_ELEMENTS (actions), object); - /* Load the "Add" button menu */ - builder = gtk_builder_new_from_resource ("/org/gnome/calendar/gtk/menus.ui"); - - menu = G_MENU_MODEL (gtk_builder_get_object (builder, "add-source-menu")); - gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (self->add_calendar_menu_button), menu); - - g_object_unref (builder); - /* setup titlebar */ gtk_window_set_titlebar (GTK_WINDOW (object), self->headerbar); } @@ -1822,13 +1471,11 @@ gcal_calendar_management_dialog_class_init (GcalCalendarManagementDialogClass *k gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, account_box); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, account_label); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, add_button); - gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, add_calendar_menu_button); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, back_button); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, calendar_address_entry); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, calendar_color_button); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, calendar_url_button); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, calendar_visible_check); - gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, calendars_listbox); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, cancel_button); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, credentials_cancel_button); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, credentials_connect_button); @@ -1839,10 +1486,7 @@ gcal_calendar_management_dialog_class_init (GcalCalendarManagementDialogClass *k gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, edit_grid); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, headerbar); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, location_dim_label); - gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, main_scrolledwindow); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, name_entry); - gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, notification); - gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, notification_label); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, remove_button); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, settings_button); gtk_widget_class_bind_template_child (widget_class, GcalCalendarManagementDialog, stack); @@ -1855,7 +1499,6 @@ gcal_calendar_management_dialog_class_init (GcalCalendarManagementDialogClass *k gtk_widget_class_bind_template_callback (widget_class, back_button_clicked); gtk_widget_class_bind_template_callback (widget_class, calendar_address_activated); gtk_widget_class_bind_template_callback (widget_class, calendar_file_selected); - gtk_widget_class_bind_template_callback (widget_class, calendar_listbox_row_activated); gtk_widget_class_bind_template_callback (widget_class, calendar_visible_check_toggled); gtk_widget_class_bind_template_callback (widget_class, cancel_button_clicked); gtk_widget_class_bind_template_callback (widget_class, credential_button_clicked); @@ -1863,14 +1506,10 @@ gcal_calendar_management_dialog_class_init (GcalCalendarManagementDialogClass *k gtk_widget_class_bind_template_callback (widget_class, color_set); gtk_widget_class_bind_template_callback (widget_class, default_check_toggled); gtk_widget_class_bind_template_callback (widget_class, description_label_link_activated); - gtk_widget_class_bind_template_callback (widget_class, hide_notification); gtk_widget_class_bind_template_callback (widget_class, name_entry_text_changed); - gtk_widget_class_bind_template_callback (widget_class, notification_child_revealed_changed); - gtk_widget_class_bind_template_callback (widget_class, remove_button_clicked); gtk_widget_class_bind_template_callback (widget_class, response_signal); gtk_widget_class_bind_template_callback (widget_class, settings_button_clicked); - gtk_widget_class_bind_template_callback (widget_class, stack_visible_child_name_changed); - gtk_widget_class_bind_template_callback (widget_class, undo_remove_action); + //gtk_widget_class_bind_template_callback (widget_class, stack_visible_child_name_changed); gtk_widget_class_bind_template_callback (widget_class, url_entry_text_changed); } @@ -1890,8 +1529,8 @@ gcal_calendar_management_dialog_init (GcalCalendarManagementDialog *self) * will be edited. */ void -gcal_calendar_management_dialog_set_mode (GcalCalendarManagementDialog *dialog, - GcalCalendarManagementDialogMode mode) +gcal_calendar_management_dialog_set_mode (GcalCalendarManagementDialog *dialog, + GcalCalendarManagementDialogMode mode) { GcalCalendarManagementDialogMode previous_mode = dialog->mode; @@ -1934,15 +1573,15 @@ gcal_calendar_management_dialog_set_mode (GcalCalendarManagementDialog *dialo gtk_header_bar_set_title (GTK_HEADER_BAR (dialog->headerbar), _("Calendar Settings")); gtk_header_bar_set_subtitle (GTK_HEADER_BAR (dialog->headerbar), NULL); - gtk_stack_set_visible_child (GTK_STACK (dialog->stack), dialog->main_scrolledwindow); + gtk_stack_set_visible_child (GTK_STACK (dialog->stack), GTK_WIDGET (dialog->pages[GCAL_PAGE_CALENDARS])); break; default: g_assert_not_reached (); } - if (previous_mode == mode) - stack_visible_child_name_changed (G_OBJECT (dialog->stack), NULL, dialog); + //if (previous_mode == mode) + // stack_visible_child_name_changed (G_OBJECT (dialog->stack), NULL, dialog); } /** @@ -1954,7 +1593,7 @@ gcal_calendar_management_dialog_set_mode (GcalCalendarManagementDialog *dialo */ void gcal_calendar_management_dialog_set_source (GcalCalendarManagementDialog *dialog, - ESource *source) + ESource *source) { g_return_if_fail (source && E_IS_SOURCE (source)); diff --git a/src/gui/calendar-management/gcal-calendars-page.c b/src/gui/calendar-management/gcal-calendars-page.c new file mode 100644 index 00000000..b4daebe7 --- /dev/null +++ b/src/gui/calendar-management/gcal-calendars-page.c @@ -0,0 +1,501 @@ +/* gcal-calendars-page.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 "GcalCalendarsPage" + +#include + +#include "gcal-context.h" +#include "gcal-calendar-management-page.h" +#include "gcal-calendars-page.h" +#include "gcal-debug.h" +#include "gcal-utils.h" + +struct _GcalCalendarsPage +{ + GtkBox parent; + + GtkListBoxRow *add_calendar_row; + GtkListBox *listbox; + GtkLabel *notification_label; + GtkRevealer *notification_revealer; + GtkSizeGroup *sizegroup; + + GcalCalendar *removed_calendar; + gint notification_timeout_id; + + GcalContext *context; +}; + +static void gcal_calendar_management_page_iface_init (GcalCalendarManagementPageInterface *iface); + +static void on_calendar_color_changed_cb (GcalCalendar *calendar, + GParamSpec *pspec, + GtkImage *icon); + +G_DEFINE_TYPE_WITH_CODE (GcalCalendarsPage, gcal_calendars_page, GTK_TYPE_BOX, + G_IMPLEMENT_INTERFACE (GCAL_TYPE_CALENDAR_MANAGEMENT_PAGE, + gcal_calendar_management_page_iface_init)) + +enum +{ + PROP_0, + PROP_CONTEXT, + N_PROPS +}; + + +/* + * Auxiliary methods + */ + +static GtkWidget* +make_calendar_row (GcalCalendarsPage *self, + GcalCalendar *calendar) +{ + g_autofree gchar *parent_name = NULL; + g_autoptr (GtkBuilder) builder = NULL; + cairo_surface_t *surface; + const GdkRGBA *color; + GcalManager *manager; + GtkWidget *bottom_label; + GtkWidget *top_label; + GtkWidget *icon; + GtkWidget *row; + + manager = gcal_context_get_manager (self->context); + get_source_parent_name_color (manager, gcal_calendar_get_source (calendar), &parent_name, NULL); + + builder = gtk_builder_new_from_resource ("/org/gnome/calendar/calendar-row.ui"); + + /* + * Since we're destroying the builder instance before adding + * the row to the listbox, it should be referenced here so + * it isn't destroyed with the GtkBuilder. + */ + row = g_object_ref (GTK_WIDGET (gtk_builder_get_object (builder, "row"))); + + /* source color icon */ + color = gcal_calendar_get_color (calendar); + surface = get_circle_surface_from_color (color, 24); + icon = GTK_WIDGET (gtk_builder_get_object (builder, "icon")); + gtk_image_set_from_surface (GTK_IMAGE (icon), surface); + + /* source name label */ + top_label = GTK_WIDGET (gtk_builder_get_object (builder, "title")); + gtk_label_set_label (GTK_LABEL (top_label), gcal_calendar_get_name (calendar)); + g_object_bind_property (calendar, "name", top_label, "label", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + g_signal_connect_swapped (calendar, + "notify::name", + G_CALLBACK (gtk_list_box_invalidate_sort), + self->listbox); + + g_signal_connect_object (calendar, + "notify::color", + G_CALLBACK (on_calendar_color_changed_cb), + icon, + 0); + + /* parent source name label */ + bottom_label = GTK_WIDGET (gtk_builder_get_object (builder, "subtitle")); + gtk_label_set_label (GTK_LABEL (bottom_label), parent_name); + + g_clear_pointer (&surface, cairo_surface_destroy); + + gtk_size_group_add_widget (self->sizegroup, row); + + return row; +} + +static void +add_calendar (GcalCalendarsPage *self, + GcalCalendar *calendar) +{ + g_autoptr (GList) children = NULL; + GtkWidget *row; + ESource *source; + GList *l; + + children = gtk_container_get_children (GTK_CONTAINER (self->listbox)); + + for (l = children; l; l = l->next) + { + if (g_object_get_data (l->data, "calendar") == calendar) + return; + } + + source = gcal_calendar_get_source (calendar); + + row = make_calendar_row (self, calendar); + g_object_set_data (G_OBJECT (row), "source", source); + g_object_set_data (G_OBJECT (row), "calendar", calendar); + gtk_container_add (GTK_CONTAINER (self->listbox), row); +} + + +static void +remove_calendar (GcalCalendarsPage *self, + GcalCalendar *calendar) +{ + g_autoptr (GList) children = NULL; + GList *aux; + + children = gtk_container_get_children (GTK_CONTAINER (self->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; + } + } +} + +static void +delete_calendar (GcalCalendarsPage *self, + GcalCalendar *calendar) +{ + g_autoptr (GError) error = NULL; + ESource *removed_source; + + g_assert (calendar != NULL); + + removed_source = gcal_calendar_get_source (self->removed_calendar); + + /* We don't really want to remove non-removable sources */ + if (!e_source_get_removable (removed_source)) + return; + + /* Enable the source again to remove it's name from disabled list */ + gcal_calendar_set_visible (self->removed_calendar, TRUE); + e_source_remove_sync (removed_source, NULL, &error); + + if (error != NULL) + { + g_warning ("[source-dialog] Error removing source: %s", error->message); + add_calendar (self, self->removed_calendar); + } + + gtk_revealer_set_reveal_child (self->notification_revealer, FALSE); + g_clear_handle_id (&self->notification_timeout_id, g_source_remove); +} + + +/* + * Callbacks + */ + +static gint +listbox_sort_func (GtkListBoxRow *row1, + GtkListBoxRow *row2, + gpointer user_data) +{ + g_autofree gchar *parent_name1 = NULL; + g_autofree gchar *parent_name2 = NULL; + GcalCalendarsPage *self; + GcalManager *manager; + ESource *source1; + ESource *source2; + gint retval; + + self = GCAL_CALENDARS_PAGE (user_data); + + /* Keep "Add Calendar" row always at the bottom */ + if (row1 == self->add_calendar_row) + return 1; + else if (row2 == self->add_calendar_row) + return -1; + + manager = gcal_context_get_manager (self->context); + + source1 = g_object_get_data (G_OBJECT (row1), "source"); + source2 = g_object_get_data (G_OBJECT (row2), "source"); + + retval = g_ascii_strcasecmp (e_source_get_display_name (source1), e_source_get_display_name (source2)); + + if (retval != 0) + return retval; + + get_source_parent_name_color (manager, source1, &parent_name1, NULL); + get_source_parent_name_color (manager, source2, &parent_name2, NULL); + + return g_strcmp0 (parent_name1, parent_name2); +} + +static gboolean +remove_calendar_after_delay_cb (gpointer data) +{ + GcalCalendarsPage *self = GCAL_CALENDARS_PAGE (data); + + g_assert (self->removed_calendar != NULL); + + delete_calendar (self, self->removed_calendar); + g_clear_object (&self->removed_calendar); + + gtk_revealer_set_reveal_child (self->notification_revealer, FALSE); + self->notification_timeout_id = 0; + + return G_SOURCE_REMOVE; +} + +static void +on_calendar_color_changed_cb (GcalCalendar *calendar, + GParamSpec *pspec, + GtkImage *icon) +{ + cairo_surface_t *surface; + const GdkRGBA *color; + + color = gcal_calendar_get_color (calendar); + surface = get_circle_surface_from_color (color, 24); + gtk_image_set_from_surface (GTK_IMAGE (icon), surface); + g_clear_pointer (&surface, cairo_surface_destroy); +} + +static void +on_close_notification_button_clicked_cb (GcalCalendarsPage *self, + GtkWidget *button) +{ + g_assert (self->removed_calendar != NULL); + + delete_calendar (self, self->removed_calendar); + g_clear_object (&self->removed_calendar); + + gtk_revealer_set_reveal_child (self->notification_revealer, FALSE); + g_clear_handle_id (&self->notification_timeout_id, g_source_remove); +} + +static void +on_listbox_row_activated_cb (GtkListBox *listbox, + GtkListBoxRow *row, + GcalCalendarsPage *self) +{ + GcalCalendarManagementPage *page = GCAL_CALENDAR_MANAGEMENT_PAGE (self); + + if (row == self->add_calendar_row) + { + gcal_calendar_management_page_switch_page (page, "new-calendar", NULL); + } + else + { + GcalCalendar *calendar = g_object_get_data (G_OBJECT (row), "calendar"); + + gcal_calendar_management_page_switch_page (page, "edit-calendar", calendar); + } +} + +static void +on_manager_calendar_added_cb (GcalManager *manager, + GcalCalendar *calendar, + GcalCalendarsPage *self) +{ + add_calendar (self, calendar); +} + +static void +on_manager_calendar_removed_cb (GcalManager *manager, + GcalCalendar *calendar, + GcalCalendarsPage *self) +{ + remove_calendar (self, calendar); +} + +static void +on_undo_remove_button_clicked_cb (GtkButton *button, + GcalCalendarsPage *self) +{ + if (!self->removed_calendar) + return; + + gcal_calendar_set_visible (self->removed_calendar, TRUE); + + add_calendar (self, self->removed_calendar); + g_clear_object (&self->removed_calendar); + + gtk_revealer_set_reveal_child (self->notification_revealer, FALSE); + g_clear_handle_id (&self->notification_timeout_id, g_source_remove); +} + + +/* + * GcalCalendarManagementPage iface + */ + +static const gchar* +gcal_calendars_page_get_name (GcalCalendarManagementPage *page) +{ + return "calendars"; +} + +static const gchar* +gcal_calendars_page_get_title (GcalCalendarManagementPage *page) +{ + return _("Manage Calendars"); +} + +static void +gcal_calendars_page_activate (GcalCalendarManagementPage *page, + gpointer page_data) +{ + g_autofree gchar *new_string = NULL; + GcalCalendarsPage *self; + GcalCalendar *calendar; + + GCAL_ENTRY; + + g_assert (!page_data || GCAL_IS_CALENDAR (page_data)); + + if (!page_data) + GCAL_RETURN (); + + self = GCAL_CALENDARS_PAGE (page); + calendar = GCAL_CALENDAR (page_data); + self->removed_calendar = g_object_ref (calendar); + + /* Remove the listbox entry (if any) */ + remove_calendar (self, calendar); + + /* Update notification label */ + new_string = g_markup_printf_escaped (_("Calendar %s removed"), + gcal_calendar_get_name (self->removed_calendar)); + gtk_label_set_markup (self->notification_label, new_string); + + gtk_revealer_set_reveal_child (self->notification_revealer, TRUE); + + /* Remove old notifications */ + if (self->notification_timeout_id != 0) + g_source_remove (self->notification_timeout_id); + + self->notification_timeout_id = g_timeout_add_seconds (7, remove_calendar_after_delay_cb, self); + + gcal_calendar_set_visible (self->removed_calendar, FALSE); + + + GCAL_EXIT; +} + +static void +gcal_calendar_management_page_iface_init (GcalCalendarManagementPageInterface *iface) +{ + iface->get_name = gcal_calendars_page_get_name; + iface->get_title = gcal_calendars_page_get_title; + iface->activate = gcal_calendars_page_activate; +} + +/* + * GObject overrides + */ + +static void +gcal_calendars_page_finalize (GObject *object) +{ + GcalCalendarsPage *self = (GcalCalendarsPage *)object; + + g_clear_handle_id (&self->notification_timeout_id, g_source_remove); + g_clear_object (&self->context); + + G_OBJECT_CLASS (gcal_calendars_page_parent_class)->finalize (object); +} + +static void +gcal_calendars_page_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcalCalendarsPage *self = GCAL_CALENDARS_PAGE (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_calendars_page_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcalCalendarsPage *self = GCAL_CALENDARS_PAGE (object); + + switch (prop_id) + { + case PROP_CONTEXT: + { + GcalManager *manager; + + self->context = g_value_dup_object (value); + g_assert (self->context != NULL); + + manager = gcal_context_get_manager (self->context); + g_signal_connect_object (manager, "calendar-added", G_CALLBACK (on_manager_calendar_added_cb), self, 0); + g_signal_connect_object (manager, "calendar-removed", G_CALLBACK (on_manager_calendar_removed_cb), self, 0); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_calendars_page_class_init (GcalCalendarsPageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = gcal_calendars_page_finalize; + object_class->get_property = gcal_calendars_page_get_property; + object_class->set_property = gcal_calendars_page_set_property; + + g_object_class_override_property (object_class, PROP_CONTEXT, "context"); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/calendars-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, GcalCalendarsPage, add_calendar_row); + gtk_widget_class_bind_template_child (widget_class, GcalCalendarsPage, listbox); + gtk_widget_class_bind_template_child (widget_class, GcalCalendarsPage, notification_label); + gtk_widget_class_bind_template_child (widget_class, GcalCalendarsPage, notification_revealer); + gtk_widget_class_bind_template_child (widget_class, GcalCalendarsPage, sizegroup); + + gtk_widget_class_bind_template_callback (widget_class, on_close_notification_button_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, on_undo_remove_button_clicked_cb); +} + +static void +gcal_calendars_page_init (GcalCalendarsPage *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_list_box_set_sort_func (self->listbox, + (GtkListBoxSortFunc) listbox_sort_func, + self, + NULL); +} diff --git a/src/gui/calendar-management/gcal-calendars-page.h b/src/gui/calendar-management/gcal-calendars-page.h new file mode 100644 index 00000000..a3ae760e --- /dev/null +++ b/src/gui/calendar-management/gcal-calendars-page.h @@ -0,0 +1,30 @@ +/* gcal-calendars-page.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_CALENDARS_PAGE (gcal_calendars_page_get_type()) +G_DECLARE_FINAL_TYPE (GcalCalendarsPage, gcal_calendars_page, GCAL, CALENDARS_PAGE, GtkBox) + +G_END_DECLS diff --git a/src/meson.build b/src/meson.build index 44211790..cdb76228 100644 --- a/src/meson.build +++ b/src/meson.build @@ -104,6 +104,7 @@ sources = files( 'core/gcal-time-zone-monitor.c', 'gui/calendar-management/gcal-calendar-management-dialog.c', 'gui/calendar-management/gcal-calendar-management-page.c', + 'gui/calendar-management/gcal-calendars-page.c', 'gui/gcal-application.c', 'gui/gcal-calendar-popover.c', 'gui/gcal-date-chooser.c',