[evolution] Bug 788603 - Ability to reorder mail filtering rules' conditions and actions



commit 0350acb1fd1f01afb5c124704f7a74a70df71fb5
Author: Milan Crha <mcrha redhat com>
Date:   Wed Dec 6 18:36:56 2017 +0100

    Bug 788603 - Ability to reorder mail filtering rules' conditions and actions

 src/e-util/e-filter-rule.c |  304 ++++++++++++++++++++++++++++++++++++++------
 src/mail/em-filter-rule.c  |  299 +++++++++++++++++++++++++++++++++++++------
 2 files changed, 522 insertions(+), 81 deletions(-)
---
diff --git a/src/e-util/e-filter-rule.c b/src/e-util/e-filter-rule.c
index 4b5d7ae..2382b37 100644
--- a/src/e-util/e-filter-rule.c
+++ b/src/e-util/e-filter-rule.c
@@ -55,7 +55,10 @@ struct _FilterPartData {
 struct _FilterRuleData {
        EFilterRule *rule;
        ERuleContext *context;
-       GtkWidget *parts;
+       GtkGrid *parts_grid;
+       GtkWidget *drag_widget;
+
+       gint n_rows;
 };
 
 enum {
@@ -181,31 +184,196 @@ get_rule_part_widget (ERuleContext *context,
        return hbox;
 }
 
+enum {
+       DND_TYPE_FILTER_RULE,
+       N_DND_TYPES
+};
+
+static GtkTargetEntry dnd_types[] = {
+       { (gchar *) "x-evolution-filter-rule", GTK_TARGET_SAME_APP, DND_TYPE_FILTER_RULE }
+};
+
+static GdkAtom dnd_atoms[N_DND_TYPES] = { 0 };
+
+static void
+event_box_drag_begin (GtkWidget *widget,
+                     GdkDragContext *context,
+                     gpointer user_data)
+{
+       FilterRuleData *data = user_data;
+       cairo_surface_t *surface;
+       cairo_t *cr;
+       GtkStyleContext *style_context;
+
+       data->drag_widget = widget;
+
+       /* Just 1x1 dot as an image. No need to have there real icon,
+          because the move is done immediately */
+       surface = gdk_window_create_similar_surface ( gtk_widget_get_window (widget), CAIRO_CONTENT_COLOR, 1, 
1);
+       style_context = gtk_widget_get_style_context (widget);
+       cr = cairo_create (surface);
+       gtk_render_background (style_context, cr, 0, 0, 1, 1);
+       cairo_destroy (cr);
+
+       cairo_surface_set_device_offset (surface, 0, 0);
+
+       gtk_drag_set_icon_surface (context, surface);
+}
+
+static gboolean
+event_box_drag_drop (GtkWidget *widget,
+                    GdkDragContext *context,
+                    gint x,
+                    gint y,
+                    guint time,
+                    gpointer user_data)
+{
+       FilterRuleData *data = user_data;
+
+       data->drag_widget = NULL;
+
+       return FALSE;
+}
+
+static void
+event_box_drag_end (GtkWidget *widget,
+                   GdkDragContext *context,
+                   gpointer user_data)
+{
+       FilterRuleData *data = user_data;
+
+       data->drag_widget = NULL;
+}
+
+static gboolean
+event_box_drag_motion_cb (GtkWidget *widget,
+                         GdkDragContext *context,
+                         gint x,
+                         gint y,
+                         guint time,
+                         gpointer user_data)
+{
+       FilterRuleData *data = user_data;
+
+       gdk_drag_status (context, widget == data->drag_widget ? 0 : GDK_ACTION_MOVE, time);
+
+       if (widget != data->drag_widget) {
+               gint index, index_src = -1, index_des = -1;
+
+               for (index = 0; index < data->n_rows && (index_src == -1 || index_des == -1); index++) {
+                       GtkWidget *event_box;
+
+                       event_box = gtk_grid_get_child_at (data->parts_grid, 0, index);
+                       if (event_box == data->drag_widget) {
+                               index_src = index;
+                       } else if (event_box == widget) {
+                               index_des = index;
+                       }
+               }
+
+               g_warn_if_fail (index_src != -1);
+               g_warn_if_fail (index_des != -1);
+               g_warn_if_fail (index_src != index_des);
+
+               if (index_src != -1 && index_des != -1 && index_src != index_des) {
+                       GtkWidget *event_box, *content, *remove_button;
+                       gpointer rule;
+
+                       /* Move internal data first */
+                       rule = g_list_nth_data (data->rule->parts, index_src);
+                       data->rule->parts = g_list_remove (data->rule->parts, rule);
+                       data->rule->parts = g_list_insert (data->rule->parts, rule, index_des);
+
+                       /* Then the UI part */
+                       event_box = gtk_grid_get_child_at (data->parts_grid, 0, index_src);
+                       content = gtk_grid_get_child_at (data->parts_grid, 1, index_src);
+                       remove_button = gtk_grid_get_child_at (data->parts_grid, 2, index_src);
+
+                       g_warn_if_fail (event_box != NULL);
+                       g_warn_if_fail (content != NULL);
+                       g_warn_if_fail (remove_button != NULL);
+
+                       g_object_ref (event_box);
+                       g_object_ref (content);
+                       g_object_ref (remove_button);
+
+                       gtk_grid_remove_row (data->parts_grid, index_src);
+                       gtk_grid_insert_row (data->parts_grid, index_des);
+                       gtk_grid_attach (data->parts_grid, event_box, 0, index_des, 1, 1);
+                       gtk_grid_attach (data->parts_grid, content, 1, index_des, 1, 1);
+                       gtk_grid_attach (data->parts_grid, remove_button, 2, index_des, 1, 1);
+
+                       g_object_unref (event_box);
+                       g_object_unref (content);
+                       g_object_unref (remove_button);
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean
+rule_widget_drag_motion_cb (GtkWidget *widget,
+                           GdkDragContext *context,
+                           gint x,
+                           gint y,
+                           guint time,
+                           gpointer user_data)
+{
+       FilterRuleData *data = user_data;
+       gint ii;
+
+       for (ii = 0; ii < data->n_rows; ii++) {
+               if (gtk_grid_get_child_at (data->parts_grid, 1, ii) == widget) {
+                       return event_box_drag_motion_cb (gtk_grid_get_child_at (data->parts_grid, 0, ii),
+                               context, x, y, time, user_data);
+               }
+       }
+
+       gdk_drag_status (context, 0, time);
+
+       return FALSE;
+}
+
 static void
 less_parts (GtkWidget *button,
             FilterRuleData *data)
 {
        EFilterPart *part;
-       GtkWidget *rule;
+       GtkWidget *content = NULL;
        FilterPartData *part_data;
+       gint index;
 
        if (g_list_length (data->rule->parts) < 1)
                return;
 
-       rule = g_object_get_data ((GObject *) button, "rule");
-       part_data = g_object_get_data ((GObject *) rule, "data");
+       for (index = 0; index < data->n_rows; index++) {
+               if (button == gtk_grid_get_child_at (data->parts_grid, 2, index)) {
+                       content = gtk_grid_get_child_at (data->parts_grid, 1, index);
+                       break;
+               }
+       }
+
+       g_return_if_fail (content != NULL);
+
+       part_data = g_object_get_data ((GObject *) content, "data");
 
        g_return_if_fail (part_data != NULL);
 
        part = part_data->part;
 
+       index = g_list_index (data->rule->parts, part);
+       g_warn_if_fail (index >= 0);
+
        /* remove the part from the list */
        e_filter_rule_remove_part (data->rule, part);
        g_object_unref (part);
 
        /* and from the display */
-       gtk_container_remove (GTK_CONTAINER (data->parts), rule);
-       gtk_container_remove (GTK_CONTAINER (data->parts), button);
+       if (index >= 0) {
+               gtk_grid_remove_row (data->parts_grid, index);
+               data->n_rows--;
+       }
 }
 
 static void
@@ -215,21 +383,70 @@ attach_rule (GtkWidget *rule,
              gint row)
 {
        GtkWidget *remove;
-
-       gtk_table_attach (
-               GTK_TABLE (data->parts), rule, 0, 1, row, row + 1,
-               GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       GtkWidget *event_box, *label;
+
+       event_box = gtk_event_box_new ();
+       label = gtk_label_new ("⇕");
+       gtk_container_add (GTK_CONTAINER (event_box), label);
+       gtk_widget_set_sensitive (label, FALSE);
+       gtk_widget_show (label);
+
+       g_object_set (G_OBJECT (event_box),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", FALSE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "visible", TRUE,
+               NULL);
+
+       g_object_set (G_OBJECT (rule),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_CENTER,
+               "vexpand", FALSE,
+               NULL);
 
        remove = e_dialog_button_new_with_icon ("list-remove", _("_Remove"));
-       g_object_set_data ((GObject *) remove, "rule", rule);
+       g_object_set (G_OBJECT (remove),
+               "halign", GTK_ALIGN_START,
+               "hexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "vexpand", FALSE,
+               "visible", TRUE,
+               NULL);
+
        g_signal_connect (
                remove, "clicked",
                G_CALLBACK (less_parts), data);
-       gtk_table_attach (
-               GTK_TABLE (data->parts), remove, 1, 2, row, row + 1,
-               0, 0, 0, 0);
 
-       gtk_widget_show (remove);
+       gtk_grid_insert_row (data->parts_grid, row);
+       gtk_grid_attach (data->parts_grid, event_box, 0, row, 1, 1);
+       gtk_grid_attach (data->parts_grid, rule, 1, row, 1, 1);
+       gtk_grid_attach (data->parts_grid, remove, 2, row, 1, 1);
+
+       if (!dnd_atoms[0]) {
+               gint ii;
+
+               for (ii = 0; ii < N_DND_TYPES; ii++)
+                       dnd_atoms[ii] = gdk_atom_intern (dnd_types[ii].target, FALSE);
+       }
+
+       gtk_drag_source_set (event_box, GDK_BUTTON1_MASK, dnd_types, N_DND_TYPES, GDK_ACTION_MOVE);
+       gtk_drag_dest_set (event_box, GTK_DEST_DEFAULT_MOTION, dnd_types, N_DND_TYPES, GDK_ACTION_MOVE);
+
+       g_signal_connect (event_box, "drag-begin",
+               G_CALLBACK (event_box_drag_begin), data);
+       g_signal_connect (event_box, "drag-motion",
+               G_CALLBACK (event_box_drag_motion_cb), data);
+       g_signal_connect (event_box, "drag-drop",
+               G_CALLBACK (event_box_drag_drop), data);
+       g_signal_connect (event_box, "drag-end",
+               G_CALLBACK (event_box_drag_end), data);
+
+       gtk_drag_dest_set (rule, GTK_DEST_DEFAULT_MOTION, dnd_types, N_DND_TYPES, GDK_ACTION_MOVE);
+
+       g_signal_connect (rule, "drag-motion",
+               G_CALLBACK (rule_widget_drag_motion_cb), data);
 }
 
 static void
@@ -249,8 +466,8 @@ do_grab_focus_cb (GtkWidget *widget,
        }
 }
 
-static void parts_mapped_cb (GtkWidget *widget,
-                            GtkScrolledWindow *scrolled_window);
+static void parts_grid_mapped_cb (GtkWidget *widget,
+                                 GtkScrolledWindow *scrolled_window);
 
 static void
 more_parts (GtkWidget *button,
@@ -278,15 +495,13 @@ more_parts (GtkWidget *button,
        new = e_rule_context_next_part (data->context, NULL);
        if (new) {
                GtkWidget *w;
-               guint rows;
 
                new = e_filter_part_clone (new);
                e_filter_rule_add_part (data->rule, new);
                w = get_rule_part_widget (data->context, new, data->rule);
 
-               g_object_get (data->parts, "n-rows", &rows, NULL);
-               gtk_table_resize (GTK_TABLE (data->parts), rows + 1, 2);
-               attach_rule (w, data, new, rows);
+               attach_rule (w, data, new, data->n_rows);
+               data->n_rows++;
 
                if (GTK_IS_CONTAINER (w)) {
                        gboolean done = FALSE;
@@ -309,7 +524,7 @@ more_parts (GtkWidget *button,
                                gtk_adjustment_set_value (adjustment, upper);
                        }
 
-                       parts_mapped_cb (NULL, GTK_SCROLLED_WINDOW (w));
+                       parts_grid_mapped_cb (NULL, GTK_SCROLLED_WINDOW (w));
                }
        }
 }
@@ -730,8 +945,8 @@ ensure_scrolled_height_cb (GtkAdjustment *adj,
 }
 
 static void
-parts_mapped_cb (GtkWidget *widget,
-                GtkScrolledWindow *scrolled_window)
+parts_grid_mapped_cb (GtkWidget *widget,
+                     GtkScrolledWindow *scrolled_window)
 {
        g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
 
@@ -743,16 +958,16 @@ static GtkWidget *
 filter_rule_get_widget (EFilterRule *rule,
                         ERuleContext *context)
 {
-       GtkGrid *hgrid, *vgrid, *inframe;
-       GtkWidget *parts, *add, *label, *name, *w;
+       GtkGrid *hgrid, *vgrid, *inframe, *parts_grid;
+       GtkWidget *add, *label, *name, *w;
        GtkWidget *combobox;
        GtkWidget *scrolledwindow;
        GtkAdjustment *hadj, *vadj;
-       GList *l;
+       GList *link;
        gchar *text;
        EFilterPart *part;
        FilterRuleData *data;
-       gint rows, i;
+       gint i;
 
        /* this stuff should probably be a table, but the
         * rule parts need to be a vbox */
@@ -795,15 +1010,21 @@ filter_rule_get_widget (EFilterRule *rule,
        gtk_grid_set_column_spacing (hgrid, 12);
        gtk_container_add (GTK_CONTAINER (vgrid), GTK_WIDGET (hgrid));
 
-       /* this is the parts table, it should probably be inside a scrolling list */
-       rows = g_list_length (rule->parts);
-       parts = gtk_table_new (rows, 2, FALSE);
+       parts_grid = GTK_GRID (gtk_grid_new ());
+       g_object_set (G_OBJECT (parts_grid),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               NULL);
 
        /* data for the parts part of the display */
-       data = g_malloc0 (sizeof (*data));
+       data = g_malloc0 (sizeof (FilterRuleData));
        data->context = context;
        data->rule = rule;
-       data->parts = parts;
+       data->parts_grid = parts_grid;
+       data->drag_widget = NULL;
+       data->n_rows = 0;
 
        /* only set to automatically clean up the memory */
        g_object_set_data_full ((GObject *) vgrid, "data", data, g_free);
@@ -903,21 +1124,20 @@ filter_rule_get_widget (EFilterRule *rule,
        gtk_widget_set_valign (GTK_WIDGET (inframe), GTK_ALIGN_FILL);
        gtk_grid_attach_next_to (hgrid, GTK_WIDGET (inframe), label, GTK_POS_RIGHT, 1, 1);
 
-       l = rule->parts;
-       i = 0;
-       while (l) {
-               part = l->data;
+       for (link = rule->parts; link; link = g_list_next (link)) {
+               part = link->data;
                w = get_rule_part_widget (context, part, rule);
-               attach_rule (w, data, part, i++);
-               l = g_list_next (l);
+
+               attach_rule (w, data, part, data->n_rows);
+               data->n_rows++;
        }
 
        hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 1.0, 1.0, 1.0, 1.0));
        vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 1.0, 1.0, 1.0, 1.0));
        scrolledwindow = gtk_scrolled_window_new (hadj, vadj);
 
-       g_signal_connect (parts, "map",
-               G_CALLBACK (parts_mapped_cb), scrolledwindow);
+       g_signal_connect (parts_grid, "map",
+               G_CALLBACK (parts_grid_mapped_cb), scrolledwindow);
        e_signal_connect_notify (
                hadj, "notify::upper",
                G_CALLBACK (ensure_scrolled_width_cb), scrolledwindow);
@@ -930,7 +1150,7 @@ filter_rule_get_widget (EFilterRule *rule,
                GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
 
        gtk_scrolled_window_add_with_viewport (
-               GTK_SCROLLED_WINDOW (scrolledwindow), parts);
+               GTK_SCROLLED_WINDOW (scrolledwindow), GTK_WIDGET (parts_grid));
 
        gtk_widget_set_vexpand (scrolledwindow, TRUE);
        gtk_widget_set_valign (scrolledwindow, GTK_ALIGN_FILL);
diff --git a/src/mail/em-filter-rule.c b/src/mail/em-filter-rule.c
index 051126b..503dd05 100644
--- a/src/mail/em-filter-rule.c
+++ b/src/mail/em-filter-rule.c
@@ -391,36 +391,203 @@ get_rule_part_widget (EMFilterContext *f,
 struct _rule_data {
        EFilterRule *fr;
        EMFilterContext *f;
-       GtkWidget *parts;
+       GtkGrid *parts_grid;
+       GtkWidget *drag_widget;
+
+       gint n_rows;
+};
+
+enum {
+       DND_TYPE_FILTER_ACTION,
+       N_DND_TYPES
+};
+
+static GtkTargetEntry dnd_types[] = {
+       { (gchar *) "x-evolution-filter-action", GTK_TARGET_SAME_APP, DND_TYPE_FILTER_ACTION }
 };
 
+static GdkAtom dnd_atoms[N_DND_TYPES] = { 0 };
+
+static void
+event_box_drag_begin (GtkWidget *widget,
+                     GdkDragContext *context,
+                     gpointer user_data)
+{
+       struct _rule_data *data = user_data;
+       cairo_surface_t *surface;
+       cairo_t *cr;
+       GtkStyleContext *style_context;
+
+       data->drag_widget = widget;
+
+       /* Just 1x1 dot as an image. No need to have there real icon,
+          because the move is done immediately */
+       surface = gdk_window_create_similar_surface ( gtk_widget_get_window (widget), CAIRO_CONTENT_COLOR, 1, 
1);
+       style_context = gtk_widget_get_style_context (widget);
+       cr = cairo_create (surface);
+       gtk_render_background (style_context, cr, 0, 0, 1, 1);
+       cairo_destroy (cr);
+
+       cairo_surface_set_device_offset (surface, 0, 0);
+
+       gtk_drag_set_icon_surface (context, surface);
+}
+
+static gboolean
+event_box_drag_drop (GtkWidget *widget,
+                    GdkDragContext *context,
+                    gint x,
+                    gint y,
+                    guint time,
+                    gpointer user_data)
+{
+       struct _rule_data *data = user_data;
+
+       data->drag_widget = NULL;
+
+       return FALSE;
+}
+
+static void
+event_box_drag_end (GtkWidget *widget,
+                   GdkDragContext *context,
+                   gpointer user_data)
+{
+       struct _rule_data *data = user_data;
+
+       data->drag_widget = NULL;
+}
+
+static gboolean
+event_box_drag_motion_cb (GtkWidget *widget,
+                         GdkDragContext *context,
+                         gint x,
+                         gint y,
+                         guint time,
+                         gpointer user_data)
+{
+       struct _rule_data *data = user_data;
+
+       gdk_drag_status (context, widget == data->drag_widget ? 0 : GDK_ACTION_MOVE, time);
+
+       if (widget != data->drag_widget) {
+               gint index, index_src = -1, index_des = -1;
+
+               for (index = 0; index < data->n_rows && (index_src == -1 || index_des == -1); index++) {
+                       GtkWidget *event_box;
+
+                       event_box = gtk_grid_get_child_at (data->parts_grid, 0, index);
+                       if (event_box == data->drag_widget) {
+                               index_src = index;
+                       } else if (event_box == widget) {
+                               index_des = index;
+                       }
+               }
+
+               g_warn_if_fail (index_src != -1);
+               g_warn_if_fail (index_des != -1);
+               g_warn_if_fail (index_src != index_des);
+
+               if (index_src != -1 && index_des != -1 && index_src != index_des) {
+                       EMFilterRule *fr = (EMFilterRule *) data->fr;
+                       GtkWidget *event_box, *content, *remove_button;
+                       gpointer rule;
+
+                       /* Move internal data first */
+                       rule = g_list_nth_data (fr->actions, index_src);
+                       fr->actions = g_list_remove (fr->actions, rule);
+                       fr->actions = g_list_insert (fr->actions, rule, index_des);
+
+                       /* Then the UI part */
+                       event_box = gtk_grid_get_child_at (data->parts_grid, 0, index_src);
+                       content = gtk_grid_get_child_at (data->parts_grid, 1, index_src);
+                       remove_button = gtk_grid_get_child_at (data->parts_grid, 2, index_src);
+
+                       g_warn_if_fail (event_box != NULL);
+                       g_warn_if_fail (content != NULL);
+                       g_warn_if_fail (remove_button != NULL);
+
+                       g_object_ref (event_box);
+                       g_object_ref (content);
+                       g_object_ref (remove_button);
+
+                       gtk_grid_remove_row (data->parts_grid, index_src);
+                       gtk_grid_insert_row (data->parts_grid, index_des);
+                       gtk_grid_attach (data->parts_grid, event_box, 0, index_des, 1, 1);
+                       gtk_grid_attach (data->parts_grid, content, 1, index_des, 1, 1);
+                       gtk_grid_attach (data->parts_grid, remove_button, 2, index_des, 1, 1);
+
+                       g_object_unref (event_box);
+                       g_object_unref (content);
+                       g_object_unref (remove_button);
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean
+rule_widget_drag_motion_cb (GtkWidget *widget,
+                           GdkDragContext *context,
+                           gint x,
+                           gint y,
+                           guint time,
+                           gpointer user_data)
+{
+       struct _rule_data *data = user_data;
+       gint ii;
+
+       for (ii = 0; ii < data->n_rows; ii++) {
+               if (gtk_grid_get_child_at (data->parts_grid, 1, ii) == widget) {
+                       return event_box_drag_motion_cb (gtk_grid_get_child_at (data->parts_grid, 0, ii),
+                               context, x, y, time, user_data);
+               }
+       }
+
+       gdk_drag_status (context, 0, time);
+
+       return FALSE;
+}
+
 static void
 less_parts (GtkWidget *button,
             struct _rule_data *data)
 {
        EFilterPart *part;
-       GtkWidget *rule;
+       GtkWidget *content = NULL;
        struct _part_data *part_data;
-       GList *l;
+       gint index;
 
-       l =((EMFilterRule *) data->fr)->actions;
-       if (g_list_length (l) < 2)
+       if (g_list_length (((EMFilterRule *) data->fr)->actions) < 2)
                return;
 
-       rule = g_object_get_data ((GObject *) button, "rule");
-       part_data = g_object_get_data ((GObject *) rule, "data");
+       for (index = 0; index < data->n_rows; index++) {
+               if (button == gtk_grid_get_child_at (data->parts_grid, 2, index)) {
+                       content = gtk_grid_get_child_at (data->parts_grid, 1, index);
+                       break;
+               }
+       }
+
+       g_return_if_fail (content != NULL);
+
+       part_data = g_object_get_data ((GObject *) content, "data");
 
        g_return_if_fail (part_data != NULL);
 
        part = part_data->part;
 
+       index = g_list_index (((EMFilterRule *) data->fr)->actions, part);
+       g_warn_if_fail (index >= 0);
+
        /* remove the part from the list */
        em_filter_rule_remove_action ((EMFilterRule *) data->fr, part);
        g_object_unref (part);
 
        /* and from the display */
-       gtk_container_remove (GTK_CONTAINER (data->parts), rule);
-       gtk_container_remove (GTK_CONTAINER (data->parts), button);
+       if (index >= 0) {
+               gtk_grid_remove_row (data->parts_grid, index);
+               data->n_rows--;
+       }
 }
 
 static void
@@ -430,21 +597,70 @@ attach_rule (GtkWidget *rule,
              gint row)
 {
        GtkWidget *remove;
-
-       gtk_table_attach (
-               GTK_TABLE (data->parts), rule, 0, 1, row, row + 1,
-               GTK_EXPAND | GTK_FILL, 0, 0, 0);
+       GtkWidget *event_box, *label;
+
+       event_box = gtk_event_box_new ();
+       label = gtk_label_new ("⇕");
+       gtk_container_add (GTK_CONTAINER (event_box), label);
+       gtk_widget_set_sensitive (label, FALSE);
+       gtk_widget_show (label);
+
+       g_object_set (G_OBJECT (event_box),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", FALSE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "visible", TRUE,
+               NULL);
+
+       g_object_set (G_OBJECT (rule),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_CENTER,
+               "vexpand", FALSE,
+               NULL);
 
        remove = e_dialog_button_new_with_icon ("list-remove", _("_Remove"));
-       g_object_set_data ((GObject *) remove, "rule", rule);
-       /*gtk_button_set_relief(GTK_BUTTON(remove), GTK_RELIEF_NONE);*/
+       g_object_set (G_OBJECT (remove),
+               "halign", GTK_ALIGN_START,
+               "hexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "vexpand", FALSE,
+               "visible", TRUE,
+               NULL);
+
        g_signal_connect (
                remove, "clicked",
                G_CALLBACK (less_parts), data);
-       gtk_table_attach (
-               GTK_TABLE (data->parts), remove, 1, 2, row, row + 1,
-               0, 0, 0, 0);
-       gtk_widget_show (remove);
+
+       gtk_grid_insert_row (data->parts_grid, row);
+       gtk_grid_attach (data->parts_grid, event_box, 0, row, 1, 1);
+       gtk_grid_attach (data->parts_grid, rule, 1, row, 1, 1);
+       gtk_grid_attach (data->parts_grid, remove, 2, row, 1, 1);
+
+       if (!dnd_atoms[0]) {
+               gint ii;
+
+               for (ii = 0; ii < N_DND_TYPES; ii++)
+                       dnd_atoms[ii] = gdk_atom_intern (dnd_types[ii].target, FALSE);
+       }
+
+       gtk_drag_source_set (event_box, GDK_BUTTON1_MASK, dnd_types, N_DND_TYPES, GDK_ACTION_MOVE);
+       gtk_drag_dest_set (event_box, GTK_DEST_DEFAULT_MOTION, dnd_types, N_DND_TYPES, GDK_ACTION_MOVE);
+
+       g_signal_connect (event_box, "drag-begin",
+               G_CALLBACK (event_box_drag_begin), data);
+       g_signal_connect (event_box, "drag-motion",
+               G_CALLBACK (event_box_drag_motion_cb), data);
+       g_signal_connect (event_box, "drag-drop",
+               G_CALLBACK (event_box_drag_drop), data);
+       g_signal_connect (event_box, "drag-end",
+               G_CALLBACK (event_box_drag_end), data);
+
+       gtk_drag_dest_set (rule, GTK_DEST_DEFAULT_MOTION, dnd_types, N_DND_TYPES, GDK_ACTION_MOVE);
+
+       g_signal_connect (rule, "drag-motion",
+               G_CALLBACK (rule_widget_drag_motion_cb), data);
 }
 
 static void
@@ -476,15 +692,13 @@ more_parts (GtkWidget *button,
        new = em_filter_context_next_action ((EMFilterContext *) data->f, NULL);
        if (new) {
                GtkWidget *w;
-               guint rows;
 
                new = e_filter_part_clone (new);
                em_filter_rule_add_action ((EMFilterRule *) data->fr, new);
                w = get_rule_part_widget (data->f, new, data->fr);
 
-               g_object_get (data->parts, "n-rows", &rows, NULL);
-               gtk_table_resize (GTK_TABLE (data->parts), rows + 1, 2);
-               attach_rule (w, data, new, rows);
+               attach_rule (w, data, new, data->n_rows);
+               data->n_rows++;
 
                if (GTK_IS_CONTAINER (w)) {
                        gboolean done = FALSE;
@@ -591,15 +805,14 @@ get_widget (EFilterRule *fr,
             ERuleContext *rc)
 {
        GtkWidget *widget, *add, *label;
-       GtkWidget *parts, *inframe, *w;
+       GtkWidget *inframe, *w;
        GtkWidget *scrolledwindow;
-       GtkGrid *hgrid;
+       GtkGrid *hgrid, *parts_grid;
        GtkAdjustment *hadj, *vadj;
-       GList *l;
+       GList *link;
        EFilterPart *part;
        struct _rule_data *data;
-       EMFilterRule *ff =(EMFilterRule *) fr;
-       gint rows, i = 0;
+       EMFilterRule *ff = (EMFilterRule *) fr;
        gchar *msg;
 
        widget = E_FILTER_RULE_CLASS (em_filter_rule_parent_class)->
@@ -655,23 +868,31 @@ get_widget (EFilterRule *fr,
 
        gtk_grid_attach_next_to (hgrid, inframe, label, GTK_POS_RIGHT, 1, 1);
 
-       rows = g_list_length (ff->actions);
-       parts = gtk_table_new (rows, 2, FALSE);
-       data = g_malloc0 (sizeof (*data));
-       data->f =(EMFilterContext *) rc;
+       parts_grid = GTK_GRID (gtk_grid_new ());
+       g_object_set (G_OBJECT (parts_grid),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               NULL);
+
+       data = g_malloc0 (sizeof (struct _rule_data));
+       data->f = (EMFilterContext *) rc;
        data->fr = fr;
-       data->parts = parts;
+       data->parts_grid = parts_grid;
+       data->drag_widget = NULL;
+       data->n_rows = 0;
 
        /* only set to automatically clean up the memory */
        g_object_set_data_full ((GObject *) hgrid, "data", data, g_free);
 
-       l = ff->actions;
-       while (l) {
-               part = l->data;
+       for (link = ff->actions; link; link = g_list_next (link)) {
+               part = link->data;
                d (printf ("adding action %s\n", part->title));
                w = get_rule_part_widget ((EMFilterContext *) rc, part, fr);
-               attach_rule (w, data, part, i++);
-               l = l->next;
+
+               attach_rule (w, data, part, data->n_rows);
+               data->n_rows++;
        }
 
        hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 1.0, 1.0 ,1.0, 1.0));
@@ -682,7 +903,7 @@ get_widget (EFilterRule *fr,
                GTK_SCROLLED_WINDOW (scrolledwindow),
                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
        gtk_scrolled_window_add_with_viewport (
-               GTK_SCROLLED_WINDOW (scrolledwindow), parts);
+               GTK_SCROLLED_WINDOW (scrolledwindow), GTK_WIDGET (parts_grid));
 
        gtk_widget_set_hexpand (scrolledwindow, TRUE);
        gtk_widget_set_halign (scrolledwindow, GTK_ALIGN_FILL);


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