Root/gui_frame_drag.c

Source at commit 377b7f81193685f23c93546767a059e194419446 created 3 years 4 months ago.
By Werner Almesberger, fped.c: new option -m (for -p and -P) to suppress showing measurements
1/*
2 * gui_frame_drag.c - GUI, dragging of frame items
3 *
4 * Written 2010 by Werner Almesberger
5 * Copyright 2010 by Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <assert.h>
15#include <gtk/gtk.h>
16
17#include "util.h"
18#include "obj.h"
19#include "gui_util.h"
20#include "gui.h"
21#include "gui_canvas.h"
22#include "gui_frame_drag.h"
23
24#if 0
25#include "icons/frame.xpm"
26#endif
27
28
29enum {
30    target_id_var,
31    target_id_value,
32    target_id_frame,
33    target_id_canvas,
34};
35
36
37static GtkTargetEntry target_var = {
38    .target = "var",
39    .flags = GTK_TARGET_SAME_APP,
40    .info = target_id_var,
41};
42
43static GtkTargetEntry target_value = {
44    .target = "value",
45    .flags = GTK_TARGET_SAME_APP,
46    .info = target_id_value,
47};
48
49static GtkTargetEntry target_frame = {
50    .target = "frame",
51    .flags = GTK_TARGET_SAME_APP,
52    .info = target_id_frame,
53};
54
55
56/* ----- dragging status --------------------------------------------------- */
57
58
59/*
60 * Pointer to whatever it is we're dragging. NULL if not dragging.
61 */
62
63static void *dragging;
64
65
66int is_dragging(void *this)
67{
68    return this == dragging;
69}
70
71
72int is_dragging_anything(void)
73{
74    return !!dragging;
75}
76
77
78/* ----- helper functions for indexed list --------------------------------- */
79
80
81#define NDX(first, item) \
82    ({ typeof(first) NDX_walk; \
83       int NDX_n = 0; \
84       for (NDX_walk = (first); NDX_walk != (item); \
85           NDX_walk = NDX_walk->next) \
86        NDX_n++; \
87       NDX_n; })
88
89#define NTH(first, n) \
90    ({ typeof(first) *NTH_walk; \
91       int NTH_n = (n); \
92       for (NTH_walk = &(first); NTH_n; NTH_n--) \
93        NTH_walk = &(*NTH_walk)->next; \
94       NTH_walk; })
95
96#define FOR_UNORDERED(var, a, b) \
97    for (var = (a) < (b) ? (a) : (b); var != ((a) < (b) ? (b) : (a)); \
98        var++)
99
100
101/* ----- generic helper functions. maybe move to gui_util later ------------ */
102
103
104static void get_cell_coords(GtkWidget *widget, guint res[4])
105{
106    GtkWidget *tab;
107
108    tab = gtk_widget_get_ancestor(widget, GTK_TYPE_TABLE);
109    gtk_container_child_get(GTK_CONTAINER(tab), widget,
110        "left-attach", res,
111        "right-attach", res+1,
112        "top-attach", res+2,
113        "bottom-attach", res+3, NULL);
114}
115
116
117static void swap_table_cells(GtkWidget *a, GtkWidget *b)
118{
119    GtkWidget *tab_a, *tab_b;
120    guint pos_a[4], pos_b[4];
121
122    tab_a = gtk_widget_get_ancestor(a, GTK_TYPE_TABLE);
123    tab_b = gtk_widget_get_ancestor(b, GTK_TYPE_TABLE);
124    get_cell_coords(a, pos_a);
125    get_cell_coords(b, pos_b);
126    g_object_ref(a);
127    g_object_ref(b);
128    gtk_container_remove(GTK_CONTAINER(tab_a), a);
129    gtk_container_remove(GTK_CONTAINER(tab_b), b);
130    gtk_table_attach_defaults(GTK_TABLE(tab_a), b,
131        pos_a[0], pos_a[1], pos_a[2], pos_a[3]);
132    gtk_table_attach_defaults(GTK_TABLE(tab_b), a,
133        pos_b[0], pos_b[1], pos_b[2], pos_b[3]);
134    g_object_unref(a);
135    g_object_unref(b);
136}
137
138
139static GtkWidget *pick_table_cell(GtkWidget *table, int x, int y)
140{
141    GList *children, *walk;
142    GtkWidget *child;
143    guint pos[4];
144
145    children = gtk_container_get_children(GTK_CONTAINER(table));
146    for (walk = children; walk; walk = g_list_next(walk)) {
147        child = g_list_nth_data(walk, 0);
148        assert(child);
149        get_cell_coords(child, pos);
150        if (pos[0] == x && pos[2] == y)
151            break;
152    }
153    g_list_free(children);
154    return walk ? child : NULL;
155}
156
157
158static void swap_table_cells_by_coord(GtkWidget *table_a,
159    int a_col, int a_row, GtkWidget *table_b, int b_col, int b_row)
160{
161    GtkWidget *a, *b;
162
163    a = pick_table_cell(table_a, a_col, a_row);
164    b = pick_table_cell(table_b, b_col, b_row);
165    if (a) {
166        g_object_ref(a);
167        gtk_container_remove(GTK_CONTAINER(table_a), a);
168    }
169    if (b) {
170        g_object_ref(b);
171        gtk_container_remove(GTK_CONTAINER(table_b), b);
172    }
173    if (a)
174            gtk_table_attach_defaults(GTK_TABLE(table_b), a,
175            b_col, b_col+1, b_row, b_row+1);
176    if (b)
177            gtk_table_attach_defaults(GTK_TABLE(table_a), b,
178            a_col, a_col+1, a_row, a_row+1);
179    if (a)
180            g_object_unref(a);
181    if (b)
182            g_object_unref(b);
183}
184
185
186static void swap_table_rows(GtkWidget *table, int a, int b)
187{
188    guint cols;
189    int i;
190
191    g_object_get(table, "n-columns", &cols, NULL);
192    for (i = 0; i != cols; i++)
193        swap_table_cells_by_coord(table, i, a, table, i, b);
194}
195
196
197/* ----- swap table items -------------------------------------------------- */
198
199
200static void swap_vars(struct table *table, int a, int b)
201{
202    struct var **var_a, **var_b;
203
204    var_a = NTH(table->vars, a);
205    var_b = NTH(table->vars, b);
206
207    swap_table_cells(box_of_label((*var_a)->widget),
208        box_of_label((*var_b)->widget));
209
210    SWAP(*var_a, *var_b);
211    SWAP((*var_a)->next, (*var_b)->next);
212}
213
214
215static void swap_values(struct row *row, int a, int b)
216{
217    struct value **value_a, **value_b;
218
219    value_a = NTH(row->values, a);
220    value_b = NTH(row->values, b);
221
222    swap_table_cells(box_of_label((*value_a)->widget),
223        box_of_label((*value_b)->widget));
224
225    SWAP(*value_a, *value_b);
226    SWAP((*value_a)->next, (*value_b)->next);
227}
228
229
230static void swap_cols(struct table *table, int a, int b)
231{
232    struct row *row;
233
234    swap_vars(table, a, b);
235    for (row = table->rows; row; row = row->next)
236        swap_values(row, a, b);
237}
238
239
240static void swap_rows(struct row **a, struct row **b)
241{
242    struct value *value_a, *value_b;
243
244    value_a = (*a)->values;
245    value_b = (*b)->values;
246    while (value_a) {
247        swap_table_cells(box_of_label(value_a->widget),
248            box_of_label(value_b->widget));
249        value_a = value_a->next;
250        value_b = value_b->next;
251    }
252    SWAP(*a, *b);
253    SWAP((*a)->next, (*b)->next);
254}
255
256
257/* ----- swap frames ------------------------------------------------------- */
258
259
260static void swap_frames(GtkWidget *table, int a, int b)
261{
262    struct frame **frame_a = NTH(frames, a);
263    struct frame **frame_b = NTH(frames, b);
264
265    swap_table_rows(table, 2*a+1, 2*b+1);
266    swap_table_rows(table, 2*a+2, 2*b+2);
267
268    SWAP(*frame_a, *frame_b);
269    SWAP((*frame_a)->next, (*frame_b)->next);
270}
271
272
273/* ----- common functions -------------------------------------------------- */
274
275
276/*
277 * according to
278 * http://www.pubbs.net/201004/gtk/22819-re-drag-and-drop-drag-motion-cursor-lockup-fixed-.html
279 * http://www.cryingwolf.org/articles/gtk-dnd.html
280 */
281
282static int has_target(GtkWidget *widget, GdkDragContext *drag_context,
283    const char *name)
284{
285    GdkAtom target;
286
287    target = gtk_drag_dest_find_target(widget, drag_context, NULL);
288
289    /*
290     * Force allocation so that we don't have to check for GDK_NONE.
291     */
292    return target == gdk_atom_intern(name, FALSE);
293}
294
295
296static void drag_begin(GtkWidget *widget,
297    GtkTextDirection previous_direction, gpointer user_data)
298{
299    GdkPixbuf *pixbuf;
300
301    /*
302     * Suppress the icon. PixBufs can't be zero-sized, but nobody will
303     * notice a lone pixel either :-)
304     */
305    pixbuf =
306        gdk_pixbuf_get_from_drawable(NULL, DA, NULL, 0, 0, 0, 0, 1, 1);
307    gtk_drag_source_set_icon_pixbuf(widget, pixbuf);
308    g_object_unref(pixbuf);
309
310    dragging = user_data;
311}
312
313
314static void drag_end(GtkWidget *widget, GdkDragContext *drag_context,
315    gpointer user_data)
316{
317    dragging = NULL;
318}
319
320
321static void setup_drag_common(GtkWidget *widget, void *user_arg)
322{
323    g_signal_connect(G_OBJECT(widget), "drag-begin",
324        G_CALLBACK(drag_begin), user_arg);
325    g_signal_connect(G_OBJECT(widget), "drag-end",
326        G_CALLBACK(drag_end), user_arg);
327}
328
329
330/* ----- drag variables ---------------------------------------------------- */
331
332
333static gboolean drag_var_motion(GtkWidget *widget,
334    GdkDragContext *drag_context, gint x, gint y, guint time_,
335    gpointer user_data)
336{
337    struct var *from = dragging;
338    struct var *to = user_data;
339    int from_n, to_n, i;
340
341    if (!has_target(widget, drag_context, "var"))
342        return FALSE;
343    if (from == to || from->table != to->table)
344        return FALSE;
345    from_n = NDX(from->table->vars, from);
346    to_n = NDX(to->table->vars, to);
347    FOR_UNORDERED(i, from_n, to_n)
348        swap_cols(from->table, i, i+1);
349    return FALSE;
350}
351
352
353void setup_var_drag(struct var *var)
354{
355    GtkWidget *box;
356
357    box = box_of_label(var->widget);
358    gtk_drag_source_set(box, GDK_BUTTON1_MASK,
359        &target_var, 1, GDK_ACTION_PRIVATE);
360    gtk_drag_dest_set(box, GTK_DEST_DEFAULT_MOTION,
361        &target_var, 1, GDK_ACTION_PRIVATE);
362    setup_drag_common(box, var);
363    g_signal_connect(G_OBJECT(box), "drag-motion",
364        G_CALLBACK(drag_var_motion), var);
365}
366
367
368/* ----- drag values ------------------------------------------------------- */
369
370
371static gboolean drag_value_motion(GtkWidget *widget,
372    GdkDragContext *drag_context, gint x, gint y, guint time_,
373    gpointer user_data)
374{
375    struct value *from = dragging;
376    struct value *to = user_data;
377    struct table *table;
378    struct row **row, *end;
379    int from_n, to_n, i;
380
381    if (!has_target(widget, drag_context, "value"))
382        return FALSE;
383    table = from->row->table;
384    if (table != to->row->table)
385        return FALSE;
386
387    /* columns */
388
389    from_n = NDX(from->row->values, from);
390    to_n = NDX(to->row->values, to);
391    FOR_UNORDERED(i, from_n, to_n)
392        swap_cols(table, i, i+1);
393
394    /* rows */
395
396    if (from->row == to->row)
397        return FALSE;
398    row = &table->rows;
399    while (1) {
400        if (*row == from->row) {
401            end = to->row;
402            break;
403        }
404        if (*row == to->row) {
405            end = from->row;
406            break;
407        }
408        row = &(*row)->next;
409    }
410    while (1) {
411        swap_rows(row, &(*row)->next);
412        if (*row == end)
413            break;
414        row = &(*row)->next;
415    }
416
417    return FALSE;
418}
419
420
421void setup_value_drag(struct value *value)
422{
423    GtkWidget *box;
424
425    box = box_of_label(value->widget);
426    gtk_drag_source_set(box, GDK_BUTTON1_MASK,
427        &target_value, 1, GDK_ACTION_PRIVATE);
428    gtk_drag_dest_set(box, GTK_DEST_DEFAULT_MOTION,
429        &target_value, 1, GDK_ACTION_PRIVATE);
430    setup_drag_common(box, value);
431    g_signal_connect(G_OBJECT(box), "drag-motion",
432        G_CALLBACK(drag_value_motion), value);
433}
434
435
436/* ----- frame to canvas helper functions ---------------------------------- */
437
438
439static int frame_on_canvas = 0;
440
441
442static void leave_canvas(void)
443{
444    if (frame_on_canvas)
445        canvas_frame_end();
446    frame_on_canvas = 0;
447}
448
449
450/* ----- drag frame labels ------------------------------------------------- */
451
452
453#if 0
454
455/*
456 * Setting our own icon looks nice but it slows things down to the point where
457 * cursor movements can lag noticeable and it adds yet another element to an
458 * already crowded cursor.
459 */
460
461static void drag_frame_begin(GtkWidget *widget,
462    GtkTextDirection previous_direction, gpointer user_data)
463{
464    GdkPixmap *pixmap;
465    GdkBitmap *mask;
466    GdkColormap *cmap;
467
468    pixmap = gdk_pixmap_create_from_xpm_d(DA, &mask, NULL, xpm_frame);
469    cmap = gdk_drawable_get_colormap(root->window);
470    gtk_drag_source_set_icon(widget, cmap, pixmap, mask);
471    g_object_unref(pixmap);
472    g_object_unref(mask);
473
474    dragging = user_data;
475}
476
477#endif
478
479
480static gboolean drag_frame_motion(GtkWidget *widget,
481    GdkDragContext *drag_context, gint x, gint y, guint time_,
482    gpointer user_data)
483{
484    struct frame *from = dragging;
485    struct frame *to = user_data;
486    int from_n, to_n, i;
487
488    if (!has_target(widget, drag_context, "frame"))
489        return FALSE;
490    assert(from != frames);
491    assert(to != frames);
492    from_n = NDX(frames, from);
493    to_n = NDX(frames, to);
494    FOR_UNORDERED(i, from_n, to_n)
495        swap_frames(gtk_widget_get_ancestor(widget, GTK_TYPE_TABLE),
496            i, i+1);
497    return FALSE;
498}
499
500
501static void drag_frame_end(GtkWidget *widget, GdkDragContext *drag_context,
502    gpointer user_data)
503{
504    leave_canvas();
505    drag_end(widget, drag_context, user_data);
506}
507
508
509void setup_frame_drag(struct frame *frame)
510{
511    GtkWidget *box;
512
513    box = box_of_label(frame->label);
514    gtk_drag_source_set(box, GDK_BUTTON1_MASK,
515        &target_frame, 1, GDK_ACTION_COPY | GDK_ACTION_MOVE);
516    gtk_drag_dest_set(box, GTK_DEST_DEFAULT_MOTION,
517        &target_frame, 1, GDK_ACTION_MOVE);
518    setup_drag_common(box, frame);
519
520    /* override */
521#if 0
522    g_signal_connect(G_OBJECT(box), "drag-begin",
523        G_CALLBACK(drag_frame_begin), frame);
524#endif
525    g_signal_connect(G_OBJECT(box), "drag-end",
526        G_CALLBACK(drag_frame_end), frame);
527
528    g_signal_connect(G_OBJECT(box), "drag-motion",
529        G_CALLBACK(drag_frame_motion), frame);
530}
531
532
533/* ----- drag to the canvas ------------------------------------------------ */
534
535
536static gboolean drag_canvas_motion(GtkWidget *widget,
537    GdkDragContext *drag_context, gint x, gint y, guint time_,
538    gpointer user_data)
539{
540    if (!has_target(widget, drag_context, "frame"))
541        return FALSE;
542    if (!frame_on_canvas) {
543        frame_on_canvas = 1;
544        canvas_frame_begin(dragging);
545    }
546    if (canvas_frame_motion(dragging, x, y)) {
547        gdk_drag_status(drag_context, GDK_ACTION_COPY, time_);
548        return TRUE;
549    } else {
550        gdk_drag_status(drag_context, 0, time_);
551        return FALSE;
552    }
553}
554
555
556static void drag_canvas_leave(GtkWidget *widget, GdkDragContext *drag_context,
557    guint time_, gpointer user_data)
558{
559    leave_canvas();
560}
561
562
563static gboolean drag_canvas_drop(GtkWidget *widget,
564    GdkDragContext *drag_context, gint x, gint y, guint time_,
565    gpointer user_data)
566{
567    if (!has_target(widget, drag_context, "frame"))
568        return FALSE;
569    if (!canvas_frame_drop(dragging, x, y))
570        return FALSE;
571    gtk_drag_finish(drag_context, TRUE, FALSE, time_);
572    drag_end(widget, drag_context, user_data);
573    return TRUE;
574}
575
576
577void setup_canvas_drag(GtkWidget *canvas)
578{
579    gtk_drag_dest_set(canvas,
580        GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
581        &target_frame, 1, GDK_ACTION_COPY);
582
583    g_signal_connect(G_OBJECT(canvas), "drag-motion",
584        G_CALLBACK(drag_canvas_motion), NULL);
585    g_signal_connect(G_OBJECT(canvas), "drag-leave",
586        G_CALLBACK(drag_canvas_leave), NULL);
587    g_signal_connect(G_OBJECT(canvas), "drag-drop",
588        G_CALLBACK(drag_canvas_drop), NULL);
589}
590

Archive Download this file

Branches:
master



interactive