From 093a34f8605f6b01fcc1b453ec74d4b40f9017eb Mon Sep 17 00:00:00 2001 From: werner Date: Fri, 23 Apr 2010 13:43:19 +0000 Subject: [PATCH] Added support for reordering rows and columns in a table via the GUI. To move a column, drag the variable or any of its values and drag horizontally. To move a row, drag one of its value vertically. - gui_frame.c (table_var_select_event, table_value_select_event): return FALSE if dragging is possible - gui_frame.c (build_table), gui_frame_drag.h, gui_frame_drag.c, Makefile: support for rearranging tables using Gtk's drag and drop mechanism - TODO: two items less git-svn-id: http://svn.openmoko.org/trunk/eda/fped@5929 99fdad57-331a-0410-800a-d7fa5415bdb3 --- Makefile | 2 +- TODO | 2 - gui_frame.c | 9 +- gui_frame_drag.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++ gui_frame_drag.h | 25 +++++ 5 files changed, 313 insertions(+), 6 deletions(-) create mode 100644 gui_frame_drag.c create mode 100644 gui_frame_drag.h diff --git a/Makefile b/Makefile index a534581..bb14f3f 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ OBJS = fped.o expr.o coord.o obj.o delete.o inst.o util.o error.o \ layer.o overlap.o \ cpp.o lex.yy.o y.tab.o \ gui.o gui_util.o gui_style.o gui_inst.o gui_status.o gui_canvas.o \ - gui_tool.o gui_over.o gui_meas.o gui_frame.o + gui_tool.o gui_over.o gui_meas.o gui_frame.o gui_frame_drag.o XPMS = point.xpm delete.xpm delete_off.xpm \ vec.xpm frame.xpm frame_locked.xpm frame_ready.xpm \ diff --git a/TODO b/TODO index 1423201..9542551 100644 --- a/TODO +++ b/TODO @@ -6,8 +6,6 @@ Major missing features: Minor missing features: - reorder frames (can use text editor) -- reorder rows in a table (can use text editor) -- reorder columns in a table - reorder variables in a frame (can use text editor) - move items/vectors up and down the hierarchy diff --git a/gui_frame.c b/gui_frame.c index e02a0ce..6467346 100644 --- a/gui_frame.c +++ b/gui_frame.c @@ -27,6 +27,7 @@ #include "gui_tool.h" #include "gui_canvas.h" #include "gui.h" +#include "gui_frame_drag.h" #include "gui_frame.h" @@ -893,7 +894,7 @@ static gboolean table_var_select_event(GtkWidget *widget, switch (event->button) { case 1: edit_var(var, set_col_values, var, -1); - break; + return FALSE; case 3: pop_up_table_var(var, event); break; @@ -918,7 +919,7 @@ static gboolean table_value_select_event(GtkWidget *widget, select_row(value->row); change_world(); } - break; + return FALSE; case 3: pop_up_table_value(value, event); break; @@ -999,7 +1000,6 @@ static void build_table(GtkWidget *vbox, struct frame *frame, gtk_table_set_row_spacings(GTK_TABLE(tab), 1); gtk_table_set_col_spacings(GTK_TABLE(tab), 1); - } field = label_in_box_new(var->name, @@ -1015,6 +1015,8 @@ static void build_table(GtkWidget *vbox, struct frame *frame, G_CALLBACK(table_scroll_event), table); var->widget = field; + setup_var_drag(var); + n_row = 0; for (row = table->rows; row; row = row->next) { value = row->values; @@ -1037,6 +1039,7 @@ static void build_table(GtkWidget *vbox, struct frame *frame, "scroll_event", G_CALLBACK(table_scroll_event), table); value->widget = field; + setup_value_drag(value); n_row++; } diff --git a/gui_frame_drag.c b/gui_frame_drag.c new file mode 100644 index 0000000..b8db128 --- /dev/null +++ b/gui_frame_drag.c @@ -0,0 +1,281 @@ +/* + * gui_frame_drag.c - GUI, dragging of frame items + * + * Written 2010 by Werner Almesberger + * Copyright 2010 by Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include + +#include "obj.h" +#include "gui_util.h" +#include "gui_frame_drag.h" + + +/* + * Pointer to whatever it is we're dragging. Undefined if not dragging. + */ +static void *dragging; + + +/* ----- helper functions for indexed list and swapping -------------------- */ + + +#define NDX(first, item) \ + ({ typeof(first) NDX_walk; \ + int NDX_n = 0; \ + for (NDX_walk = (first); NDX_walk != (item); \ + NDX_walk = NDX_walk->next) \ + NDX_n++; \ + NDX_n; }) + +#define NTH(first, n) \ + ({ typeof(first) *NTH_walk; \ + int NTH_n = (n); \ + for (NTH_walk = &(first); NTH_n; NTH_n--) \ + NTH_walk = &(*NTH_walk)->next; \ + NTH_walk; }) + +#define SWAP(a, b) \ + ({ typeof(a) SWAP_tmp; \ + SWAP_tmp = a; \ + a = b; \ + b = SWAP_tmp; }) + + +/* ----- generic helper functions. maybe move to gui_util later ------------ */ + + +static void get_cell_coords(GtkWidget *widget, guint res[4]) +{ + GtkWidget *tab; + + tab = gtk_widget_get_ancestor(widget, GTK_TYPE_TABLE); + gtk_container_child_get(GTK_CONTAINER(tab), widget, + "left-attach", res, + "right-attach", res+1, + "top-attach", res+2, + "bottom-attach", res+3, NULL); +} + + +static void swap_table_cells(GtkWidget *a, GtkWidget *b) +{ + GtkWidget *tab_a, *tab_b; + guint pos_a[4], pos_b[4]; + + tab_a = gtk_widget_get_ancestor(a, GTK_TYPE_TABLE); + tab_b = gtk_widget_get_ancestor(b, GTK_TYPE_TABLE); + get_cell_coords(a, pos_a); + get_cell_coords(b, pos_b); + g_object_ref(a); + g_object_ref(b); + gtk_container_remove(GTK_CONTAINER(tab_a), a); + gtk_container_remove(GTK_CONTAINER(tab_b), b); + gtk_table_attach_defaults(GTK_TABLE(tab_a), b, + pos_a[0], pos_a[1], pos_a[2], pos_a[3]); + gtk_table_attach_defaults(GTK_TABLE(tab_b), a, + pos_b[0], pos_b[1], pos_b[2], pos_b[3]); + g_object_unref(a); + g_object_unref(b); +} + + +/* ----- swap table items -------------------------------------------------- */ + + +static void swap_vars(struct table *table, int a, int b) +{ + struct var **var_a, **var_b; + + var_a = NTH(table->vars, a); + var_b = NTH(table->vars, b); + + swap_table_cells(box_of_label((*var_a)->widget), + box_of_label((*var_b)->widget)); + + SWAP(*var_a, *var_b); + SWAP((*var_a)->next, (*var_b)->next); +} + + +static void swap_values(struct row *row, int a, int b) +{ + struct value **value_a, **value_b; + + value_a = NTH(row->values, a); + value_b = NTH(row->values, b); + + swap_table_cells(box_of_label((*value_a)->widget), + box_of_label((*value_b)->widget)); + + SWAP(*value_a, *value_b); + SWAP((*value_a)->next, (*value_b)->next); +} + + + +static void swap_cols(struct table *table, int a, int b) +{ + struct row *row; + + swap_vars(table, a, b); + for (row = table->rows; row; row = row->next) + swap_values(row, a, b); +} + + +static void swap_rows(struct row **a, struct row **b) +{ + struct value *value_a, *value_b; + + value_a = (*a)->values; + value_b = (*b)->values; + while (value_a) { + swap_table_cells(box_of_label(value_a->widget), + box_of_label(value_b->widget)); + value_a = value_a->next; + value_b = value_b->next; + } + SWAP(*a, *b); + SWAP((*a)->next, (*b)->next); +} + + +/* ----- common callback --------------------------------------------------- */ + + +static void drag_begin(GtkWidget *widget, + GtkTextDirection previous_direction, gpointer user_data) +{ + GdkPixbuf *pixbuf; + + /* + * Suppress the icon. PixBufs can't be zero-sized, but nobody will + * notice a lone pixel either :-) + */ + pixbuf = + gdk_pixbuf_get_from_drawable(NULL, DA, NULL, 0, 0, 0, 0, 1, 1); + gtk_drag_source_set_icon_pixbuf(widget, pixbuf); + + dragging = user_data; +} + + +/* ----- drag variables ---------------------------------------------------- */ + + +static gboolean drag_var_motion(GtkWidget *widget, + GdkDragContext *drag_context, gint x, gint y, guint time_, + gpointer user_data) +{ + struct var *from = dragging; + struct var *to = user_data; + int from_n, to_n, i; + + if (from == to || from->table != to->table) + return FALSE; + from_n = NDX(from->table->vars, from); + to_n = NDX(to->table->vars, to); + for (i = from_n < to_n ? from_n : to_n; + i != (from_n < to_n ? to_n : from_n); i++) + swap_cols(from->table, i, i+1); + return FALSE; +} + + +void setup_var_drag(struct var *var) +{ + static GtkTargetEntry target = { + .target = "var", + .flags = GTK_TARGET_SAME_APP, + }; + GtkWidget *box; + + box = box_of_label(var->widget); + gtk_drag_source_set(box, GDK_BUTTON1_MASK, + &target, 1, GDK_ACTION_PRIVATE); + gtk_drag_dest_set(box, GTK_DEST_DEFAULT_HIGHLIGHT, + &target, 1, GDK_ACTION_PRIVATE); + g_signal_connect(G_OBJECT(box), "drag-begin", + G_CALLBACK(drag_begin), var); + g_signal_connect(G_OBJECT(box), "drag-motion", + G_CALLBACK(drag_var_motion), var); +} + + +/* ----- drag values ------------------------------------------------------- */ + + +static gboolean drag_value_motion(GtkWidget *widget, + GdkDragContext *drag_context, gint x, gint y, guint time_, + gpointer user_data) +{ + struct value *from = dragging; + struct value *to = user_data; + struct table *table = from->row->table; + struct row **row, *end; + int from_n, to_n, i; + + if (table != to->row->table) + return FALSE; + + /* columns */ + + from_n = NDX(from->row->values, from); + to_n = NDX(to->row->values, to); + for (i = from_n < to_n ? from_n : to_n; + i != (from_n < to_n ? to_n : from_n); i++) + swap_cols(table, i, i+1); + + /* rows */ + + if (from->row == to->row) + return FALSE; + row = &table->rows; + while (1) { + if (*row == from->row) { + end = to->row; + break; + } + if (*row == to->row) { + end = from->row; + break; + } + row = &(*row)->next; + } + while (1) { + swap_rows(row, &(*row)->next); + if (*row == end) + break; + row = &(*row)->next; + } + + return FALSE; +} + + +void setup_value_drag(struct value *value) +{ + static GtkTargetEntry target = { + .target = "value", + .flags = GTK_TARGET_SAME_APP, + }; + GtkWidget *box; + + box = box_of_label(value->widget); + gtk_drag_source_set(box, GDK_BUTTON1_MASK, + &target, 1, GDK_ACTION_PRIVATE); + gtk_drag_dest_set(box, GTK_DEST_DEFAULT_HIGHLIGHT, + &target, 1, GDK_ACTION_PRIVATE); + g_signal_connect(G_OBJECT(box), "drag-begin", + G_CALLBACK(drag_begin), value); + g_signal_connect(G_OBJECT(box), "drag-motion", + G_CALLBACK(drag_value_motion), value); +} diff --git a/gui_frame_drag.h b/gui_frame_drag.h new file mode 100644 index 0000000..1044259 --- /dev/null +++ b/gui_frame_drag.h @@ -0,0 +1,25 @@ +/* + * gui_frame_drag.h - GUI, dragging of frame items + * + * Written 2010 by Werner Almesberger + * Copyright 2010 by Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#ifndef GUI_FRAME_DRAG_H +#define GUI_FRAME_DRAG_H + +#include + +#include "obj.h" + + +void setup_var_drag(struct var *var); +void setup_value_drag(struct value *value); + +#endif /* !GUI_FRAME_DRAG_H */