Add file importing dialog
Add a file importing dialog, and pass through files received from command line. Fixes https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/5
This commit is contained in:
@@ -34,6 +34,10 @@ src/gui/gcal-weather-settings.ui
|
||||
src/gui/gcal-window.c
|
||||
src/gui/gcal-window.ui
|
||||
src/gui/gtk/help-overlay.ui
|
||||
src/gui/importer/gcal-import-dialog.c
|
||||
src/gui/importer/gcal-import-dialog.ui
|
||||
src/gui/importer/gcal-importer.c
|
||||
src/gui/importer/gcal-import-file-row.c
|
||||
src/gui/views/gcal-month-popover.ui
|
||||
src/gui/views/gcal-week-grid.c
|
||||
src/gui/views/gcal-week-header.c
|
||||
|
||||
@@ -457,11 +457,14 @@ gcal_application_command_line (GApplication *app,
|
||||
GApplicationCommandLine *command_line)
|
||||
{
|
||||
g_autoptr (GVariant) option = NULL;
|
||||
g_auto (GStrv) arguments = NULL;
|
||||
GcalApplication *self;
|
||||
GVariantDict *options;
|
||||
const gchar* date = NULL;
|
||||
const gchar* uuid = NULL;
|
||||
gsize length;
|
||||
gint n_arguments;
|
||||
gint i;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
@@ -510,6 +513,37 @@ gcal_application_command_line (GApplication *app,
|
||||
|
||||
g_application_activate (app);
|
||||
|
||||
arguments = g_application_command_line_get_arguments (command_line, &n_arguments);
|
||||
if (n_arguments > 1)
|
||||
{
|
||||
g_autoptr (GHashTable) unique_files = NULL;
|
||||
g_autoptr (GPtrArray) files = NULL;
|
||||
gint n_files = 0;
|
||||
|
||||
files = g_ptr_array_new_full (n_arguments - 1, g_object_unref);
|
||||
unique_files = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal);
|
||||
|
||||
for (i = 1; i < n_arguments; i++)
|
||||
{
|
||||
g_autoptr (GFile) file = NULL;
|
||||
const gchar *arg;
|
||||
|
||||
arg = arguments[i];
|
||||
file = g_application_command_line_create_file_for_arg (command_line, arg);
|
||||
|
||||
if (g_str_has_prefix (arg, "-") || g_hash_table_contains (unique_files, file))
|
||||
continue;
|
||||
|
||||
g_hash_table_add (unique_files, file);
|
||||
g_ptr_array_add (files, g_steal_pointer (&file));
|
||||
|
||||
n_files++;
|
||||
}
|
||||
|
||||
if (n_files > 0)
|
||||
g_application_open (app, (GFile **) files->pdata, n_files, "");
|
||||
}
|
||||
|
||||
GCAL_RETURN (0);
|
||||
}
|
||||
|
||||
@@ -574,6 +608,23 @@ gcal_application_dbus_unregister (GApplication *application,
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
gcal_application_open (GApplication *application,
|
||||
GFile **files,
|
||||
gint n_files,
|
||||
const gchar *hint)
|
||||
{
|
||||
GcalApplication *self;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
self = GCAL_APPLICATION (application);
|
||||
|
||||
gcal_window_import_files (GCAL_WINDOW (self->window), files, n_files);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
gcal_application_class_init (GcalApplicationClass *klass)
|
||||
{
|
||||
@@ -588,6 +639,7 @@ gcal_application_class_init (GcalApplicationClass *klass)
|
||||
application_class->activate = gcal_application_activate;
|
||||
application_class->startup = gcal_application_startup;
|
||||
application_class->command_line = gcal_application_command_line;
|
||||
application_class->open = gcal_application_open;
|
||||
application_class->handle_local_options = gcal_application_handle_local_options;
|
||||
application_class->dbus_register = gcal_application_dbus_register;
|
||||
application_class->dbus_unregister = gcal_application_dbus_unregister;
|
||||
@@ -631,7 +683,7 @@ gcal_application_new (void)
|
||||
return g_object_new (gcal_application_get_type (),
|
||||
"resource-base-path", "/org/gnome/calendar",
|
||||
"application-id", APPLICATION_ID,
|
||||
"flags", G_APPLICATION_HANDLES_COMMAND_LINE,
|
||||
"flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
#include "gcal-window.h"
|
||||
#include "gcal-year-view.h"
|
||||
|
||||
#include "importer/gcal-import-dialog.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <libecal/libecal.h>
|
||||
|
||||
@@ -116,6 +118,7 @@ struct _GcalWindow
|
||||
GtkWidget *views_switcher;
|
||||
|
||||
GcalEventEditorDialog *event_editor;
|
||||
GtkWidget *import_dialog;
|
||||
|
||||
DzlSuggestionButton *search_button;
|
||||
|
||||
@@ -1205,3 +1208,17 @@ gcal_window_open_event_by_uuid (GcalWindow *self,
|
||||
edit_dialog_data);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gcal_window_import_files (GcalWindow *self,
|
||||
GFile **files,
|
||||
gint n_files)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_WINDOW (self));
|
||||
|
||||
g_clear_pointer (&self->import_dialog, gtk_widget_destroy);
|
||||
|
||||
self->import_dialog = gcal_import_dialog_new_for_files (self->context, files, n_files);
|
||||
gtk_window_set_transient_for (GTK_WINDOW (self->import_dialog), GTK_WINDOW (self));
|
||||
gtk_window_present (GTK_WINDOW (self->import_dialog));
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@ void gcal_window_set_search_query (GcalWindow
|
||||
void gcal_window_open_event_by_uuid (GcalWindow *self,
|
||||
const gchar *uuid);
|
||||
|
||||
void gcal_window_import_files (GcalWindow *self,
|
||||
GFile **files,
|
||||
gint n_files);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GCAL_WINDOW_H__ */
|
||||
|
||||
@@ -0,0 +1,569 @@
|
||||
/* gcal-import-dialog.c
|
||||
*
|
||||
* Copyright 2021 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "GcalImportDialog"
|
||||
|
||||
#include "gcal-import-dialog.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "gcal-debug.h"
|
||||
#include "gcal-import-file-row.h"
|
||||
#include "gcal-utils.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
struct _GcalImportDialog
|
||||
{
|
||||
HdyWindow parent;
|
||||
|
||||
GtkImage *calendar_color_image;
|
||||
GtkLabel *calendar_name_label;
|
||||
GtkListBox *calendars_listbox;
|
||||
GtkPopover *calendars_popover;
|
||||
GtkWidget *cancel_button;
|
||||
GtkListBox *files_listbox;
|
||||
HdyHeaderBar *headerbar;
|
||||
GtkWidget *import_button;
|
||||
GtkSizeGroup *title_sizegroup;
|
||||
|
||||
GtkWidget *selected_row;
|
||||
|
||||
GCancellable *cancellable;
|
||||
GcalContext *context;
|
||||
gint n_events;
|
||||
gint n_files;
|
||||
};
|
||||
|
||||
|
||||
static void on_import_row_file_loaded_cb (GcalImportFileRow *row,
|
||||
GPtrArray *events,
|
||||
GcalImportDialog *self);
|
||||
|
||||
static void on_manager_calendar_added_cb (GcalManager *manager,
|
||||
GcalCalendar *calendar,
|
||||
GcalImportDialog *self);
|
||||
|
||||
static void on_manager_calendar_changed_cb (GcalManager *manager,
|
||||
GcalCalendar *calendar,
|
||||
GcalImportDialog *self);
|
||||
|
||||
static void on_manager_calendar_removed_cb (GcalManager *manager,
|
||||
GcalCalendar *calendar,
|
||||
GcalImportDialog *self);
|
||||
|
||||
G_DEFINE_TYPE (GcalImportDialog, gcal_import_dialog, HDY_TYPE_WINDOW)
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CONTEXT,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
|
||||
/*
|
||||
* Auxiliary methods
|
||||
*/
|
||||
|
||||
static GtkWidget*
|
||||
create_calendar_row (GcalManager *manager,
|
||||
GcalCalendar *calendar)
|
||||
{
|
||||
g_autofree gchar *parent_name = NULL;
|
||||
cairo_surface_t *surface;
|
||||
const GdkRGBA *color;
|
||||
GtkWidget *icon;
|
||||
GtkWidget *row;
|
||||
|
||||
color = gcal_calendar_get_color (calendar);
|
||||
surface = get_circle_surface_from_color (color, 16);
|
||||
get_source_parent_name_color (manager,
|
||||
gcal_calendar_get_source (calendar),
|
||||
&parent_name,
|
||||
NULL);
|
||||
|
||||
/* The icon with the source color */
|
||||
icon = gtk_image_new_from_surface (surface);
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (icon), "calendar-color-image");
|
||||
gtk_widget_show (icon);
|
||||
|
||||
/* The row itself */
|
||||
row = g_object_new (HDY_TYPE_ACTION_ROW,
|
||||
"title", gcal_calendar_get_name (calendar),
|
||||
"subtitle", parent_name,
|
||||
"sensitive", !gcal_calendar_is_read_only (calendar),
|
||||
"activatable", TRUE,
|
||||
"width-request", 300,
|
||||
NULL);
|
||||
hdy_action_row_add_prefix (HDY_ACTION_ROW (row), icon);
|
||||
gtk_widget_show (row);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (row), "calendar", g_object_ref (calendar), g_object_unref);
|
||||
g_object_set_data (G_OBJECT (row), "color-icon", icon);
|
||||
|
||||
g_clear_pointer (&surface, cairo_surface_destroy);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
static GtkWidget*
|
||||
get_row_for_calendar (GcalImportDialog *self,
|
||||
GcalCalendar *calendar)
|
||||
{
|
||||
g_autoptr (GList) children = NULL;
|
||||
GtkWidget *row;
|
||||
GList *l;
|
||||
|
||||
row = NULL;
|
||||
children = gtk_container_get_children (GTK_CONTAINER (self->calendars_listbox));
|
||||
|
||||
for (l = children; l != NULL; l = g_list_next (l))
|
||||
{
|
||||
GcalCalendar *row_calendar = g_object_get_data (l->data, "calendar");
|
||||
|
||||
if (row_calendar == calendar)
|
||||
{
|
||||
row = l->data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
static void
|
||||
select_row (GcalImportDialog *self,
|
||||
GtkListBoxRow *row)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
const GdkRGBA *color;
|
||||
GcalCalendar *calendar;
|
||||
|
||||
self->selected_row = GTK_WIDGET (row);
|
||||
|
||||
/* Setup the event page's source name and color */
|
||||
calendar = g_object_get_data (G_OBJECT (row), "calendar");
|
||||
|
||||
gtk_label_set_label (self->calendar_name_label, gcal_calendar_get_name (calendar));
|
||||
|
||||
color = gcal_calendar_get_color (calendar);
|
||||
surface = get_circle_surface_from_color (color, 16);
|
||||
gtk_image_set_from_surface (self->calendar_color_image, surface);
|
||||
|
||||
g_clear_pointer (&surface, cairo_surface_destroy);
|
||||
}
|
||||
|
||||
static void
|
||||
update_default_calendar_row (GcalImportDialog *self)
|
||||
{
|
||||
GcalCalendar *default_calendar;
|
||||
GcalManager *manager;
|
||||
GtkWidget *row;
|
||||
|
||||
manager = gcal_context_get_manager (self->context);
|
||||
default_calendar = gcal_manager_get_default_calendar (manager);
|
||||
|
||||
row = get_row_for_calendar (self, default_calendar);
|
||||
if (row != NULL)
|
||||
select_row (self, GTK_LIST_BOX_ROW (row));
|
||||
}
|
||||
|
||||
static void
|
||||
setup_calendars (GcalImportDialog *self)
|
||||
{
|
||||
g_autoptr (GList) calendars = NULL;
|
||||
GcalManager *manager;
|
||||
GList *l;
|
||||
|
||||
manager = gcal_context_get_manager (self->context);
|
||||
calendars = gcal_manager_get_calendars (manager);
|
||||
|
||||
for (l = calendars; l; l = l->next)
|
||||
on_manager_calendar_added_cb (manager, l->data, self);
|
||||
|
||||
update_default_calendar_row (self);
|
||||
|
||||
g_signal_connect_object (manager, "calendar-added", G_CALLBACK (on_manager_calendar_added_cb), self, 0);
|
||||
g_signal_connect_object (manager, "calendar-changed", G_CALLBACK (on_manager_calendar_changed_cb), self, 0);
|
||||
g_signal_connect_object (manager, "calendar-removed", G_CALLBACK (on_manager_calendar_removed_cb), self, 0);
|
||||
g_signal_connect_object (manager, "notify::default-calendar", G_CALLBACK (update_default_calendar_row), self, G_CONNECT_SWAPPED);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_files (GcalImportDialog *self,
|
||||
GFile **files,
|
||||
gint n_files)
|
||||
{
|
||||
gint i;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
self->n_files = n_files;
|
||||
|
||||
for (i = 0; i < n_files; i++)
|
||||
{
|
||||
GtkWidget *row;
|
||||
|
||||
row = gcal_import_file_row_new (files[i], self->title_sizegroup);
|
||||
g_signal_connect (row, "file-loaded", G_CALLBACK (on_import_row_file_loaded_cb), self);
|
||||
|
||||
if (n_files > 1)
|
||||
gcal_import_file_row_show_filename (GCAL_IMPORT_FILE_ROW (row));
|
||||
|
||||
gtk_list_box_insert (self->files_listbox, row, -1);
|
||||
}
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
|
||||
static void
|
||||
on_events_created_cb (GObject *source_object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
GcalImportDialog *self;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
self = GCAL_IMPORT_DIALOG (user_data);
|
||||
|
||||
e_cal_client_create_objects_finish (E_CAL_CLIENT (source_object), result, NULL, &error);
|
||||
if (error)
|
||||
g_warning ("Error creating events: %s", error->message);
|
||||
|
||||
gtk_widget_destroy (GTK_WIDGET (self));
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
on_calendars_listbox_row_activated_cb (GtkListBox *listbox,
|
||||
GtkListBoxRow *row,
|
||||
GcalImportDialog *self)
|
||||
{
|
||||
GCAL_ENTRY;
|
||||
|
||||
select_row (self, row);
|
||||
gtk_popover_popdown (self->calendars_popover);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
on_cancel_button_clicked_cb (GtkButton *button,
|
||||
GcalImportDialog *self)
|
||||
{
|
||||
gtk_widget_destroy (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
on_import_button_clicked_cb (GtkButton *button,
|
||||
GcalImportDialog *self)
|
||||
{
|
||||
g_autoptr (GList) children = NULL;
|
||||
GcalCalendar *calendar;
|
||||
ECalClient *client;
|
||||
GSList *slist;
|
||||
GList *l;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
calendar = g_object_get_data (G_OBJECT (self->selected_row), "calendar");
|
||||
g_assert (self->selected_row != NULL);
|
||||
|
||||
slist = NULL;
|
||||
children = gtk_container_get_children (GTK_CONTAINER (self->files_listbox));
|
||||
for (l = children; l; l = l->next)
|
||||
{
|
||||
GcalImportFileRow *row = l->data;
|
||||
GPtrArray *ical_components;
|
||||
guint i;
|
||||
|
||||
ical_components = gcal_import_file_row_get_ical_components (row);
|
||||
if (!ical_components)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < ical_components->len; i++)
|
||||
slist = g_slist_prepend (slist, g_ptr_array_index (ical_components, i));
|
||||
}
|
||||
|
||||
if (!slist)
|
||||
GCAL_RETURN ();
|
||||
|
||||
self->cancellable = g_cancellable_new ();
|
||||
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
|
||||
|
||||
client = gcal_calendar_get_client (calendar);
|
||||
e_cal_client_create_objects (client,
|
||||
slist,
|
||||
E_CAL_OPERATION_FLAG_NONE,
|
||||
self->cancellable,
|
||||
on_events_created_cb,
|
||||
self);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
on_import_row_file_loaded_cb (GcalImportFileRow *row,
|
||||
GPtrArray *events,
|
||||
GcalImportDialog *self)
|
||||
{
|
||||
g_autofree gchar *title = NULL;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
self->n_events += events ? events->len : 0;
|
||||
|
||||
title = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
|
||||
"Import %d event",
|
||||
"Import %d events",
|
||||
self->n_events),
|
||||
self->n_events);
|
||||
hdy_header_bar_set_title (self->headerbar, title);
|
||||
|
||||
gtk_widget_show (GTK_WIDGET (row));
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
on_manager_calendar_added_cb (GcalManager *manager,
|
||||
GcalCalendar *calendar,
|
||||
GcalImportDialog *self)
|
||||
{
|
||||
if (gcal_calendar_is_read_only (calendar))
|
||||
return;
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (self->calendars_listbox),
|
||||
create_calendar_row (manager, calendar));
|
||||
}
|
||||
|
||||
static void
|
||||
on_manager_calendar_changed_cb (GcalManager *manager,
|
||||
GcalCalendar *calendar,
|
||||
GcalImportDialog *self)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
const GdkRGBA *color;
|
||||
GtkWidget *row, *color_icon;
|
||||
gboolean read_only;
|
||||
|
||||
read_only = gcal_calendar_is_read_only (calendar);
|
||||
row = get_row_for_calendar (self, calendar);
|
||||
|
||||
/* If the calendar changed from/to read-only, we add or remove it here */
|
||||
if (read_only)
|
||||
{
|
||||
if (row)
|
||||
gtk_container_remove (GTK_CONTAINER (self->calendars_listbox), row);
|
||||
return;
|
||||
}
|
||||
else if (!row)
|
||||
{
|
||||
on_manager_calendar_added_cb (manager, calendar, self);
|
||||
row = get_row_for_calendar (self, calendar);
|
||||
}
|
||||
|
||||
hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), gcal_calendar_get_name (calendar));
|
||||
gtk_widget_set_sensitive (row, !read_only);
|
||||
|
||||
/* Setup the source color, in case it changed */
|
||||
color = gcal_calendar_get_color (calendar);
|
||||
surface = get_circle_surface_from_color (color, 16);
|
||||
color_icon = g_object_get_data (G_OBJECT (row), "color-icon");
|
||||
gtk_image_set_from_surface (GTK_IMAGE (color_icon), surface);
|
||||
|
||||
gtk_list_box_invalidate_sort (GTK_LIST_BOX (self->calendars_listbox));
|
||||
|
||||
g_clear_pointer (&surface, cairo_surface_destroy);
|
||||
}
|
||||
|
||||
static void
|
||||
on_manager_calendar_removed_cb (GcalManager *manager,
|
||||
GcalCalendar *calendar,
|
||||
GcalImportDialog *self)
|
||||
{
|
||||
GtkWidget *row;
|
||||
|
||||
row = get_row_for_calendar (self, calendar);
|
||||
|
||||
if (!row)
|
||||
return;
|
||||
|
||||
gtk_container_remove (GTK_CONTAINER (self->calendars_listbox), row);
|
||||
}
|
||||
|
||||
static void
|
||||
on_select_calendar_row_activated_cb (GtkListBox *listbox,
|
||||
GtkListBoxRow *row,
|
||||
GcalImportDialog *self)
|
||||
{
|
||||
gtk_popover_popup (self->calendars_popover);
|
||||
}
|
||||
|
||||
static gint
|
||||
sort_func (GtkListBoxRow *row1,
|
||||
GtkListBoxRow *row2,
|
||||
gpointer user_data)
|
||||
{
|
||||
GcalCalendar *calendar1, *calendar2;
|
||||
g_autofree gchar *name1 = NULL;
|
||||
g_autofree gchar *name2 = NULL;
|
||||
|
||||
calendar1 = g_object_get_data (G_OBJECT (row1), "calendar");
|
||||
calendar2 = g_object_get_data (G_OBJECT (row2), "calendar");
|
||||
|
||||
name1 = g_utf8_casefold (gcal_calendar_get_name (calendar1), -1);
|
||||
name2 = g_utf8_casefold (gcal_calendar_get_name (calendar2), -1);
|
||||
|
||||
return g_strcmp0 (name1, name2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GObject overrides
|
||||
*/
|
||||
|
||||
static void
|
||||
gcal_import_dialog_finalize (GObject *object)
|
||||
{
|
||||
GcalImportDialog *self = (GcalImportDialog *)object;
|
||||
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
g_clear_object (&self->context);
|
||||
|
||||
G_OBJECT_CLASS (gcal_import_dialog_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gcal_import_dialog_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GcalImportDialog *self = GCAL_IMPORT_DIALOG (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_import_dialog_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GcalImportDialog *self = GCAL_IMPORT_DIALOG (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CONTEXT:
|
||||
g_assert (self->context == NULL);
|
||||
self->context = g_value_dup_object (value);
|
||||
setup_calendars (self);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gcal_import_dialog_class_init (GcalImportDialogClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->finalize = gcal_import_dialog_finalize;
|
||||
object_class->get_property = gcal_import_dialog_get_property;
|
||||
object_class->set_property = gcal_import_dialog_set_property;
|
||||
|
||||
/**
|
||||
* GcalEventPopover::context:
|
||||
*
|
||||
* The context of the import dialog.
|
||||
*/
|
||||
properties[PROP_CONTEXT] = g_param_spec_object ("context",
|
||||
"Context",
|
||||
"Context",
|
||||
GCAL_TYPE_CONTEXT,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 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/ui/gui/importer/gcal-import-dialog.ui");
|
||||
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportDialog, calendar_color_image);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportDialog, calendar_name_label);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportDialog, calendars_listbox);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportDialog, calendars_popover);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportDialog, cancel_button);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportDialog, files_listbox);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportDialog, headerbar);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportDialog, import_button);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportDialog, title_sizegroup);
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_calendars_listbox_row_activated_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_cancel_button_clicked_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_import_button_clicked_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_select_calendar_row_activated_cb);
|
||||
}
|
||||
|
||||
static void
|
||||
gcal_import_dialog_init (GcalImportDialog *self)
|
||||
{
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
|
||||
gtk_list_box_set_sort_func (GTK_LIST_BOX (self->calendars_listbox), sort_func, NULL, NULL);
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
gcal_import_dialog_new_for_files (GcalContext *context,
|
||||
GFile **files,
|
||||
gint n_files)
|
||||
{
|
||||
GcalImportDialog *self;
|
||||
|
||||
self = g_object_new (GCAL_TYPE_IMPORT_DIALOG,
|
||||
"context", context,
|
||||
NULL);
|
||||
|
||||
setup_files (self, files, n_files);
|
||||
|
||||
return GTK_WIDGET (self);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* gcal-import-dialog.h
|
||||
*
|
||||
* Copyright 2021 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gcal-context.h"
|
||||
|
||||
#include <handy.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GCAL_TYPE_IMPORT_DIALOG (gcal_import_dialog_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GcalImportDialog, gcal_import_dialog, GCAL, IMPORT_DIALOG, HdyWindow)
|
||||
|
||||
GtkWidget* gcal_import_dialog_new_for_files (GcalContext *context,
|
||||
GFile **files,
|
||||
gint n_files);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GcalImportDialog" parent="HdyWindow">
|
||||
<property name="width_request">500</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">0</property>
|
||||
<property name="default_width">550</property>
|
||||
<property name="default_height">500</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<child>
|
||||
<object class="HdyDeck">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
|
||||
<child>
|
||||
<object class="HdyHeaderBar" id="headerbar">
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">Import Files…</property>
|
||||
<property name="show_close_button">False</property>
|
||||
|
||||
<!-- Cancel button -->
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Cancel</property>
|
||||
<property name="use-underline">True</property>
|
||||
<signal name="clicked" handler="on_cancel_button_clicked_cb" object="GcalImportDialog" swapped="no" />
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<!-- Import button -->
|
||||
<child>
|
||||
<object class="GtkButton" id="import_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Import</property>
|
||||
<property name="use-underline">True</property>
|
||||
<signal name="clicked" handler="on_import_button_clicked_cb" object="GcalImportDialog" swapped="no" />
|
||||
<style>
|
||||
<class name="suggested-action" />
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="propagate-natural-height">True</property>
|
||||
<property name="min-content-height">400</property>
|
||||
<property name="max-content-height">700</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">12</property>
|
||||
<property name="margin-top">24</property>
|
||||
<property name="margin-bottom">24</property>
|
||||
<property name="margin-start">36</property>
|
||||
<property name="margin-end">36</property>
|
||||
<property name="orientation">vertical</property>
|
||||
|
||||
<!-- Calendar row -->
|
||||
<child>
|
||||
<object class="GtkListBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="selection-mode">none</property>
|
||||
<signal name="row-activated" handler="on_select_calendar_row_activated_cb" object="GcalImportDialog" swapped="no" />
|
||||
<style>
|
||||
<class name="content" />
|
||||
</style>
|
||||
|
||||
<child>
|
||||
<object class="HdyActionRow" id="calendar_row">
|
||||
<property name="visible">True</property>
|
||||
<property name="activatable">True</property>
|
||||
<property name="title" translatable="yes">C_alendar</property>
|
||||
<property name="use-underline">True</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkBox" id="calendar_row_widgets_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">12</property>
|
||||
<property name="spacing">12</property>
|
||||
|
||||
<!-- Color -->
|
||||
<child>
|
||||
<object class="GtkImage" id="calendar_color_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<!-- Calendar name -->
|
||||
<child>
|
||||
<object class="GtkLabel" id="calendar_name_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="pixel-size">16</property>
|
||||
<property name="icon-name">pan-down-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<!-- Files overview -->
|
||||
<child>
|
||||
<object class="GtkListBox" id="files_listbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="selection-mode">none</property>
|
||||
|
||||
<child type="placeholder">
|
||||
<object class="GtkSpinner">
|
||||
<property name="visible">True</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<style>
|
||||
<class name="background" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</template>
|
||||
|
||||
<!-- Calendars popover -->
|
||||
<object class="GtkPopover" id="calendars_popover">
|
||||
<property name="position">bottom</property>
|
||||
<property name="relative-to">calendar_row_widgets_box</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="max-content-height">350</property>
|
||||
<property name="propagate-natural-width">True</property>
|
||||
<property name="propagate-natural-height">True</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="calendars_listbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="selection-mode">none</property>
|
||||
<signal name="row-activated" handler="on_calendars_listbox_row_activated_cb" object="GcalImportDialog" swapped="no" />
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
<object class="GtkSizeGroup" id="title_sizegroup">
|
||||
<property name="mode">horizontal</property>
|
||||
</object>
|
||||
|
||||
</interface>
|
||||
@@ -0,0 +1,388 @@
|
||||
/* gcal-import-file-row.c
|
||||
*
|
||||
* Copyright 2021 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "GcalImportFileRow"
|
||||
|
||||
#include "config.h"
|
||||
#include "gcal-import-file-row.h"
|
||||
#include "gcal-importer.h"
|
||||
#include "gcal-utils.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
struct _GcalImportFileRow
|
||||
{
|
||||
GtkListBoxRow parent;
|
||||
|
||||
GtkListBox *events_listbox;
|
||||
GtkLabel *filename_label;
|
||||
GtkSizeGroup *title_sizegroup;
|
||||
|
||||
GCancellable *cancellable;
|
||||
GFile *file;
|
||||
GPtrArray *ical_components;
|
||||
};
|
||||
|
||||
static void read_calendar_finished_cb (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data);
|
||||
|
||||
G_DEFINE_TYPE (GcalImportFileRow, gcal_import_file_row, GTK_TYPE_LIST_BOX_ROW)
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_FILE,
|
||||
N_PROPS,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
FILE_LOADED,
|
||||
N_SIGNALS,
|
||||
};
|
||||
|
||||
static guint signals[N_SIGNALS] = { 0, };
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
|
||||
/*
|
||||
* Auxiliary methods
|
||||
*/
|
||||
|
||||
static void
|
||||
add_grid_row (GcalImportFileRow *self,
|
||||
GtkGrid *grid,
|
||||
gint row,
|
||||
const gchar *title,
|
||||
const gchar *value)
|
||||
{
|
||||
GtkWidget *title_label;
|
||||
GtkWidget *value_label;
|
||||
|
||||
if (!value || g_utf8_strlen (value, -1) == 0)
|
||||
return;
|
||||
|
||||
title_label = g_object_new (GTK_TYPE_LABEL,
|
||||
"visible", TRUE,
|
||||
"label", title,
|
||||
"xalign", 1.0,
|
||||
"yalign", 0.0,
|
||||
"ellipsize", PANGO_ELLIPSIZE_END,
|
||||
"max-width-chars", 40,
|
||||
NULL);
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (title_label), "dim-label");
|
||||
gtk_grid_attach (grid, title_label, 0, row, 1, 1);
|
||||
|
||||
gtk_size_group_add_widget (self->title_sizegroup, title_label);
|
||||
|
||||
value_label = g_object_new (GTK_TYPE_LABEL,
|
||||
"visible", TRUE,
|
||||
"label", value,
|
||||
"xalign", 0.0,
|
||||
"selectable", TRUE,
|
||||
"ellipsize", PANGO_ELLIPSIZE_END,
|
||||
"max-width-chars", 40,
|
||||
NULL);
|
||||
gtk_grid_attach (grid, value_label, 1, row, 1, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
fill_grid_with_event_data (GcalImportFileRow *self,
|
||||
GtkGrid *grid,
|
||||
ICalComponent *ical_component)
|
||||
{
|
||||
g_autofree gchar *start_string = NULL;
|
||||
g_autofree gchar *description = NULL;
|
||||
g_autofree gchar *end_string = NULL;
|
||||
g_autoptr (GDateTime) start = NULL;
|
||||
g_autoptr (GDateTime) end = NULL;
|
||||
ICalTime *ical_start;
|
||||
ICalTime *ical_end;
|
||||
gint row = 0;
|
||||
|
||||
ical_start = i_cal_component_get_dtstart (ical_component);
|
||||
start = gcal_date_time_from_icaltime (ical_start);
|
||||
if (i_cal_time_is_date (ical_start))
|
||||
start_string = g_date_time_format (start, "%x");
|
||||
else
|
||||
start_string = g_date_time_format (start, "%x %X");
|
||||
|
||||
ical_end = i_cal_component_get_dtend (ical_component);
|
||||
if (ical_end)
|
||||
{
|
||||
end = gcal_date_time_from_icaltime (ical_end);
|
||||
if (i_cal_time_is_date (ical_end))
|
||||
end_string = g_date_time_format (end, "%x");
|
||||
else
|
||||
end_string = g_date_time_format (end, "%x %X");
|
||||
}
|
||||
else
|
||||
{
|
||||
end = g_date_time_add_days (start, 1);
|
||||
if (i_cal_time_is_date (ical_start))
|
||||
end_string = g_date_time_format (end, "%x");
|
||||
else
|
||||
end_string = g_date_time_format (end, "%x %X");
|
||||
}
|
||||
|
||||
gcal_utils_extract_google_section (i_cal_component_get_description (ical_component),
|
||||
&description,
|
||||
NULL);
|
||||
|
||||
add_grid_row (self, grid, row++, _("Title"), i_cal_component_get_summary (ical_component));
|
||||
add_grid_row (self, grid, row++, _("Location"), i_cal_component_get_location (ical_component));
|
||||
add_grid_row (self, grid, row++, _("Starts"), start_string);
|
||||
add_grid_row (self, grid, row++, _("Ends"), end_string);
|
||||
add_grid_row (self, grid, row++, _("Description"), description);
|
||||
|
||||
g_clear_object (&ical_start);
|
||||
g_clear_object (&ical_end);
|
||||
}
|
||||
|
||||
static void
|
||||
add_events_to_listbox (GcalImportFileRow *self,
|
||||
GPtrArray *events)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < events->len; i++)
|
||||
{
|
||||
ICalComponent *ical_component;
|
||||
GtkWidget *grid;
|
||||
GtkWidget *row;
|
||||
|
||||
ical_component = g_ptr_array_index (events, i);
|
||||
|
||||
row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
|
||||
"visible", TRUE,
|
||||
"activatable", FALSE,
|
||||
NULL);
|
||||
|
||||
grid = g_object_new (GTK_TYPE_GRID,
|
||||
"visible", TRUE,
|
||||
"row-spacing", 6,
|
||||
"column-spacing", 12,
|
||||
"margin-top", 18,
|
||||
"margin-bottom", 18,
|
||||
"margin-start", 24,
|
||||
"margin-end", 24,
|
||||
NULL);
|
||||
fill_grid_with_event_data (self, GTK_GRID (grid), ical_component);
|
||||
gtk_container_add (GTK_CONTAINER (row), grid);
|
||||
|
||||
gtk_list_box_insert (self->events_listbox, row, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static GPtrArray*
|
||||
filter_event_components (ICalComponent *component)
|
||||
{
|
||||
g_autoptr (GPtrArray) event_components = NULL;
|
||||
ICalComponent *aux;
|
||||
|
||||
if (!component)
|
||||
return NULL;
|
||||
|
||||
event_components = g_ptr_array_new_full (20, g_object_unref);
|
||||
aux = i_cal_component_get_first_real_component (component);
|
||||
while (aux)
|
||||
{
|
||||
g_ptr_array_add (event_components, g_object_ref (aux));
|
||||
aux = i_cal_component_get_next_component (component, I_CAL_VEVENT_COMPONENT);
|
||||
}
|
||||
|
||||
return g_steal_pointer (&event_components);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_file (GcalImportFileRow *self)
|
||||
{
|
||||
g_autofree gchar *basename = NULL;
|
||||
|
||||
basename = g_file_get_basename (self->file);
|
||||
gtk_label_set_label (self->filename_label, basename);
|
||||
|
||||
gcal_importer_import_file (self->file,
|
||||
self->cancellable,
|
||||
read_calendar_finished_cb,
|
||||
self);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
|
||||
static void
|
||||
read_calendar_finished_cb (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr (GPtrArray) event_components = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
g_autofree gchar *subtitle = NULL;
|
||||
ICalComponent *component;
|
||||
GcalImportFileRow *self;
|
||||
|
||||
self = GCAL_IMPORT_FILE_ROW (user_data);
|
||||
component = gcal_importer_import_file_finish (res, &error);
|
||||
event_components = filter_event_components (component);
|
||||
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (self), !error && event_components && event_components->len > 0);
|
||||
|
||||
if (error || !event_components || event_components->len == 0)
|
||||
return;
|
||||
|
||||
add_events_to_listbox (self, event_components);
|
||||
|
||||
self->ical_components = g_ptr_array_ref (event_components);
|
||||
|
||||
g_signal_emit (self, signals[FILE_LOADED], 0, event_components);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GObject overrides
|
||||
*/
|
||||
|
||||
static void
|
||||
gcal_import_file_row_finalize (GObject *object)
|
||||
{
|
||||
GcalImportFileRow *self = (GcalImportFileRow *)object;
|
||||
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
g_clear_object (&self->file);
|
||||
g_clear_pointer (&self->ical_components, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (gcal_import_file_row_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gcal_import_file_row_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GcalImportFileRow *self = GCAL_IMPORT_FILE_ROW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_FILE:
|
||||
g_value_set_object (value, self->file);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gcal_import_file_row_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GcalImportFileRow *self = GCAL_IMPORT_FILE_ROW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_FILE:
|
||||
g_assert (self->file == NULL);
|
||||
self->file = g_value_dup_object (value);
|
||||
setup_file (self);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gcal_import_file_row_class_init (GcalImportFileRowClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->finalize = gcal_import_file_row_finalize;
|
||||
object_class->get_property = gcal_import_file_row_get_property;
|
||||
object_class->set_property = gcal_import_file_row_set_property;
|
||||
|
||||
signals[FILE_LOADED] = g_signal_new ("file-loaded",
|
||||
GCAL_TYPE_IMPORT_FILE_ROW,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL,
|
||||
g_cclosure_marshal_VOID__BOXED,
|
||||
G_TYPE_NONE,
|
||||
1,
|
||||
G_TYPE_PTR_ARRAY);
|
||||
|
||||
properties[PROP_FILE] = g_param_spec_object ("file",
|
||||
"An ICS file",
|
||||
"An ICS file",
|
||||
G_TYPE_FILE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 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/ui/gui/importer/gcal-import-file-row.ui");
|
||||
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportFileRow, events_listbox);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalImportFileRow, filename_label);
|
||||
}
|
||||
|
||||
static void
|
||||
gcal_import_file_row_init (GcalImportFileRow *self)
|
||||
{
|
||||
self->cancellable = g_cancellable_new ();
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
gcal_import_file_row_new (GFile *file,
|
||||
GtkSizeGroup *title_sizegroup)
|
||||
{
|
||||
GcalImportFileRow *self;
|
||||
|
||||
self = g_object_new (GCAL_TYPE_IMPORT_FILE_ROW,
|
||||
"file", file,
|
||||
NULL);
|
||||
self->title_sizegroup = title_sizegroup;
|
||||
|
||||
return (GtkWidget*) self;
|
||||
}
|
||||
|
||||
void
|
||||
gcal_import_file_row_show_filename (GcalImportFileRow *self)
|
||||
{
|
||||
g_return_if_fail (GCAL_IS_IMPORT_FILE_ROW (self));
|
||||
|
||||
gtk_widget_show (GTK_WIDGET (self->filename_label));
|
||||
}
|
||||
|
||||
GPtrArray*
|
||||
gcal_import_file_row_get_ical_components (GcalImportFileRow *self)
|
||||
{
|
||||
g_return_val_if_fail (GCAL_IS_IMPORT_FILE_ROW (self), NULL);
|
||||
|
||||
return self->ical_components;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* gcal-import-file-row.h
|
||||
*
|
||||
* Copyright 2021 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GCAL_TYPE_IMPORT_FILE_ROW (gcal_import_file_row_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GcalImportFileRow, gcal_import_file_row, GCAL, IMPORT_FILE_ROW, GtkListBoxRow)
|
||||
|
||||
GtkWidget* gcal_import_file_row_new (GFile *file,
|
||||
GtkSizeGroup *title_sizegroup);
|
||||
|
||||
void gcal_import_file_row_show_filename (GcalImportFileRow *self);
|
||||
|
||||
GPtrArray* gcal_import_file_row_get_ical_components (GcalImportFileRow *self);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GcalImportFileRow" parent="GtkListBoxRow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="activatable">False</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
|
||||
<!-- File name label -->
|
||||
<child>
|
||||
<object class="GtkLabel" id="filename_label">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-top">12</property>
|
||||
<property name="xalign">0.0</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold" />
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<!-- Event preview listbox -->
|
||||
<child>
|
||||
<object class="GtkListBox" id="events_listbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="selection-mode">none</property>
|
||||
<style>
|
||||
<class name="content" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</template>
|
||||
</interface>
|
||||
@@ -0,0 +1,173 @@
|
||||
/* gcal-importer.c
|
||||
*
|
||||
* Copyright 2021 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "gcal-importer.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
G_DEFINE_QUARK (ICalErrorEnum, i_cal_error);
|
||||
|
||||
static const gchar*
|
||||
i_cal_error_enum_to_string (ICalErrorEnum ical_error)
|
||||
{
|
||||
switch (ical_error)
|
||||
{
|
||||
case I_CAL_NO_ERROR:
|
||||
return _("No error");
|
||||
|
||||
case I_CAL_BADARG_ERROR:
|
||||
return _("Bad argument to function");
|
||||
|
||||
case I_CAL_NEWFAILED_ERROR:
|
||||
case I_CAL_ALLOCATION_ERROR:
|
||||
return _("Failed to allocate a new object in memory");
|
||||
|
||||
case I_CAL_MALFORMEDDATA_ERROR:
|
||||
return _("File is malformed, invalid, or corrupted");
|
||||
|
||||
case I_CAL_PARSE_ERROR:
|
||||
return _("Failed to parse the calendar contents");
|
||||
|
||||
case I_CAL_FILE_ERROR:
|
||||
return _("Failed to read file");
|
||||
|
||||
case I_CAL_INTERNAL_ERROR:
|
||||
case I_CAL_USAGE_ERROR:
|
||||
case I_CAL_UNIMPLEMENTED_ERROR:
|
||||
case I_CAL_UNKNOWN_ERROR:
|
||||
default:
|
||||
return _("Internal error");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
read_file_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
g_autoptr (GFileInfo) file_info = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
g_autofree gchar *contents = NULL;
|
||||
g_autofree gchar *path = NULL;
|
||||
ICalComponent *component;
|
||||
ICalErrorEnum ical_error;
|
||||
gsize length;
|
||||
GFile *file;
|
||||
|
||||
file = task_data;
|
||||
file_info = g_file_query_info (file,
|
||||
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
cancellable,
|
||||
&error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_task_return_error (task, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_strcmp0 (g_file_info_get_content_type (file_info), "text/calendar") != 0)
|
||||
{
|
||||
g_task_return_new_error (task,
|
||||
G_FILE_ERROR,
|
||||
G_FILE_ERROR_FAILED,
|
||||
"%s",
|
||||
_("File is not an iCalendar (.ics) file"));
|
||||
return;
|
||||
}
|
||||
|
||||
path = g_file_get_path (file);
|
||||
g_file_get_contents (path, &contents, &length, &error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_task_return_error (task, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
component = i_cal_parser_parse_string (contents);
|
||||
ical_error = i_cal_errno_return ();
|
||||
|
||||
if (ical_error != I_CAL_NO_ERROR)
|
||||
{
|
||||
g_task_return_new_error (task,
|
||||
I_CAL_ERROR,
|
||||
ical_error,
|
||||
"%s",
|
||||
i_cal_error_enum_to_string (ical_error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!component)
|
||||
{
|
||||
g_task_return_new_error (task,
|
||||
I_CAL_ERROR,
|
||||
I_CAL_MALFORMEDDATA_ERROR,
|
||||
"%s",
|
||||
i_cal_error_enum_to_string (I_CAL_MALFORMEDDATA_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
g_task_return_pointer (task, g_object_ref (component), g_object_unref);
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_importer_import_file:
|
||||
* @file: a #GFile
|
||||
* @cancellable: (nullable): a #GCancellable
|
||||
* @callback: a #GAsyncReadyCallback to execute upon completion
|
||||
* @user_data: closure data for @callback
|
||||
*
|
||||
* Import an ICS file.
|
||||
*/
|
||||
void
|
||||
gcal_importer_import_file (GFile *file,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
|
||||
g_autoptr (GTask) task = NULL;
|
||||
|
||||
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||||
|
||||
task = g_task_new (NULL, cancellable, callback, user_data);
|
||||
g_task_set_task_data (task, g_object_ref (file), g_object_unref);
|
||||
g_task_set_source_tag (task, gcal_importer_import_file);
|
||||
g_task_run_in_thread (task, read_file_in_thread);
|
||||
}
|
||||
|
||||
/**
|
||||
* gcal_importer_do_something_finish:
|
||||
* @result: a #GAsyncResult provided to callback
|
||||
* @error: a location for a #GError, or %NULL
|
||||
*
|
||||
* Returns: (nullable): an #ICalComponent
|
||||
*/
|
||||
ICalComponent*
|
||||
gcal_importer_import_file_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
|
||||
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/* gcal-importer.h
|
||||
*
|
||||
* Copyright 2021 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <libecal/libecal.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define I_CAL_ERROR i_cal_error_quark ()
|
||||
GQuark i_cal_error_quark (void);
|
||||
|
||||
void gcal_importer_import_file (GFile *file,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
ICalComponent* gcal_importer_import_file_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/org/gnome/calendar/ui/gui/importer">
|
||||
<file compressed="true">gcal-import-dialog.ui</file>
|
||||
<file compressed="true">gcal-import-file-row.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
@@ -0,0 +1,13 @@
|
||||
calendar_incs += include_directories('.')
|
||||
|
||||
built_sources += gnome.compile_resources(
|
||||
'importer-resources',
|
||||
'importer.gresource.xml',
|
||||
c_name: 'importer',
|
||||
)
|
||||
|
||||
sources += files(
|
||||
'gcal-import-dialog.c',
|
||||
'gcal-import-file-row.c',
|
||||
'gcal-importer.c',
|
||||
)
|
||||
@@ -2,6 +2,7 @@ subdir('calendar-management')
|
||||
subdir('event-editor')
|
||||
subdir('gtk')
|
||||
subdir('icons')
|
||||
subdir('importer')
|
||||
subdir('views')
|
||||
|
||||
calendar_incs += include_directories('.')
|
||||
|
||||
Reference in New Issue
Block a user