Root/gui_canvas.c

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

Archive Download this file

Branches:
master



interactive