[gnome-calendar/wip/pandusonu/week-view] week-grid: implement drag n' drop



commit 530aaa24c471be50fcd846c9d0280dc723238bf9
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Tue Jan 17 10:56:32 2017 -0200

    week-grid: implement drag n' drop

 src/views/gcal-week-grid.c |  174 +++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 172 insertions(+), 2 deletions(-)
---
diff --git a/src/views/gcal-week-grid.c b/src/views/gcal-week-grid.c
index 4a727a9..4bafb87 100644
--- a/src/views/gcal-week-grid.c
+++ b/src/views/gcal-week-grid.c
@@ -66,6 +66,7 @@ struct _GcalWeekGrid
    */
   gint                selection_start;
   gint                selection_end;
+  gint                dnd_cell;
 
   GcalManager        *manager;
 };
@@ -441,6 +442,7 @@ gcal_week_grid_draw (GtkWidget *widget,
   GdkRGBA color;
 
   gboolean ltr;
+  gdouble minutes_height;
   gdouble x, column_width;
   gint i, width, height, today_column;
 
@@ -464,13 +466,13 @@ gcal_week_grid_draw (GtkWidget *widget,
   width = gtk_widget_get_allocated_width (widget);
   height = gtk_widget_get_allocated_height (widget);
   column_width = width / 7.0;
+  minutes_height = (gdouble) height / MINUTES_PER_DAY;
 
   cairo_set_line_width (cr, 0.65);
 
   /* First, draw the selection */
   if (self->selection_start != -1 && self->selection_end != -1)
     {
-      gdouble minutes_height;
       gint selection_height;
       gint column;
       gint start;
@@ -487,7 +489,6 @@ gcal_week_grid_draw (GtkWidget *widget,
           start = start - end;
         }
 
-      minutes_height = (gdouble) height / MINUTES_PER_DAY;
       column = start * 30 / MINUTES_PER_DAY;
       selection_height = (end - start + 1) * 30 * minutes_height;
 
@@ -506,6 +507,24 @@ gcal_week_grid_draw (GtkWidget *widget,
       gtk_style_context_restore (context);
     }
 
+  /* Drag and Drop highlight */
+  if (self->dnd_cell != -1)
+    {
+      gdouble cell_height;
+      gint column, row;
+
+      cell_height = minutes_height * 30;
+      column = self->dnd_cell / (MINUTES_PER_DAY / 30);
+      row = self->dnd_cell - column * 48;
+
+      gtk_render_background (context,
+                             cr,
+                             column * column_width,
+                             row * cell_height,
+                             column_width,
+                             cell_height);
+    }
+
   /* Today column */
   today_column = get_today_column (GCAL_WEEK_GRID (widget));
 
@@ -902,6 +921,146 @@ gcal_week_grid_button_release (GtkWidget      *widget,
   return GDK_EVENT_STOP;
 }
 
+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_COPY,
+                   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)
+{
+  GcalWeekGrid *self;
+  g_autoptr (GDateTime) week_start;
+  g_autoptr (GDateTime) dnd_date;
+  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);
+
+  week_start = dnd_date = NULL;
+
+  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));
+  week_start = 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.
+   */
+  if (gcal_event_get_date_end (event))
+    timespan = g_date_time_difference (gcal_event_get_date_end (event), gcal_event_get_date_start (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 (event, FALSE);
+  gcal_event_set_date_start (event, dnd_date);
+
+  if (gcal_event_get_date_end (event))
+    {
+      g_autoptr (GDateTime) new_end = g_date_time_add (dnd_date, timespan);
+
+      gcal_event_set_date_end (event, new_end);
+    }
+
+  /* Commit the changes */
+  gcal_manager_update_event (self->manager, event);
+
+  /* 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);
+}
+
 static void
 gcal_week_grid_class_init (GcalWeekGridClass *klass)
 {
@@ -927,6 +1086,9 @@ gcal_week_grid_class_init (GcalWeekGridClass *klass)
   widget_class->button_press_event = gcal_week_grid_button_press;
   widget_class->motion_notify_event = gcal_week_grid_motion_notify_event;
   widget_class->button_release_event = gcal_week_grid_button_release;
+  widget_class->drag_motion = gcal_week_grid_drag_motion;
+  widget_class->drag_leave = gcal_week_grid_drag_leave;
+  widget_class->drag_drop = gcal_week_grid_drag_drop;
 
   properties[PROP_ACTIVE_DATE] = g_param_spec_boxed ("active-date",
                                                      "Date",
@@ -955,8 +1117,16 @@ gcal_week_grid_init (GcalWeekGrid *self)
 
   self->selection_start = -1;
   self->selection_end = -1;
+  self->dnd_cell = -1;
 
   self->events = gcal_range_tree_new ();
+
+  /* Setup the week view as a drag n' drop destination */
+  gtk_drag_dest_set (GTK_WIDGET (self),
+                     0,
+                     NULL,
+                     0,
+                     GDK_ACTION_MOVE);
 }
 
 /* Public API */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]