[gnome-calendar/gbsneto/gtk4: 44/46] Reimplement event drag n' drop
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-calendar/gbsneto/gtk4: 44/46] Reimplement event drag n' drop
- Date: Sat, 12 Feb 2022 16:25:52 +0000 (UTC)
commit ee5acbd493ab82ae4f18080dc926b969f1789f93
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Sat Feb 12 09:49:51 2022 -0300
Reimplement event drag n' drop
Reintroduce the code using GTK4 tooling for in-process DnD.
This is looking much better!
src/core/gcal-recurrence.h | 1 +
src/gui/views/gcal-month-cell.c | 201 +++++++++++------------
src/gui/views/gcal-week-grid.c | 325 ++++++++++++++++++-------------------
src/gui/views/gcal-week-header.c | 334 +++++++++++++++++++++------------------
src/theme/Adwaita.css | 3 +
src/utils/gcal-utils.c | 83 ++++++++++
src/utils/gcal-utils.h | 9 ++
7 files changed, 529 insertions(+), 427 deletions(-)
---
diff --git a/src/core/gcal-recurrence.h b/src/core/gcal-recurrence.h
index 340db66a..7b536706 100644
--- a/src/core/gcal-recurrence.h
+++ b/src/core/gcal-recurrence.h
@@ -48,6 +48,7 @@ typedef enum
typedef enum
{
+ GCAL_RECURRENCE_MOD_NONE = 0,
GCAL_RECURRENCE_MOD_THIS_ONLY = E_CAL_OBJ_MOD_THIS,
GCAL_RECURRENCE_MOD_THIS_AND_FUTURE = E_CAL_OBJ_MOD_THIS_AND_FUTURE,
GCAL_RECURRENCE_MOD_ALL = E_CAL_OBJ_MOD_ALL,
diff --git a/src/gui/views/gcal-month-cell.c b/src/gui/views/gcal-month-cell.c
index c0b6cc3c..1e38fa5e 100644
--- a/src/gui/views/gcal-month-cell.c
+++ b/src/gui/views/gcal-month-cell.c
@@ -88,92 +88,23 @@ update_style_flags (GcalMonthCell *self)
gtk_widget_remove_css_class (GTK_WIDGET (self), "today");
}
-
-/*
- * Callbacks
- */
-
static void
-day_changed_cb (GcalClock *clock,
- GcalMonthCell *self)
+move_event (GcalMonthCell *self,
+ GcalEvent *event,
+ GcalRecurrenceModType mod_type)
{
- update_style_flags (self);
-}
-static void
-overflow_button_clicked_cb (GtkWidget *button,
- GcalMonthCell *self)
-{
- g_signal_emit (self, signals[SHOW_OVERFLOW], 0, button);
-}
-
-/*
- * GtkWidget overrides
- */
-
-#if 0 // TODO: DND
-
-static gboolean
-gcal_month_cell_drag_motion (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
- GcalMonthCell *self;
-
- self = GCAL_MONTH_CELL (widget);
-
- if (self->different_month)
- gtk_drag_unhighlight (widget);
- else
- gtk_drag_highlight (widget);
-
- gdk_drag_status (context, self->different_month ? 0 : GDK_ACTION_MOVE, time);
-
- return !self->different_month;
-}
-
-static gboolean
-gcal_month_cell_drag_drop (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
g_autoptr (GcalEvent) changed_event = NULL;
- GcalRecurrenceModType mod;
- GcalMonthCell *self;
- GcalCalendar *calendar;
- GtkWidget *event_widget;
- GDateTime *start_dt, *end_dt;
- GcalEvent *event;
+ g_autoptr (GDateTime) start_dt = NULL;
+ GTimeSpan timespan = 0;
+ GDateTime *end_dt;
gint diff;
gint start_month, current_month;
gint start_year, current_year;
- GTimeSpan timespan = 0;
-
- self = GCAL_MONTH_CELL (widget);
- event_widget = gtk_drag_get_source_widget (context);
- mod = GCAL_RECURRENCE_MOD_THIS_ONLY;
GCAL_ENTRY;
- if (!GCAL_IS_EVENT_WIDGET (event_widget))
- GCAL_RETURN (FALSE);
-
- if (self->different_month)
- GCAL_RETURN (FALSE);
-
- event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (event_widget));
changed_event = gcal_event_new_from_event (event);
- calendar = gcal_event_get_calendar (changed_event);
-
- if (gcal_event_has_recurrence (changed_event) &&
- !ask_recurrence_modification_type (widget, &mod, calendar))
- {
- GCAL_GOTO (out);
- }
/* Move the event's date */
start_dt = gcal_event_get_date_start (changed_event);
@@ -194,47 +125,107 @@ gcal_month_cell_drag_drop (GtkWidget *widget,
diff = g_date_time_get_day_of_month (self->date) - g_date_time_get_day_of_month (start_dt);
- if (diff != 0 ||
- current_month != start_month ||
- current_year != start_year)
+ if (diff != 0 || current_month != start_month || current_year != start_year)
{
- g_autoptr (GDateTime) new_start = NULL;
-
- new_start = g_date_time_add_days (start_dt, diff);
+ g_autoptr (GDateTime) new_start = g_date_time_add_days (start_dt, diff);
gcal_event_set_date_start (changed_event, new_start);
/* The event may have a NULL end date, so we have to check it here */
if (end_dt)
{
- GDateTime *new_end = g_date_time_add (new_start, timespan);
+ g_autoptr (GDateTime) new_end = g_date_time_add (new_start, timespan);
gcal_event_set_date_end (changed_event, new_end);
- g_clear_pointer (&new_end, g_date_time_unref);
}
- gcal_manager_update_event (gcal_context_get_manager (self->context), changed_event, mod);
+ gcal_manager_update_event (gcal_context_get_manager (self->context), changed_event, mod_type);
}
+}
- g_clear_pointer (&start_dt, g_date_time_unref);
-out:
- /* Cancel the DnD */
- gtk_drag_unhighlight (widget);
- gtk_drag_finish (context, TRUE, FALSE, time);
+/*
+ * Callbacks
+ */
- GCAL_RETURN (TRUE);
+static void
+day_changed_cb (GcalClock *clock,
+ GcalMonthCell *self)
+{
+ update_style_flags (self);
+}
+
+static void
+overflow_button_clicked_cb (GtkWidget *button,
+ GcalMonthCell *self)
+{
+ g_signal_emit (self, signals[SHOW_OVERFLOW], 0, button);
+}
+
+static gboolean
+on_drop_target_accept_cb (GtkDropTarget *drop_target,
+ GdkDrop *drop,
+ GcalMonthCell *self)
+{
+ GCAL_ENTRY;
+
+ if ((gdk_drop_get_actions (drop) & gtk_drop_target_get_actions (drop_target)) == 0)
+ GCAL_RETURN (FALSE);
+
+ if (!gdk_content_formats_contain_gtype (gdk_drop_get_formats (drop), GCAL_TYPE_EVENT_WIDGET))
+ GCAL_RETURN (FALSE);
+
+ GCAL_RETURN (!self->different_month);
}
static void
-gcal_month_cell_drag_leave (GtkWidget *widget,
- GdkDragContext *context,
- guint time)
+on_ask_recurrence_response_cb (GcalEvent *event,
+ GcalRecurrenceModType mod_type,
+ gpointer user_data)
+{
+ GcalMonthCell *self = GCAL_MONTH_CELL (user_data);
+
+ if (mod_type != GCAL_RECURRENCE_MOD_NONE)
+ move_event (self, event, mod_type);
+}
+
+static gboolean
+on_drop_target_drop_cb (GtkDropTarget *drop_target,
+ const GValue *value,
+ gdouble x,
+ gdouble y,
+ GcalMonthCell *self)
{
- gtk_drag_unhighlight (widget);
+ GcalEventWidget *event_widget;
+ GcalEvent *event;
+
+ GCAL_ENTRY;
+
+ if (self->different_month)
+ GCAL_RETURN (FALSE);
+
+ if (!G_VALUE_HOLDS (value, GCAL_TYPE_EVENT_WIDGET))
+ GCAL_RETURN (FALSE);
+
+ event_widget = g_value_get_object (value);
+
+ event = gcal_event_widget_get_event (event_widget);
+
+ if (gcal_event_has_recurrence (event))
+ {
+ gcal_utils_ask_recurrence_modification_type (GTK_WIDGET (self),
+ event,
+ on_ask_recurrence_response_cb,
+ self);
+ }
+ else
+ {
+ move_event (self, event, GCAL_RECURRENCE_MOD_THIS_ONLY);
+ }
+
+ GCAL_RETURN (TRUE);
}
-#endif
/*
* GObject overrides
@@ -302,12 +293,6 @@ gcal_month_cell_class_init (GcalMonthCellClass *klass)
object_class->set_property = gcal_month_cell_set_property;
object_class->get_property = gcal_month_cell_get_property;
-#if 0 // TODO: DND
- widget_class->drag_motion = gcal_month_cell_drag_motion;
- widget_class->drag_drop = gcal_month_cell_drag_drop;
- widget_class->drag_leave = gcal_month_cell_drag_leave;
-#endif
-
signals[SHOW_OVERFLOW] = g_signal_new ("show-overflow",
GCAL_TYPE_MONTH_CELL,
G_SIGNAL_RUN_LAST,
@@ -342,16 +327,14 @@ gcal_month_cell_class_init (GcalMonthCellClass *klass)
static void
gcal_month_cell_init (GcalMonthCell *self)
{
+ GtkDropTarget *drop_target;
+
gtk_widget_init_template (GTK_WIDGET (self));
-#if 0 // TODO: DND
- /* Setup the month cell as a drag n' drop destination */
- gtk_drag_dest_set (GTK_WIDGET (self),
- 0,
- NULL,
- 0,
- GDK_ACTION_MOVE);
-#endif
+ drop_target = gtk_drop_target_new (GCAL_TYPE_EVENT_WIDGET, GDK_ACTION_COPY);
+ g_signal_connect (drop_target, "accept", G_CALLBACK (on_drop_target_accept_cb), self);
+ g_signal_connect (drop_target, "drop", G_CALLBACK (on_drop_target_drop_cb), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drop_target));
}
GtkWidget*
diff --git a/src/gui/views/gcal-week-grid.c b/src/gui/views/gcal-week-grid.c
index 3c9236a3..47f77f93 100644
--- a/src/gui/views/gcal-week-grid.c
+++ b/src/gui/views/gcal-week-grid.c
@@ -35,6 +35,13 @@
#include <string.h>
#include <math.h>
+typedef struct
+{
+ GcalWeekGrid *self;
+ GcalEvent *event;
+ gint drop_cell;
+} DropData;
+
typedef struct
{
GtkWidget *widget;
@@ -322,6 +329,156 @@ on_click_gesture_released_cb (GtkGestureClick *click_gesture,
gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_NONE);
}
+static gint
+get_dnd_cell (GcalWeekGrid *self,
+ gdouble x,
+ gdouble y)
+{
+ GtkAllocation alloc;
+ gdouble column_width, cell_height;
+ gint column, row;
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+ column_width = alloc.width / 7.0;
+ cell_height = alloc.height / 48.0;
+ column = floor (x / column_width);
+ row = y / cell_height;
+
+ return column * 48 + row;
+}
+
+static void
+move_event_to_cell (GcalWeekGrid *self,
+ GcalEvent *event,
+ guint cell,
+ GcalRecurrenceModType mod_type)
+{
+
+ g_autoptr (GDateTime) week_start = NULL;
+ g_autoptr (GDateTime) dnd_date = NULL;
+ g_autoptr (GDateTime) new_end = NULL;
+ g_autoptr (GcalEvent) changed_event = NULL;
+ GTimeSpan timespan = 0;
+
+ /* RTL languages swap the drop cell column */
+ if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
+ {
+ gint column, row;
+
+ column = cell / (MINUTES_PER_DAY / 30);
+ row = cell - column * 48;
+
+ cell = (6 - column) * 48 + row;
+ }
+
+ changed_event = gcal_event_new_from_event (event);
+ week_start = gcal_date_time_get_start_of_week (self->active_date);
+ dnd_date = g_date_time_add_minutes (week_start, cell * 30);
+
+ /*
+ * Calculate the diff between the dropped cell and the event's start date,
+ * so we can update the end date accordingly.
+ */
+ timespan = g_date_time_difference (gcal_event_get_date_end (changed_event), gcal_event_get_date_start
(changed_event));
+
+ /*
+ * Set the event's start and end dates. Since the event may have a
+ * NULL end date, so we have to check it here
+ */
+ gcal_event_set_all_day (changed_event, FALSE);
+ gcal_event_set_date_start (changed_event, dnd_date);
+
+ /* Setup the new end date */
+ new_end = g_date_time_add (dnd_date, timespan);
+ gcal_event_set_date_end (changed_event, new_end);
+
+ /* Commit the changes */
+ gcal_manager_update_event (gcal_context_get_manager (self->context), changed_event, mod_type);
+}
+
+static void
+on_ask_recurrence_response_cb (GcalEvent *event,
+ GcalRecurrenceModType mod_type,
+ gpointer user_data)
+{
+ DropData *data = user_data;
+
+ if (mod_type != GCAL_RECURRENCE_MOD_NONE)
+ move_event_to_cell (data->self, data->event, data->drop_cell, mod_type);
+
+ g_clear_object (&data->event);
+ g_clear_pointer (&data, g_free);
+}
+
+static gboolean
+on_drop_target_drop_cb (GtkDropTarget *drop_target,
+ const GValue *value,
+ gdouble x,
+ gdouble y,
+ GcalWeekGrid *self)
+{
+ GcalEventWidget *event_widget;
+ GcalEvent *event;
+ gint cell;
+
+ GCAL_ENTRY;
+
+ if (!G_VALUE_HOLDS (value, GCAL_TYPE_EVENT_WIDGET))
+ GCAL_RETURN (FALSE);
+
+ cell = get_dnd_cell (self, x, y);
+ event_widget = g_value_get_object (value);
+ event = gcal_event_widget_get_event (event_widget);
+
+ if (gcal_event_has_recurrence (event))
+ {
+ DropData *data;
+
+ data = g_new0 (DropData, 1);
+ data->self = self;
+ data->event = g_object_ref (event);
+ data->drop_cell = cell;
+
+ gcal_utils_ask_recurrence_modification_type (GTK_WIDGET (self),
+ event,
+ on_ask_recurrence_response_cb,
+ data);
+ }
+ else
+ {
+ move_event_to_cell (self, event, cell, GCAL_RECURRENCE_MOD_THIS_ONLY);
+ }
+
+ GCAL_RETURN (TRUE);
+}
+
+static void
+on_drop_target_leave_cb (GtkDropTarget *drop_target,
+ GcalWeekGrid *self)
+{
+ GCAL_ENTRY;
+
+ self->dnd_cell = -1;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+
+ GCAL_EXIT;
+}
+
+static GdkDragAction
+on_drop_target_motion_cb (GtkDropTarget *drop_target,
+ gdouble x,
+ gdouble y,
+ GcalWeekGrid *self)
+{
+ GCAL_ENTRY;
+
+ self->dnd_cell = get_dnd_cell (self, x, y);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+
+ GCAL_RETURN (self->dnd_cell != -1 ? GDK_ACTION_COPY : 0);
+}
+
static void
gcal_week_grid_dispose (GObject *object)
{
@@ -689,160 +846,6 @@ gcal_week_grid_size_allocate (GtkWidget *widget,
}
}
-#if 0 // TODO: DND
-static gint
-get_dnd_cell (GtkWidget *widget,
- gint x,
- gint y)
-{
- GtkAllocation alloc;
- gdouble column_width, cell_height;
- gint column, row;
-
- gtk_widget_get_allocation (widget, &alloc);
-
- column_width = alloc.width / 7.0;
- cell_height = alloc.height / 48.0;
- column = floor (x / column_width);
- row = y / cell_height;
-
- return column * 48 + row;
-}
-
-static gboolean
-gcal_week_grid_drag_motion (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
- GcalWeekGrid *self;
-
- self = GCAL_WEEK_GRID (widget);
- self->dnd_cell = get_dnd_cell (widget, x, y);
-
- /* Setup the drag highlight */
- if (self->dnd_cell != -1)
- gtk_drag_highlight (widget);
- else
- gtk_drag_unhighlight (widget);
-
- /*
- * Sets the status of the drag - if it fails, sets the action to 0 and
- * aborts the drag with FALSE.
- */
- gdk_drag_status (context,
- self->dnd_cell == -1 ? 0 : GDK_ACTION_MOVE,
- time);
-
- gtk_widget_queue_draw (widget);
-
- return self->dnd_cell != -1;
-}
-
-static gboolean
-gcal_week_grid_drag_drop (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
- g_autoptr (GDateTime) week_start = NULL;
- g_autoptr (GDateTime) dnd_date = NULL;
- g_autoptr (GDateTime) new_end = NULL;
- g_autoptr (GcalEvent) changed_event = NULL;
- GcalRecurrenceModType mod;
- GcalWeekGrid *self;
- GcalCalendar *calendar;
- GtkWidget *event_widget;
- GcalEvent *event;
- GTimeSpan timespan = 0;
- gboolean ltr;
- gint drop_cell;
-
- self = GCAL_WEEK_GRID (widget);
- ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
- drop_cell = get_dnd_cell (widget, x, y);
- event_widget = gtk_drag_get_source_widget (context);
-
- mod = GCAL_RECURRENCE_MOD_THIS_ONLY;
-
- if (!GCAL_IS_EVENT_WIDGET (event_widget))
- return FALSE;
-
- /* RTL languages swap the drop cell column */
- if (!ltr)
- {
- gint column, row;
-
- column = drop_cell / (MINUTES_PER_DAY / 30);
- row = drop_cell - column * 48;
-
- drop_cell = (6 - column) * 48 + row;
- }
-
- event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (event_widget));
- changed_event = gcal_event_new_from_event (event);
- calendar = gcal_event_get_calendar (changed_event);
-
- if (gcal_event_has_recurrence (changed_event) &&
- !ask_recurrence_modification_type (widget, &mod, calendar))
- {
- goto out;
- }
-
- week_start = gcal_date_time_get_start_of_week (self->active_date);
- dnd_date = g_date_time_add_minutes (week_start, drop_cell * 30);
-
- /*
- * Calculate the diff between the dropped cell and the event's start date,
- * so we can update the end date accordingly.
- */
- timespan = g_date_time_difference (gcal_event_get_date_end (changed_event), gcal_event_get_date_start
(changed_event));
-
- /*
- * Set the event's start and end dates. Since the event may have a
- * NULL end date, so we have to check it here
- */
- gcal_event_set_all_day (changed_event, FALSE);
- gcal_event_set_date_start (changed_event, dnd_date);
-
-
- /* Setup the new end date */
- new_end = g_date_time_add (dnd_date, timespan);
- gcal_event_set_date_end (changed_event, new_end);
-
- /* Commit the changes */
-
- gcal_manager_update_event (gcal_context_get_manager (self->context), changed_event, mod);
-
-out:
- /* Cancel the DnD */
- self->dnd_cell = -1;
- gtk_drag_unhighlight (widget);
-
- gtk_drag_finish (context, TRUE, FALSE, time);
-
- gtk_widget_queue_draw (widget);
-
- return TRUE;
-}
-
-static void
-gcal_week_grid_drag_leave (GtkWidget *widget,
- GdkDragContext *context,
- guint time)
-{
- GcalWeekGrid *self = GCAL_WEEK_GRID (widget);
-
- /* Cancel the drag */
- self->dnd_cell = -1;
- gtk_drag_unhighlight (widget);
-
- gtk_widget_queue_draw (widget);
-}
-#endif
-
static void
gcal_week_grid_class_init (GcalWeekGridClass *klass)
{
@@ -872,6 +875,7 @@ gcal_week_grid_class_init (GcalWeekGridClass *klass)
static void
gcal_week_grid_init (GcalWeekGrid *self)
{
+ GtkDropTarget *drop_target;
GtkGesture *click_gesture;
self->selection_start = -1;
@@ -896,14 +900,11 @@ gcal_week_grid_init (GcalWeekGrid *self)
gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_NONE);
gtk_widget_add_controller (GTK_WIDGET (self), self->motion_controller);
-#if 0 // TODO: DND
- /* Setup the week view as a drag n' drop destination */
- gtk_drag_dest_set (GTK_WIDGET (self),
- 0,
- NULL,
- 0,
- GDK_ACTION_MOVE);
-#endif
+ drop_target = gtk_drop_target_new (GCAL_TYPE_EVENT_WIDGET, GDK_ACTION_COPY);
+ g_signal_connect (drop_target, "drop", G_CALLBACK (on_drop_target_drop_cb), self);
+ g_signal_connect (drop_target, "leave", G_CALLBACK (on_drop_target_leave_cb), self);
+ g_signal_connect (drop_target, "motion", G_CALLBACK (on_drop_target_motion_cb), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drop_target));
}
/* Public API */
diff --git a/src/gui/views/gcal-week-header.c b/src/gui/views/gcal-week-header.c
index e026b736..571f2342 100644
--- a/src/gui/views/gcal-week-header.c
+++ b/src/gui/views/gcal-week-header.c
@@ -60,6 +60,13 @@ typedef struct
GtkWidget *weekday_name_label;
} WeekdayHeader;
+typedef struct
+{
+ GcalWeekHeader *self;
+ GcalEvent *event;
+ gint drop_cell;
+} DropData;
+
struct _GcalWeekHeader
{
GtkBox parent;
@@ -1144,6 +1151,171 @@ on_expand_action_activated (GcalWeekHeader *self,
header_expand (self);
}
+static gint
+get_dnd_cell (GcalWeekHeader *self,
+ gint x,
+ gint y)
+{
+ gdouble column_width;
+
+ column_width = gtk_widget_get_allocated_width (GTK_WIDGET (self)) / 7.0;
+
+ return x / column_width;
+}
+
+static void
+move_event_to_cell (GcalWeekHeader *self,
+ GcalEvent *event,
+ guint cell,
+ GcalRecurrenceModType mod_type)
+{
+ g_autoptr (GDateTime) week_start = NULL;
+ g_autoptr (GDateTime) dnd_date = NULL;
+ g_autoptr (GDateTime) new_end = NULL;
+ g_autoptr (GDateTime) tmp_dt = NULL;
+ g_autoptr (GcalEvent) changed_event = NULL;
+ GDateTime *start_date;
+ GDateTime *end_date;
+ GTimeSpan difference;
+ gboolean turn_all_day;
+
+ GCAL_ENTRY;
+
+ /* RTL languages swap the drop cell column */
+ if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
+ cell = 6 - cell;
+
+ changed_event = gcal_event_new_from_event (event);
+ start_date = gcal_event_get_date_start (changed_event);
+ end_date = gcal_event_get_date_end (changed_event);
+ week_start = gcal_date_time_get_start_of_week (self->active_date);
+
+ turn_all_day = !gcal_event_is_multiday (changed_event) || gcal_event_get_all_day (changed_event);
+
+ if (!turn_all_day)
+ {
+ /*
+ * The only case where we don't touch the timezone is for
+ * timed, multiday events.
+ */
+ tmp_dt = g_date_time_new (g_date_time_get_timezone (start_date),
+ g_date_time_get_year (week_start),
+ g_date_time_get_month (week_start),
+ g_date_time_get_day_of_month (week_start),
+ g_date_time_get_hour (start_date),
+ g_date_time_get_minute (start_date),
+ 0);
+ }
+ else
+ {
+ tmp_dt = g_date_time_new_utc (g_date_time_get_year (week_start),
+ g_date_time_get_month (week_start),
+ g_date_time_get_day_of_month (week_start),
+ 0, 0, 0);
+ }
+ dnd_date = g_date_time_add_days (tmp_dt, cell);
+
+ /* End date */
+ difference = turn_all_day ? 24 : g_date_time_difference (end_date, start_date) / G_TIME_SPAN_HOUR;
+
+ new_end = g_date_time_add_hours (dnd_date, difference);
+ gcal_event_set_date_end (changed_event, new_end);
+
+ /*
+ * Set the start date ~after~ the end date, so we can compare
+ * the event's start and end dates above
+ */
+ gcal_event_set_date_start (changed_event, dnd_date);
+
+ if (turn_all_day)
+ gcal_event_set_all_day (changed_event, TRUE);
+
+ /* Commit the changes */
+ gcal_manager_update_event (gcal_context_get_manager (self->context), changed_event, mod_type);
+}
+
+static void
+on_ask_recurrence_response_cb (GcalEvent *event,
+ GcalRecurrenceModType mod_type,
+ gpointer user_data)
+{
+ DropData *data = user_data;
+
+ if (mod_type != GCAL_RECURRENCE_MOD_NONE)
+ move_event_to_cell (data->self, data->event, data->drop_cell, mod_type);
+
+ g_clear_object (&data->event);
+ g_clear_pointer (&data, g_free);
+}
+
+static gboolean
+on_drop_target_drop_cb (GtkDropTarget *drop_target,
+ const GValue *value,
+ gdouble x,
+ gdouble y,
+ GcalWeekHeader *self)
+{
+ GcalEventWidget *event_widget;
+ GcalEvent *event;
+ gint cell;
+
+ GCAL_ENTRY;
+
+ if (!G_VALUE_HOLDS (value, GCAL_TYPE_EVENT_WIDGET))
+ GCAL_RETURN (FALSE);
+
+ cell = get_dnd_cell (self, x, y);
+ event_widget = g_value_get_object (value);
+ event = gcal_event_widget_get_event (event_widget);
+
+ if (gcal_event_has_recurrence (event))
+ {
+ DropData *data;
+
+ data = g_new0 (DropData, 1);
+ data->self = self;
+ data->event = g_object_ref (event);
+ data->drop_cell = cell;
+
+ gcal_utils_ask_recurrence_modification_type (GTK_WIDGET (self),
+ event,
+ on_ask_recurrence_response_cb,
+ data);
+ }
+ else
+ {
+ move_event_to_cell (self, event, cell, GCAL_RECURRENCE_MOD_THIS_ONLY);
+ }
+
+ GCAL_RETURN (TRUE);
+}
+
+static void
+on_drop_target_leave_cb (GtkDropTarget *drop_target,
+ GcalWeekHeader *self)
+{
+ GCAL_ENTRY;
+
+ self->dnd_cell = -1;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+
+ GCAL_EXIT;
+}
+
+static GdkDragAction
+on_drop_target_motion_cb (GtkDropTarget *drop_target,
+ gdouble x,
+ gdouble y,
+ GcalWeekHeader *self)
+{
+ GCAL_ENTRY;
+
+ self->dnd_cell = get_dnd_cell (self, x, y);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+
+ GCAL_RETURN (self->dnd_cell != -1 ? GDK_ACTION_COPY : 0);
+}
+
/* Drawing area content and size */
static void
@@ -1264,154 +1436,6 @@ gcal_week_header_snapshot (GtkWidget *widget,
g_clear_pointer (&week_end, g_date_time_unref);
}
-#if 0 // TODO: DND
-static gint
-get_dnd_cell (GtkWidget *widget,
- gint x,
- gint y)
-{
- gdouble column_width;
-
- column_width = gtk_widget_get_allocated_width (widget) / 7.0;
-
- return x / column_width;
-}
-
-static gboolean
-gcal_week_header_drag_motion (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
- GcalWeekHeader *self;
-
- self = GCAL_WEEK_HEADER (widget);
- self->dnd_cell = get_dnd_cell (widget, x, y);
-
- /*
- * Sets the status of the drag - if it fails, sets the action to 0 and
- * aborts the drag with FALSE.
- */
- gdk_drag_status (context,
- self->dnd_cell == -1 ? 0 : GDK_ACTION_MOVE,
- time);
-
- gtk_widget_queue_draw (widget);
-
- return self->dnd_cell != -1;
-}
-
-static gboolean
-gcal_week_header_drag_drop (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
- g_autoptr (GDateTime) week_start = NULL;
- g_autoptr (GDateTime) dnd_date = NULL;
- g_autoptr (GDateTime) new_end = NULL;
- g_autoptr (GDateTime) tmp_dt = NULL;
- g_autoptr (GcalEvent) changed_event = NULL;
- GcalWeekHeader *self;
- GDateTime *start_date;
- GDateTime *end_date;
- GTimeSpan difference;
- GtkWidget *event_widget;
- GcalEvent *event;
- gboolean turn_all_day;
- gboolean ltr;
- gint drop_cell;
-
- GCAL_ENTRY;
-
- self = GCAL_WEEK_HEADER (widget);
- ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
- drop_cell = get_dnd_cell (widget, x, y);
- event_widget = gtk_drag_get_source_widget (context);
-
- if (!GCAL_IS_EVENT_WIDGET (event_widget))
- return FALSE;
-
- /* RTL languages swap the drop cell column */
- if (!ltr)
- drop_cell = 6 - drop_cell;
-
- event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (event_widget));
- changed_event = gcal_event_new_from_event (event);
- start_date = gcal_event_get_date_start (changed_event);
- end_date = gcal_event_get_date_end (changed_event);
- week_start = gcal_date_time_get_start_of_week (self->active_date);
-
- turn_all_day = !gcal_event_is_multiday (changed_event) || gcal_event_get_all_day (changed_event);
-
- if (!turn_all_day)
- {
- /*
- * The only case where we don't touch the timezone is for
- * timed, multiday events.
- */
- tmp_dt = g_date_time_new (g_date_time_get_timezone (start_date),
- g_date_time_get_year (week_start),
- g_date_time_get_month (week_start),
- g_date_time_get_day_of_month (week_start),
- g_date_time_get_hour (start_date),
- g_date_time_get_minute (start_date),
- 0);
- }
- else
- {
- tmp_dt = g_date_time_new_utc (g_date_time_get_year (week_start),
- g_date_time_get_month (week_start),
- g_date_time_get_day_of_month (week_start),
- 0, 0, 0);
- }
- dnd_date = g_date_time_add_days (tmp_dt, drop_cell);
-
- /* End date */
- difference = turn_all_day ? 24 : g_date_time_difference (end_date, start_date) / G_TIME_SPAN_HOUR;
-
- new_end = g_date_time_add_hours (dnd_date, difference);
- gcal_event_set_date_end (changed_event, new_end);
-
- /*
- * Set the start date ~after~ the end date, so we can compare
- * the event's start and end dates above
- */
- gcal_event_set_date_start (changed_event, dnd_date);
-
- if (turn_all_day)
- gcal_event_set_all_day (changed_event, TRUE);
-
- /* Commit the changes */
- gcal_manager_update_event (gcal_context_get_manager (self->context),
- changed_event,
- GCAL_RECURRENCE_MOD_THIS_ONLY);
-
- /* Cancel the DnD */
- self->dnd_cell = -1;
-
- gtk_drag_finish (context, TRUE, FALSE, time);
-
- gtk_widget_queue_draw (widget);
-
- GCAL_RETURN (TRUE);
-}
-
-static void
-gcal_week_header_drag_leave (GtkWidget *widget,
- GdkDragContext *context,
- guint time)
-{
- GcalWeekHeader *self = GCAL_WEEK_HEADER (widget);
-
- /* Cancel the drag */
- self->dnd_cell = -1;
-
- gtk_widget_queue_draw (widget);
-}
-#endif
/*
* GObject overrides
@@ -1477,6 +1501,7 @@ gcal_week_header_class_init (GcalWeekHeaderClass *kclass)
static void
gcal_week_header_init (GcalWeekHeader *self)
{
+ GtkDropTarget *drop_target;
gint i;
self->expanded = FALSE;
@@ -1515,14 +1540,11 @@ gcal_week_header_init (GcalWeekHeader *self)
gtk_grid_attach (self->grid, gtk_box_new (GTK_ORIENTATION_VERTICAL, 0), i, 0, 1, 1);
}
-#if 0 // TODO: DND
- /* Setup the week header as a drag n' drop destination */
- gtk_drag_dest_set (GTK_WIDGET (self),
- 0,
- NULL,
- 0,
- GDK_ACTION_MOVE);
-#endif
+ drop_target = gtk_drop_target_new (GCAL_TYPE_EVENT_WIDGET, GDK_ACTION_COPY);
+ g_signal_connect (drop_target, "drop", G_CALLBACK (on_drop_target_drop_cb), self);
+ g_signal_connect (drop_target, "leave", G_CALLBACK (on_drop_target_leave_cb), self);
+ g_signal_connect (drop_target, "motion", G_CALLBACK (on_drop_target_motion_cb), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drop_target));
}
void
diff --git a/src/theme/Adwaita.css b/src/theme/Adwaita.css
index 7090d83e..0e3abe67 100644
--- a/src/theme/Adwaita.css
+++ b/src/theme/Adwaita.css
@@ -328,6 +328,9 @@ weekgrid:selected,
background-color: alpha(@accent_bg_color, 0.25);
}
+weekgrid.dnd, .week-header.dnd {
+ background-color: alpha(@accent_bg_color, 0.25);
+}
/*
* Month cell
diff --git a/src/utils/gcal-utils.c b/src/utils/gcal-utils.c
index 6cff7aeb..576cef06 100644
--- a/src/utils/gcal-utils.c
+++ b/src/utils/gcal-utils.c
@@ -1332,3 +1332,86 @@ out:
if (out_meeting_url)
*out_meeting_url = g_steal_pointer (&meeting_url);
}
+
+typedef struct
+{
+ GcalEvent *event;
+ GcalAskRecurrenceCallback callback;
+ gpointer user_data;
+} AskRecurrenceData;
+
+static void
+on_message_dialog_response_cb (GtkDialog *dialog,
+ gint response,
+ AskRecurrenceData *data)
+{
+ GcalRecurrenceModType mod_type;
+
+ switch (response)
+ {
+ case GTK_RESPONSE_CANCEL:
+ mod_type = GCAL_RECURRENCE_MOD_NONE;
+ break;
+ case GTK_RESPONSE_ACCEPT:
+ mod_type = GCAL_RECURRENCE_MOD_THIS_ONLY;
+ break;
+ case GTK_RESPONSE_OK:
+ mod_type = GCAL_RECURRENCE_MOD_THIS_AND_FUTURE;
+ break;
+ case GTK_RESPONSE_YES:
+ mod_type = GCAL_RECURRENCE_MOD_ALL;
+ break;
+ default:
+ mod_type = GCAL_RECURRENCE_MOD_NONE;
+ break;
+ }
+
+ gtk_window_destroy (GTK_WINDOW (dialog));
+
+ data->callback (data->event, mod_type, data->user_data);
+ g_clear_object (&data->event);
+ g_clear_pointer (&data, g_free);
+}
+
+void
+gcal_utils_ask_recurrence_modification_type (GtkWidget *parent,
+ GcalEvent *event,
+ GcalAskRecurrenceCallback callback,
+ gpointer user_data)
+{
+ AskRecurrenceData *data;
+ GtkDialogFlags flags;
+ ECalClient *client;
+ GtkWidget *dialog;
+
+ data = g_new0 (AskRecurrenceData, 1);
+ data->event = g_object_ref (event);
+ data->callback = callback;
+ data->user_data = user_data;
+
+ flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_native (parent)),
+ flags,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ _("The event you are trying to modify is recurring. The changes you have
selected should be applied to:"));
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ _("_Cancel"),
+ GTK_RESPONSE_CANCEL,
+ _("_Only This Event"),
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ client = gcal_calendar_get_client (gcal_event_get_calendar (event));
+
+ if (!e_client_check_capability (E_CLIENT (client), E_CAL_STATIC_CAPABILITY_NO_THISANDFUTURE))
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Subsequent events"), GTK_RESPONSE_OK);
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("_All events"), GTK_RESPONSE_YES);
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (gtk_widget_get_native (parent)));
+ g_signal_connect (dialog, "response", G_CALLBACK (on_message_dialog_response_cb), data);
+
+ gtk_window_present (GTK_WINDOW (dialog));
+}
diff --git a/src/utils/gcal-utils.h b/src/utils/gcal-utils.h
index 46f50ab7..0985b8c2 100644
--- a/src/utils/gcal-utils.h
+++ b/src/utils/gcal-utils.h
@@ -43,6 +43,10 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (ECalComponent, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GWeatherLocation, gweather_location_unref)
#endif
+typedef void (*GcalAskRecurrenceCallback) (GcalEvent *event,
+ GcalRecurrenceModType modtype,
+ gpointer user_data);
+
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ICalTime, g_object_unref)
gchar* gcal_get_weekday (gint i);
@@ -130,4 +134,9 @@ void gcal_utils_extract_google_section (const gchar
gchar **out_description,
gchar **out_meeting_url);
+void gcal_utils_ask_recurrence_modification_type (GtkWidget *parent,
+ GcalEvent *event,
+ GcalAskRecurrenceCallback callback,
+ gpointer user_data);
+
#endif /* __GCAL_UTILS_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]