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 + */ + +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 +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) +{ + 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); } -static void -gcal_month_cell_drag_leave (GtkWidget *widget, - GdkDragContext *context, - guint time) -{ - gtk_drag_unhighlight (widget); -} - -#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 #include +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__ */