/* gcal-edit-calendar-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 "GcalEditCalendarPage" #include "gcal-context.h" #include "gcal-calendar-management-page.h" #include "gcal-debug.h" #include "gcal-edit-calendar-page.h" #include "gcal-utils.h" #include struct _GcalEditCalendarPage { AdwNavigationPage parent; AdwActionRow *account_row; GtkColorDialogButton *calendar_color_button; AdwSwitchRow *calendar_visible_row; AdwActionRow *calendar_url_row; AdwEntryRow *name_entry; GtkWidget *remove_button_row; GcalCalendar *calendar; }; static void gcal_calendar_management_page_iface_init (GcalCalendarManagementPageInterface *iface); G_DEFINE_TYPE_WITH_CODE (GcalEditCalendarPage, gcal_edit_calendar_page, ADW_TYPE_NAVIGATION_PAGE, G_IMPLEMENT_INTERFACE (GCAL_TYPE_CALENDAR_MANAGEMENT_PAGE, gcal_calendar_management_page_iface_init)) /* * Auxiliary methods */ static gboolean is_goa_calendar (GcalEditCalendarPage *self, GcalCalendar *calendar) { g_autoptr (ESource) parent = NULL; GcalContext *context; GcalManager *manager; ESource *source; g_assert (calendar && GCAL_IS_CALENDAR (calendar)); context = gcal_application_get_context (GCAL_DEFAULT_APPLICATION); manager = gcal_context_get_manager (context); source = gcal_calendar_get_source (calendar); parent = gcal_manager_get_source (manager, e_source_get_parent (source)); return e_source_has_extension (parent, E_SOURCE_EXTENSION_GOA); } static gboolean is_remote_source (ESource *source) { gboolean has_webdav, has_auth; g_assert (E_IS_SOURCE (source)); has_webdav = e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); has_auth = e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); if (!has_webdav || !has_auth) return FALSE; if (has_auth) { ESourceAuthentication *auth; auth = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); /* No host is set, it's not a remote source */ if (!e_source_authentication_get_host (auth)) return FALSE; } if (has_webdav) { ESourceWebdav *webdav; webdav = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); /* No resource path specified, not a remote source */ if (!e_source_webdav_get_resource_path (webdav)) return FALSE; } return TRUE; } static void setup_calendar (GcalEditCalendarPage *self, GcalCalendar *calendar) { ESource *source; gboolean is_remote; gboolean is_file; gboolean is_goa; self->calendar = g_object_ref (calendar); is_goa = is_goa_calendar (self, calendar); source = gcal_calendar_get_source (calendar); is_file = e_source_has_extension (source, E_SOURCE_EXTENSION_LOCAL_BACKEND); is_remote = is_remote_source (source); GCAL_TRACE_MSG ("Calendar '%s' is GOA: %d; is file: %d; is remote: %d", gcal_calendar_get_name (calendar), is_goa, is_file, is_remote); gtk_widget_set_visible (GTK_WIDGET (self->account_row), is_goa); gtk_widget_set_visible (GTK_WIDGET (self->calendar_url_row), !is_goa); /* If it's a file, set the file path */ if (is_file) { g_autofree gchar *basename = NULL; g_autofree gchar *text = NULL; g_autofree gchar *uri = NULL; ESourceLocal *local; GFile *file; local = e_source_get_extension (source, E_SOURCE_EXTENSION_LOCAL_BACKEND); file = e_source_local_get_custom_file (local); basename = g_file_get_basename (file); uri = g_file_get_uri (file); text = g_strdup_printf ("%1$s", basename, uri); adw_action_row_set_subtitle (self->calendar_url_row, text); gtk_accessible_update_property (GTK_ACCESSIBLE (self->calendar_url_row), GTK_ACCESSIBLE_PROPERTY_LABEL, basename, -1); } /* If it's remote, build the uri */ if (is_remote) { g_autoptr (GUri) guri = NULL; g_autofree gchar *text = NULL; g_autofree gchar *uri = NULL; ESourceWebdav *webdav; webdav = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); guri = e_source_webdav_dup_uri (webdav); uri = g_uri_to_string_partial (guri, G_URI_HIDE_PASSWORD); text = g_strdup_printf ("%1$s", uri); adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self->calendar_url_row), _("Calendar URL")); adw_action_row_set_subtitle (self->calendar_url_row, text); gtk_accessible_update_property (GTK_ACCESSIBLE (self->calendar_url_row), GTK_ACCESSIBLE_PROPERTY_LABEL, uri, -1); } if (is_goa) { const gchar *parent_name = e_source_get_display_name (gcal_calendar_get_parent_source (calendar)); adw_action_row_set_subtitle (self->account_row, parent_name); } if (!is_goa && !is_remote && !is_file) { adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self->calendar_url_row), _("Location")); adw_action_row_set_subtitle (self->calendar_url_row, _("On This Computer")); } gtk_color_dialog_button_set_rgba (self->calendar_color_button, gcal_calendar_get_color (calendar)); gtk_editable_set_text (GTK_EDITABLE (self->name_entry), gcal_calendar_get_name (calendar)); adw_switch_row_set_active (self->calendar_visible_row, gcal_calendar_get_visible (calendar)); gtk_widget_set_visible (self->remove_button_row, e_source_get_removable (source)); } /* * Callbacks */ static void on_calendar_color_changed_cb (GtkColorDialogButton *button, GParamSpec *pspec, GcalEditCalendarPage *self) { gcal_calendar_set_color (self->calendar, gtk_color_dialog_button_get_rgba (button)); } static void on_calendar_visibility_changed_cb (AdwSwitchRow *row, GParamSpec *pspec, GcalEditCalendarPage *self) { gcal_calendar_set_visible (self->calendar, adw_switch_row_get_active (row)); } typedef struct { GHashTable *zones; ECalClient *client; } TimeZoneData; static void insert_tz_comps (ICalParameter *param, gpointer data) { g_autoptr (GError) error = NULL; TimeZoneData *tdata = data; ICalTimezone *zone = NULL; ICalComponent *tzcomp; const gchar *tzid; tzid = i_cal_parameter_get_tzid (param); if (g_hash_table_lookup (tdata->zones, tzid)) return; if (!e_cal_client_get_timezone_sync (tdata->client, tzid, &zone, NULL, &error)) zone = NULL; if (error != NULL) { g_warning ("Could not get the timezone information for %s: %s", tzid, error->message); return; } tzcomp = i_cal_component_clone (i_cal_timezone_get_component (zone)); g_hash_table_insert (tdata->zones, (gpointer) tzid, (gpointer) tzcomp); } static void append_tz_to_comp (gpointer key, gpointer value, ICalComponent *toplevel) { i_cal_component_add_component (toplevel, (ICalComponent *) value); } static void on_file_dialog_save_cb (GObject *object, GAsyncResult *res, gpointer user_data) { GcalEditCalendarPage *self = user_data; g_autofree gchar *ics_str = NULL; g_autoptr (GError) error = NULL; g_autoptr (GSList) events = NULL; g_autoptr (GFile) file = NULL; ICalComponent *toplevel_component = NULL; ECalClient *client = NULL; TimeZoneData tz_data; file = gtk_file_dialog_save_finish (GTK_FILE_DIALOG (object), res, &error); if (error) { if (!g_error_matches (error, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_DISMISSED)) g_warning ("Error saving file: %s", error->message); return; } client = gcal_calendar_get_client (self->calendar); e_cal_client_get_object_list_sync (client, "#t", &events, NULL, NULL); /* Avoid exporting empty calendars */ if (!events) return; toplevel_component = e_cal_util_new_top_level (); tz_data.zones = g_hash_table_new (g_str_hash, g_str_equal); tz_data.client = client; for (GSList *l = events; l != NULL; l = l->next) { ICalComponent *component = i_cal_component_clone (l->data); i_cal_component_foreach_tzid (component, insert_tz_comps, &tz_data); i_cal_component_take_component (toplevel_component, g_steal_pointer (&component)); } g_hash_table_foreach (tz_data.zones, (GHFunc) append_tz_to_comp, toplevel_component); g_clear_pointer (&tz_data.zones, g_hash_table_destroy); i_cal_component_strip_errors (toplevel_component); ics_str = i_cal_component_as_ical_string (toplevel_component); g_clear_pointer (&toplevel_component, i_cal_component_free); g_file_replace_contents (file, ics_str, strlen (ics_str), NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, NULL, NULL); } static void on_export_button_row_activated_cb (GtkButton *button, GcalEditCalendarPage *self) { g_autofree gchar *filename = NULL; GtkFileDialog *file_dialog = NULL; GtkWindow *window = NULL; GCAL_ENTRY; window = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))); file_dialog = gtk_file_dialog_new (); filename = g_strconcat (gcal_calendar_get_name (self->calendar), ".ics", NULL); gtk_file_dialog_set_title (file_dialog, _("Export Calendar")); gtk_file_dialog_set_initial_name (file_dialog, filename); gtk_file_dialog_save (file_dialog, window, NULL, on_file_dialog_save_cb, self); GCAL_EXIT; } static void on_remove_button_row_activated_cb (GtkButton *button, GcalEditCalendarPage *self) { GcalCalendarManagementPage *page; GCAL_ENTRY; page = GCAL_CALENDAR_MANAGEMENT_PAGE (self); gcal_calendar_management_page_switch_page (page, "calendars", self->calendar); GCAL_EXIT; } static void on_settings_button_clicked_cb (GtkWidget *button, GcalEditCalendarPage *self) { g_autoptr (ESource) parent = NULL; GApplication *app; GcalContext *context; GcalManager *manager; ESourceGoa *goa; ESource *source; GCAL_ENTRY; context = gcal_application_get_context (GCAL_DEFAULT_APPLICATION); manager = gcal_context_get_manager (context); source = gcal_calendar_get_source (self->calendar); parent = gcal_manager_get_source (manager, e_source_get_parent (source)); g_assert (e_source_has_extension (parent, E_SOURCE_EXTENSION_GOA)); goa = e_source_get_extension (parent, E_SOURCE_EXTENSION_GOA); app = g_application_get_default (); gcal_utils_launch_gnome_settings (g_application_get_dbus_connection (app), "online-accounts", e_source_goa_get_account_id (goa)); GCAL_EXIT; } /* * GcalCalendarManagementPage iface */ static void gcal_edit_calendar_page_activate (GcalCalendarManagementPage *page, GcalCalendar *calendar) { GCAL_ENTRY; setup_calendar (GCAL_EDIT_CALENDAR_PAGE (page), calendar); GCAL_EXIT; } static void gcal_edit_calendar_page_deactivate (GcalCalendarManagementPage *page) { GcalEditCalendarPage *self; GcalContext *context; GcalManager *manager; GCAL_ENTRY; self = GCAL_EDIT_CALENDAR_PAGE (page); gcal_calendar_set_name (self->calendar, gtk_editable_get_text (GTK_EDITABLE (self->name_entry))); context = gcal_application_get_context (GCAL_DEFAULT_APPLICATION); manager = gcal_context_get_manager (context); gcal_manager_save_source (manager, gcal_calendar_get_source (self->calendar)); g_clear_object (&self->calendar); GCAL_EXIT; } static void gcal_calendar_management_page_iface_init (GcalCalendarManagementPageInterface *iface) { iface->activate = gcal_edit_calendar_page_activate; iface->deactivate = gcal_edit_calendar_page_deactivate; } /* * GObject overrides */ static void gcal_edit_calendar_page_dispose (GObject *object) { GcalEditCalendarPage *self = GCAL_EDIT_CALENDAR_PAGE (object); if (self->calendar) gcal_calendar_set_name (self->calendar, gtk_editable_get_text (GTK_EDITABLE (self->name_entry))); G_OBJECT_CLASS (gcal_edit_calendar_page_parent_class)->dispose (object); } static void gcal_edit_calendar_page_finalize (GObject *object) { GcalEditCalendarPage *self = (GcalEditCalendarPage *)object; g_clear_object (&self->calendar); G_OBJECT_CLASS (gcal_edit_calendar_page_parent_class)->finalize (object); } static void gcal_edit_calendar_page_class_init (GcalEditCalendarPageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->dispose = gcal_edit_calendar_page_dispose; object_class->finalize = gcal_edit_calendar_page_finalize; gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/gui/calendar-management/gcal-edit-calendar-page.ui"); gtk_widget_class_bind_template_child (widget_class, GcalEditCalendarPage, account_row); gtk_widget_class_bind_template_child (widget_class, GcalEditCalendarPage, calendar_color_button); gtk_widget_class_bind_template_child (widget_class, GcalEditCalendarPage, calendar_url_row); gtk_widget_class_bind_template_child (widget_class, GcalEditCalendarPage, calendar_visible_row); gtk_widget_class_bind_template_child (widget_class, GcalEditCalendarPage, name_entry); gtk_widget_class_bind_template_child (widget_class, GcalEditCalendarPage, remove_button_row); gtk_widget_class_bind_template_callback (widget_class, on_calendar_color_changed_cb); gtk_widget_class_bind_template_callback (widget_class, on_calendar_visibility_changed_cb); gtk_widget_class_bind_template_callback (widget_class, on_remove_button_row_activated_cb); gtk_widget_class_bind_template_callback (widget_class, on_export_button_row_activated_cb); gtk_widget_class_bind_template_callback (widget_class, on_settings_button_clicked_cb); } static void gcal_edit_calendar_page_init (GcalEditCalendarPage *self) { gtk_widget_init_template (GTK_WIDGET (self)); }