Root/gui_canvas.c

Source at commit 8a1a3103964533b0d517a29312ddeabdab1d6271 created 4 years 11 months ago.
By Werner Almesberger, inst.c (find_point_vec): correct indentation
1/*
2 * gui_canvas.c - GUI, canvas
3 *
4 * Written 2009, 2010, 2012 by Werner Almesberger
5 * Copyright 2009, 2010, 2012 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 <math.h>
15#include <gtk/gtk.h>
16#include <gdk/gdkkeysyms.h>
17
18#include "obj.h"
19#include "delete.h"
20#include "inst.h"
21#include "gui_util.h"
22#include "gui_inst.h"
23#include "gui_style.h"
24#include "gui_status.h"
25#include "gui_tool.h"
26#include "gui.h"
27#include "gui_frame_drag.h"
28#include "gui_canvas.h"
29
30
31#if 0
32#define DPRINTF(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
33#else
34#define DPRINTF(fmt, ...)
35#endif
36
37
38void (*highlight)(void) = NULL;
39
40static struct coord curr_pos; /* canvas coordinates ! */
41static struct coord user_origin = { 0, 0 };
42
43static int dragging = 0;
44static int drag_escaped = 0; /* 1 once we've made it out of the drag radius */
45static struct coord drag_start;
46static struct inst *selected_before_drag;
47    /* instance selected before dragging. we use it to do the click-to-select
48       routine in case we later find out the drag was really just a click. */
49
50
51/* ----- status display ---------------------------------------------------- */
52
53
54static void update_zoom(void)
55{
56    status_set_zoom("Zoom factor", "x%d", draw_ctx.scale);
57}
58
59
60static void update_pos(struct coord pos)
61{
62    struct coord user;
63    unit_type diag;
64
65    set_with_units(status_set_sys_x, "X ", pos.x, "Absolute X position");
66    set_with_units(status_set_sys_y, "Y ", pos.y, "Absolute Y position");
67
68    user.x = pos.x-user_origin.x;
69    user.y = pos.y-user_origin.y;
70    set_with_units(status_set_user_x, "x ", user.x,
71        "User X position. Press SPACE to zero.");
72    set_with_units(status_set_user_y, "y ", user.y,
73        "User Y position. Press SPACE to zero.");
74
75    if (!selected_inst) {
76        diag = hypot(user.x, user.y);
77        set_with_units(status_set_r, "r = ", diag,
78            "Distance from user origin");
79        status_set_angle_xy("Angle from user origin", user);
80    }
81}
82
83
84void refresh_pos(void)
85{
86    update_pos(canvas_to_coord(curr_pos.x, curr_pos.y));
87}
88
89
90/* ----- coordinate system ------------------------------------------------- */
91
92
93static void center(const struct bbox *this_bbox)
94{
95    struct bbox bbox;
96
97    bbox = this_bbox ? *this_bbox : inst_get_bbox(NULL);
98    draw_ctx.center.x = (bbox.min.x+bbox.max.x)/2;
99    draw_ctx.center.y = (bbox.min.y+bbox.max.y)/2;
100}
101
102
103static void auto_scale(const struct bbox *this_bbox)
104{
105    struct bbox bbox;
106    unit_type h, w;
107    int sx, sy;
108    float aw, ah;
109
110    bbox = this_bbox ? *this_bbox : inst_get_bbox(NULL);
111    aw = draw_ctx.widget->allocation.width;
112    ah = draw_ctx.widget->allocation.height;
113    h = bbox.max.x-bbox.min.x;
114    w = bbox.max.y-bbox.min.y;
115    aw -= 2*CANVAS_CLEARANCE;
116    ah -= 2*CANVAS_CLEARANCE;
117    if (aw < 1)
118        aw = 1;
119    if (ah < 1)
120        ah = 1;
121    sx = ceil(h/aw);
122    sy = ceil(w/ah);
123    draw_ctx.scale = sx > sy ? sx : sy > 0 ? sy : 1;
124
125    update_zoom();
126}
127
128
129/* ----- drawing ----------------------------------------------------------- */
130
131
132void redraw(void)
133{
134    float aw, ah;
135
136    aw = draw_ctx.widget->allocation.width;
137    ah = draw_ctx.widget->allocation.height;
138    gdk_draw_rectangle(draw_ctx.widget->window,
139        instantiation_error ? gc_bg_error : gc_bg, TRUE, 0, 0, aw, ah);
140
141    DPRINTF("--- redraw: inst_draw ---");
142    inst_draw();
143    if (highlight)
144        highlight();
145    DPRINTF("--- redraw: tool_redraw ---");
146    tool_redraw();
147    DPRINTF("--- redraw: done ---");
148}
149
150
151/* ----- drag -------------------------------------------------------------- */
152
153
154static void drag_left(struct coord pos)
155{
156    if (!dragging)
157        return;
158    if (!drag_escaped &&
159        hypot(pos.x-drag_start.x, pos.y-drag_start.y)/draw_ctx.scale <
160        DRAG_MIN_R)
161        return;
162    drag_escaped = 1;
163    tool_drag(pos);
164}
165
166
167static void drag_middle(struct coord pos)
168{
169}
170
171
172static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event,
173    gpointer data)
174{
175    struct coord pos = canvas_to_coord(event->x, event->y);
176
177    DPRINTF("--- motion ---");
178    curr_pos.x = event->x;
179    curr_pos.y = event->y;
180    tool_hover(pos);
181    if (event->state & GDK_BUTTON1_MASK)
182        drag_left(pos);
183    if (event->state & GDK_BUTTON2_MASK)
184        drag_middle(pos);
185    update_pos(pos);
186    return FALSE;
187}
188
189
190/* ----- drag and drop (frame to canvas) ----------------------------------- */
191
192
193void canvas_frame_begin(struct frame *frame)
194{
195    inst_deselect(); /* don't drag away bits of the selected object */
196    redraw();
197    tool_push_frame(frame);
198}
199
200
201int canvas_frame_motion(struct frame *frame, int x, int y)
202{
203    struct coord pos = canvas_to_coord(x, y);
204
205    return tool_hover(pos);
206}
207
208
209void canvas_frame_end(void)
210{
211    tool_dehover();
212    tool_pop_frame();
213}
214
215
216int canvas_frame_drop(struct frame *frame, int x, int y)
217{
218    struct coord pos = canvas_to_coord(x, y);
219
220    if (!tool_place_frame(frame, pos))
221        return FALSE;
222    change_world();
223    return TRUE;
224}
225
226
227/* ----- button press and release ------------------------------------------ */
228
229
230static void click_to_select(struct coord pos)
231{
232    const struct inst *prev;
233
234    tool_reset();
235    prev = selected_inst;
236    inst_select(pos);
237    if (prev != selected_inst)
238        redraw();
239}
240
241
242static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event,
243    gpointer data)
244{
245    struct coord pos = canvas_to_coord(event->x, event->y);
246    int res;
247
248    DPRINTF("--- button press ---");
249    gtk_widget_grab_focus(widget);
250    switch (event->button) {
251    case 1:
252        if (dragging) {
253            fprintf(stderr, "HUH ?!?\n");
254            tool_cancel_drag();
255            dragging = 0;
256        }
257        res = tool_consider_drag(pos);
258        /* tool doesn't do drag */
259        if (res < 0) {
260            change_world();
261            inst_deselect();
262            break;
263        }
264        if (res) {
265            selected_before_drag = selected_inst;
266            inst_deselect();
267            redraw();
268            dragging = 1;
269            drag_escaped = 0;
270            drag_start = pos;
271            break;
272        }
273        click_to_select(pos);
274        break;
275    case 2:
276        tool_dehover();
277        draw_ctx.center = pos;
278        redraw();
279        tool_hover(canvas_to_coord(event->x, event->y));
280        break;
281    }
282    return TRUE;
283}
284
285
286static gboolean button_release_event(GtkWidget *widget, GdkEventButton *event,
287    gpointer data)
288{
289    struct coord pos = canvas_to_coord(event->x, event->y);
290
291    DPRINTF("--- button release ---");
292    switch (event->button) {
293    case 1:
294        if (is_dragging_anything())
295            return FALSE;
296        if (!dragging)
297            break;
298        drag_left(pos);
299        dragging = 0;
300        if (!drag_escaped) {
301            tool_cancel_drag();
302            selected_inst = selected_before_drag;
303            click_to_select(pos);
304            break;
305        }
306        if (tool_end_drag(pos))
307            change_world();
308        break;
309    }
310    return TRUE;
311}
312
313
314/* ----- zoom control ------------------------------------------------------ */
315
316
317static void zoom_in(struct coord pos)
318{
319    if (draw_ctx.scale < 2)
320        return;
321    tool_dehover();
322    draw_ctx.scale /= 2;
323    draw_ctx.center.x = (draw_ctx.center.x+pos.x)/2;
324    draw_ctx.center.y = (draw_ctx.center.y+pos.y)/2;
325    update_zoom();
326    redraw();
327    tool_hover(pos);
328}
329
330
331static void zoom_out(struct coord pos)
332{
333    struct bbox bbox;
334
335    bbox = inst_get_bbox(NULL);
336    bbox.min = translate(bbox.min);
337    bbox.max = translate(bbox.max);
338    if (bbox.min.x >= ZOOM_STOP_BORDER &&
339        bbox.max.y >= ZOOM_STOP_BORDER &&
340        bbox.max.x < draw_ctx.widget->allocation.width-ZOOM_STOP_BORDER &&
341        bbox.min.y < draw_ctx.widget->allocation.height-ZOOM_STOP_BORDER)
342        return;
343    tool_dehover();
344    draw_ctx.scale *= 2;
345    draw_ctx.center.x = 2*draw_ctx.center.x-pos.x;
346    draw_ctx.center.y = 2*draw_ctx.center.y-pos.y;
347    update_zoom();
348    redraw();
349    tool_hover(pos);
350}
351
352
353void zoom_in_center(void)
354{
355    zoom_in(draw_ctx.center);
356}
357
358
359void zoom_out_center(void)
360{
361    zoom_out(draw_ctx.center);
362}
363
364
365void zoom_to_frame(void)
366{
367    tool_dehover();
368    center(&active_frame_bbox);
369    auto_scale(&active_frame_bbox);
370    redraw();
371    tool_hover(canvas_to_coord(curr_pos.x, curr_pos.y));
372}
373
374
375void zoom_to_extents(void)
376{
377    tool_dehover();
378    center(NULL);
379    auto_scale(NULL);
380    redraw();
381    tool_hover(canvas_to_coord(curr_pos.x, curr_pos.y));
382}
383
384
385static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event,
386    gpointer data)
387{
388    struct coord pos = canvas_to_coord(event->x, event->y);
389
390    gtk_widget_grab_focus(widget);
391    switch (event->direction) {
392    case GDK_SCROLL_UP:
393        zoom_in(pos);
394        break;
395    case GDK_SCROLL_DOWN:
396        zoom_out(pos);
397        break;
398    default:
399        /* ignore */;
400    }
401    return TRUE;
402}
403
404
405/* ----- keys -------------------------------------------------------------- */
406
407
408static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event,
409    gpointer data)
410{
411    struct coord pos = canvas_to_coord(curr_pos.x, curr_pos.y);
412
413    DPRINTF("--- key press ---");
414    switch (event->keyval) {
415    case ' ':
416        user_origin = pos;
417        update_pos(pos);
418        break;
419    case '+':
420    case '=':
421        zoom_in(pos);
422        break;
423    case '-':
424        zoom_out(pos);
425        break;
426    case '*':
427        zoom_to_extents();
428        break;
429    case '#':
430        zoom_to_frame();
431        break;
432    case '.':
433        tool_dehover();
434        draw_ctx.center = pos;
435        redraw();
436        tool_hover(canvas_to_coord(curr_pos.x, curr_pos.y));
437        break;
438    case GDK_BackSpace:
439    case GDK_Delete:
440#if 0
441    case GDK_KP_Delete:
442        if (selected_inst) {
443            inst_delete(selected_inst);
444            change_world();
445        }
446        break;
447#endif
448    case 'u':
449        if (undelete())
450            change_world();
451        break;
452    case '/':
453{
454/* @@@ find a better place for this */
455extern int show_vars;
456        show_vars = !show_vars;
457change_world();
458}
459    }
460    return TRUE;
461}
462
463
464/* ----- expose event ------------------------------------------------------ */
465
466
467static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event,
468    gpointer data)
469{
470    static int first = 1;
471
472    DPRINTF("--- expose ---");
473    if (first) {
474        init_canvas();
475        first = 0;
476    }
477    tool_dehover();
478    redraw();
479    return TRUE;
480}
481
482
483/* ----- enter/leave ------------------------------------------------------- */
484
485
486static gboolean enter_notify_event(GtkWidget *widget, GdkEventCrossing *event,
487    gpointer data)
488{
489    DPRINTF("--- enter ---");
490    gtk_widget_grab_focus(widget);
491    return FALSE;
492}
493
494
495static gboolean leave_notify_event(GtkWidget *widget, GdkEventCrossing *event,
496    gpointer data)
497{
498    DPRINTF("--- leave ---");
499    if (dragging)
500        tool_cancel_drag();
501    tool_dehover();
502    dragging = 0;
503    return FALSE;
504}
505
506
507/* ----- tooltip ----------------------------------------------------------- */
508
509
510static gboolean canvas_tooltip(GtkWidget *widget, gint x, gint y,
511    gboolean keyboard_mode, GtkTooltip *tooltip, gpointer user_data)
512{
513    struct coord pos = canvas_to_coord(x, y);
514    const char *res;
515
516    res = tool_tip(pos);
517    if (!res)
518        return FALSE;
519    gtk_tooltip_set_markup(tooltip, res);
520    return TRUE;
521}
522
523
524/* ----- canvas setup ------------------------------------------------------ */
525
526
527/*
528 * Note that we call init_canvas twice: first to make sure we'll make it safely
529 * through select_frame, and the second time to set the geometry for the actual
530 * screen.
531 */
532
533void init_canvas(void)
534{
535    center(NULL);
536    auto_scale(NULL);
537}
538
539
540GtkWidget *make_canvas(void)
541{
542    GtkWidget *canvas;
543    GdkColor black = { 0, 0, 0, 0 };
544
545    /* Canvas */
546
547    canvas = gtk_drawing_area_new();
548    gtk_widget_modify_bg(canvas, GTK_STATE_NORMAL, &black);
549
550    g_signal_connect(G_OBJECT(canvas), "motion_notify_event",
551        G_CALLBACK(motion_notify_event), NULL);
552    g_signal_connect(G_OBJECT(canvas), "button_press_event",
553        G_CALLBACK(button_press_event), NULL);
554    g_signal_connect(G_OBJECT(canvas), "button_release_event",
555        G_CALLBACK(button_release_event), NULL);
556    g_signal_connect(G_OBJECT(canvas), "scroll_event",
557        G_CALLBACK(scroll_event), NULL);
558
559    GTK_WIDGET_SET_FLAGS(canvas, GTK_CAN_FOCUS);
560
561    g_signal_connect(G_OBJECT(canvas), "key_press_event",
562        G_CALLBACK(key_press_event), NULL);
563
564    g_signal_connect(G_OBJECT(canvas), "expose_event",
565        G_CALLBACK(expose_event), NULL);
566    g_signal_connect(G_OBJECT(canvas), "enter_notify_event",
567        G_CALLBACK(enter_notify_event), NULL);
568    g_signal_connect(G_OBJECT(canvas), "leave_notify_event",
569        G_CALLBACK(leave_notify_event), NULL);
570
571    gtk_widget_set(canvas, "has-tooltip", TRUE, NULL);
572    g_signal_connect(G_OBJECT(canvas), "query_tooltip",
573        G_CALLBACK(canvas_tooltip), NULL);
574
575    gtk_widget_set_events(canvas,
576        GDK_EXPOSE | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
577        GDK_KEY_PRESS_MASK |
578        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
579        GDK_SCROLL |
580        GDK_POINTER_MOTION_MASK);
581
582    gtk_widget_set_double_buffered(canvas, FALSE);
583
584    setup_canvas_drag(canvas);
585
586    draw_ctx.widget = canvas;
587
588    return canvas;
589}
590

Archive Download this file

Branches:
master



interactive