Set set length on assignment.
[grace.git] / src / events.c
blob3119c1fcc5922ee21798dfb6bd997e9fcaa5bbee
1 /*
2 * Grace - GRaphing, Advanced Computation and Exploration of data
3 *
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
5 *
6 * Copyright (c) 1996-2004 Grace Development Team
7 *
8 * Maintained by Evgeny Stambulchik
9 *
11 * All Rights Reserved
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 * event handler
34 #include <config.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #include "utils.h"
40 #include "core_utils.h"
41 #include "events.h"
43 #include "globals.h"
45 static void scroll_bar_pix(Widget bar, int pix)
47 int value, slider_size, maxvalue, increment;
49 GetScrollBarValues(bar, &value, &maxvalue, &slider_size, &increment);
50 value += pix;
51 if (value < 0) {
52 value = 0;
53 } else
54 if (value > maxvalue - slider_size) {
55 value = maxvalue - slider_size;
57 SetScrollBarValue(bar, value);
60 static void scroll_pix(Widget w, int dx, int dy)
62 Widget bar;
64 if (dx && (bar = GetHorizontalScrollBar(w))) {
65 scroll_bar_pix(bar, dx);
67 if (dy && (bar = GetVerticalScrollBar(w))) {
68 scroll_bar_pix(bar, dy);
71 static void scroll(Widget w, int up, int horiz)
73 int value, slider_size, increment, maxvalue;
74 Widget vbar;
76 if (horiz) {
77 vbar = GetHorizontalScrollBar(w);
78 } else {
79 vbar = GetVerticalScrollBar(w);
82 if (!vbar) {
83 return;
86 GetScrollBarValues(vbar, &value, &maxvalue, &slider_size, &increment);
87 if (up) {
88 value -= increment;
89 if (value < 0) {
90 value = 0;
92 } else {
93 value += increment;
94 if (value > maxvalue - slider_size) {
95 value = maxvalue - slider_size;
98 SetScrollBarValue(vbar, value);
101 typedef struct {
102 VPoint vp;
103 int include_graphs;
104 Quark *q;
105 int part;
106 view bbox;
107 int found;
108 } canvas_target;
110 static void target_consider(canvas_target *ct, Quark *q, int part,
111 const view *v)
113 if (is_vpoint_inside(v, &ct->vp, 0.0)) {
114 ct->q = q;
115 ct->part = part;
116 ct->bbox = *v;
117 ct->found = TRUE;
121 static int target_hook(Quark *q, void *udata, QTraverseClosure *closure)
123 canvas_target *ct = (canvas_target *) udata;
124 view v;
125 AText *at;
126 DObject *o;
127 double *x, *y;
129 if (!quark_is_active(q)) {
130 closure->descend = FALSE;
131 return TRUE;
134 switch (quark_fid_get(q)) {
135 case QFlavorFrame:
137 legend *l;
139 frame_get_view(q, &v);
140 target_consider(ct, q, 0, &v);
142 if ((l = frame_get_legend(q)) && l->active) {
143 target_consider(ct, q, 1, &l->bb);
146 break;
147 case QFlavorGraph:
148 if (ct->include_graphs) {
149 GraceApp *gapp = gapp_from_quark(q);
150 Quark *cg = graph_get_current(gproject_get_top(gapp->gp));
151 if (cg == q && graph_get_viewport(q, &v) == RETURN_SUCCESS) {
152 VPoint vp;
153 GLocator *locator;
155 target_consider(ct, q, 0, &v);
157 locator = graph_get_locator(cg);
158 Wpoint2Vpoint(cg, &locator->origin, &vp);
159 v.xv1 = v.xv2 = vp.x;
160 v.yv1 = v.yv2 = vp.y;
161 view_extend(&v, 0.01);
162 target_consider(ct, q, 1, &v);
165 break;
166 case QFlavorAText:
167 at = atext_get_data(q);
169 VPoint vp;
170 target_consider(ct, q, 0, &at->bb);
172 if (at->arrow_flag &&
173 Apoint2Vpoint(q, &at->ap, &vp) == RETURN_SUCCESS) {
174 double arrsize = MAX2(0.01*at->arrow.length, 0.005);
175 v.xv1 = v.xv2 = vp.x;
176 v.yv1 = v.yv2 = vp.y;
177 view_extend(&v, arrsize);
178 target_consider(ct, q, 1, &v);
181 break;
182 case QFlavorAxis:
183 if (axis_get_bb(q, &v) == RETURN_SUCCESS) {
184 target_consider(ct, q, 0, &v);
186 break;
187 case QFlavorDObject:
188 o = object_get_data(q);
189 target_consider(ct, q, 0, &o->bb);
190 break;
191 case QFlavorSet:
192 x = set_get_col(q, DATA_X);
193 y = set_get_col(q, DATA_Y);
194 if (x && y) {
195 int i;
196 WPoint wp;
197 VPoint vp;
198 set *p = set_get_data(q);
199 double symsize = MAX2(0.01*p->sym.size, 0.005);
200 for (i = 0; i < set_get_length(q); i++) {
201 wp.x = x[i];
202 wp.y = y[i];
203 Wpoint2Vpoint(q, &wp, &vp);
204 v.xv1 = v.xv2 = vp.x;
205 v.yv1 = v.yv2 = vp.y;
206 view_extend(&v, symsize);
207 target_consider(ct, q, i, &v);
210 break;
213 return TRUE;
216 static int find_target(GProject *gp, canvas_target *ct)
218 ct->found = FALSE;
219 quark_traverse(gproject_get_top(gp), target_hook, ct);
221 return ct->found ? RETURN_SUCCESS:RETURN_FAILURE;
224 static void move_target(canvas_target *ct, const VPoint *vp)
226 VVector vshift;
228 vshift.x = vp->x - ct->vp.x;
229 vshift.y = vp->y - ct->vp.y;
231 switch (quark_fid_get(ct->q)) {
232 case QFlavorFrame:
233 switch (ct->part) {
234 case 0:
235 frame_shift(ct->q, &vshift);
236 break;
237 case 1:
238 frame_legend_shift(ct->q, &vshift);
239 break;
241 break;
242 case QFlavorAText:
243 switch (ct->part) {
244 case 0:
245 atext_shift(ct->q, &vshift);
246 break;
247 case 1:
248 atext_at_shift(ct->q, &vshift);
249 break;
251 break;
252 case QFlavorAxis:
253 axis_shift(ct->q, &vshift);
254 break;
255 case QFlavorDObject:
256 object_shift(ct->q, &vshift);
257 break;
258 case QFlavorSet:
259 set_point_shift(ct->q, ct->part, &vshift);
260 break;
264 static Widget popup = NULL, poplab, drop_pt_bt, as_set_bt, atext_bt;
265 static Widget set_locator_bt, clear_locator_bt;
266 static Widget bring_to_front_bt, move_up_bt, move_down_bt, send_to_back_bt;
268 #define EDIT_CB 0
269 #define HIDE_CB 1
270 #define DELETE_CB 2
271 #define DUPLICATE_CB 3
272 #define BRING_TO_FRONT_CB 4
273 #define SEND_TO_BACK_CB 5
274 #define MOVE_UP_CB 6
275 #define MOVE_DOWN_CB 7
276 #define DROP_POINT_CB 8
277 #define AUTOSCALE_BY_SET_CB 9
279 static void popup_any_cb(canvas_target *ct, int type)
281 Quark *q = ct->q;
283 switch (type) {
284 case EDIT_CB:
285 raise_explorer(gui_from_quark(q), q);
286 return;
287 break;
288 case HIDE_CB:
289 quark_set_active(q, FALSE);
290 break;
291 case DELETE_CB:
292 quark_free(q);
293 break;
294 case DUPLICATE_CB:
295 quark_copy(q);
296 break;
297 case BRING_TO_FRONT_CB:
298 quark_push(q, TRUE);
299 break;
300 case SEND_TO_BACK_CB:
301 quark_push(q, FALSE);
302 break;
303 case MOVE_UP_CB:
304 quark_move(q, TRUE);
305 break;
306 case MOVE_DOWN_CB:
307 quark_move(q, FALSE);
308 break;
309 case DROP_POINT_CB:
310 if (quark_fid_get(q) == QFlavorSet && ct->part >= 0) {
311 del_point(q, ct->part);
313 break;
314 case AUTOSCALE_BY_SET_CB:
315 if (quark_fid_get(q) == QFlavorSet) {
316 autoscale_bysets(&q, 1, AUTOSCALE_XY);
318 break;
321 snapshot_and_update(gapp->gp, TRUE);
324 static void edit_cb(Widget but, void *udata)
326 popup_any_cb((canvas_target *) udata, EDIT_CB);
329 static void hide_cb(Widget but, void *udata)
331 popup_any_cb((canvas_target *) udata, HIDE_CB);
334 static void delete_cb(Widget but, void *udata)
336 if (yesno("Really delete this item?", NULL, NULL, NULL)) {
337 popup_any_cb((canvas_target *) udata, DELETE_CB);
341 static void duplicate_cb(Widget but, void *udata)
343 popup_any_cb((canvas_target *) udata, DUPLICATE_CB);
346 static void bring_to_front_cb(Widget but, void *udata)
348 popup_any_cb((canvas_target *) udata, BRING_TO_FRONT_CB);
351 static void send_to_back_cb(Widget but, void *udata)
353 popup_any_cb((canvas_target *) udata, SEND_TO_BACK_CB);
356 static void move_up_cb(Widget but, void *udata)
358 popup_any_cb((canvas_target *) udata, MOVE_UP_CB);
361 static void move_down_cb(Widget but, void *udata)
363 popup_any_cb((canvas_target *) udata, MOVE_DOWN_CB);
366 static void autoscale_cb(Widget but, void *udata)
368 popup_any_cb((canvas_target *) udata, AUTOSCALE_BY_SET_CB);
371 static void drop_point_cb(Widget but, void *udata)
373 popup_any_cb((canvas_target *) udata, DROP_POINT_CB);
376 static void atext_cb(Widget but, void *udata)
378 canvas_target *ct = (canvas_target *) udata;
379 APoint ap;
380 Quark *q;
382 q = atext_new(ct->q);
383 Vpoint2Apoint(q, &ct->vp, &ap);
384 atext_set_ap(q, &ap);
385 atext_set_pointer(q, TRUE);
387 snapshot_and_update(gapp->gp, TRUE);
389 raise_explorer(gui_from_quark(ct->q), q);
393 * clear the locator reference point
395 static void do_clear_point(Widget but, void *udata)
397 canvas_target *ct = (canvas_target *) udata;
398 GLocator *locator;
400 locator = graph_get_locator(ct->q);
401 locator->pointset = FALSE;
402 quark_dirtystate_set(ct->q, TRUE);
404 snapshot_and_update(gapp->gp, TRUE);
408 * set the locator reference point
410 static void set_locator_cb(Widget but, void *udata)
412 canvas_target *ct = (canvas_target *) udata;
413 GLocator *locator;
414 WPoint wp;
416 Vpoint2Wpoint(ct->q, &ct->vp, &wp);
418 locator = graph_get_locator(ct->q);
419 locator->origin = wp;
420 locator->pointset = TRUE;
421 quark_dirtystate_set(ct->q, TRUE);
423 snapshot_and_update(gapp->gp, TRUE);
426 void canvas_event(CanvasEvent *event)
428 int x, y; /* pointer coordinates */
429 VPoint vp;
430 Quark *cg = graph_get_current(gproject_get_top(gapp->gp));
431 X11Stuff *xstuff = gapp->gui->xstuff;
432 Widget drawing_window = gapp->gui->mwui->drawing_window;
434 static unsigned long lastc_time = 0; /* time of last mouse click */
435 static int lastc_x, lastc_y; /* coords of last mouse click */
436 static int last_b1down_x, last_b1down_y; /* coords of last event */
437 int dbl_click;
439 int undo_point = FALSE;
440 int abort_action = FALSE;
442 static canvas_target ct;
443 static int on_focus;
445 x = event->x;
446 y = event->y;
448 switch (event->type) {
449 case MOUSE_MOVE:
450 if (gapp->gui->crosshair_cursor) {
451 crosshair_motion(gapp->gui, x, y);
454 x11_dev2VPoint(x, y, &vp);
456 if (xstuff->collect_points && xstuff->npoints) {
457 switch (xstuff->sel_type) {
458 case SELECTION_TYPE_RECT:
459 select_region(gapp->gui,
460 x, y, last_b1down_x, last_b1down_y, TRUE);
461 break;
462 case SELECTION_TYPE_VERT:
463 select_vregion(gapp->gui, x, last_b1down_x, TRUE);
464 break;
465 case SELECTION_TYPE_HORZ:
466 select_hregion(gapp->gui, y, last_b1down_y, TRUE);
467 break;
469 } else
470 if (event->button & LEFT_BUTTON) {
471 if (event->modifiers & CONTROL_MODIFIER) {
472 if (on_focus) {
473 resize_region(gapp->gui, xstuff->f_v, on_focus,
474 x - last_b1down_x, y - last_b1down_y, TRUE);
475 } else
476 if (ct.found) {
477 slide_region(gapp->gui, ct.bbox,
478 x - last_b1down_x, y - last_b1down_y, TRUE);
480 } else {
481 scroll_pix(drawing_window, last_b1down_x - x, last_b1down_y - y);
483 } else {
484 if (gapp->gui->focus_policy == FOCUS_FOLLOWS) {
485 cg = next_graph_containing(cg, &vp);
488 if (event->modifiers & CONTROL_MODIFIER) {
489 if (abs(x - xstuff->f_x1) <= 5 &&
490 abs(y - xstuff->f_y1) <= 5) {
491 on_focus = 1;
492 } else
493 if (abs(x - xstuff->f_x1) <= 5 &&
494 abs(y - xstuff->f_y2) <= 5) {
495 on_focus = 2;
496 } else
497 if (abs(x - xstuff->f_x2) <= 5 &&
498 abs(y - xstuff->f_y2) <= 5) {
499 on_focus = 3;
500 } else
501 if (abs(x - xstuff->f_x2) <= 5 &&
502 abs(y - xstuff->f_y1) <= 5) {
503 on_focus = 4;
504 } else {
505 on_focus = 0;
507 if (on_focus) {
508 set_cursor(gapp->gui, 4);
509 } else {
510 set_cursor(gapp->gui, -1);
515 update_locator_lab(cg, &vp);
517 break;
518 case MOUSE_PRESS:
519 x = event->x;
520 y = event->y;
521 x11_dev2VPoint(x, y, &vp);
523 switch (event->button) {
524 case LEFT_BUTTON:
525 /* first, determine if it's a double click */
526 if (event->time - lastc_time < CLICK_INT &&
527 abs(x - lastc_x) < CLICK_DIST &&
528 abs(y - lastc_y) < CLICK_DIST) {
529 dbl_click = TRUE;
530 } else {
531 dbl_click = FALSE;
533 lastc_time = event->time;
534 lastc_x = x;
535 lastc_y = y;
537 if (!dbl_click) {
538 if (event->modifiers & CONTROL_MODIFIER) {
539 ct.vp = vp;
540 ct.include_graphs = FALSE;
541 if (on_focus) {
542 resize_region(gapp->gui, xstuff->f_v, on_focus,
543 0, 0, FALSE);
544 } else
545 if (find_target(gapp->gp, &ct) == RETURN_SUCCESS) {
546 slide_region(gapp->gui, ct.bbox, 0, 0, FALSE);
548 } else {
549 if (xstuff->collect_points) {
550 XPoint xp;
551 xp.x = x;
552 xp.y = y;
553 xstuff->npoints++;
554 xstuff->xps =
555 xrealloc(xstuff->xps, xstuff->npoints*sizeof(XPoint));
556 xstuff->xps[xstuff->npoints - 1] = xp;
557 select_region(gapp->gui, x, y, x, y, FALSE);
558 } else
559 if (gapp->gui->focus_policy == FOCUS_CLICK) {
560 cg = next_graph_containing(cg, &vp);
562 update_locator_lab(cg, &vp);
564 } else {
565 ct.vp = vp;
566 ct.include_graphs = (event->modifiers & CONTROL_MODIFIER) ? FALSE:TRUE;
567 if (find_target(gapp->gp, &ct) == RETURN_SUCCESS) {
568 raise_explorer(gapp->gui, ct.q);
569 ct.found = FALSE;
573 last_b1down_x = x;
574 last_b1down_y = y;
576 if (!xstuff->collect_points) {
577 set_cursor(gapp->gui, 5);
580 break;
581 case MIDDLE_BUTTON:
582 fprintf(stderr, "Button2\n");
583 break;
584 case RIGHT_BUTTON:
585 if (xstuff->collect_points) {
586 undo_point = TRUE;
587 if (xstuff->npoints) {
588 xstuff->npoints--;
590 if (xstuff->npoints == 0) {
591 abort_action = TRUE;
593 } else {
594 ct.vp = vp;
595 ct.include_graphs = (event->modifiers & CONTROL_MODIFIER) ? FALSE:TRUE;
596 if (find_target(gapp->gp, &ct) == RETURN_SUCCESS) {
597 char *s;
598 ct.found = FALSE;
600 if (!popup) {
601 popup = CreatePopupMenu(gapp->gui->xstuff->canvas);
603 poplab = CreateMenuLabel(popup, "");
605 CreateMenuSeparator(popup);
607 CreateMenuButton(popup,
608 "Properties...", '\0', edit_cb, &ct);
610 CreateMenuSeparator(popup);
612 CreateMenuButton(popup, "Hide", '\0', hide_cb, &ct);
614 CreateMenuSeparator(popup);
616 CreateMenuButton(popup,
617 "Delete", '\0', delete_cb, &ct);
618 CreateMenuButton(popup,
619 "Duplicate", '\0', duplicate_cb, &ct);
621 CreateMenuSeparator(popup);
623 bring_to_front_bt = CreateMenuButton(popup,
624 "Bring to front", '\0', bring_to_front_cb, &ct);
625 move_up_bt = CreateMenuButton(popup,
626 "Move up", '\0', move_up_cb, &ct);
627 move_down_bt = CreateMenuButton(popup,
628 "Move down", '\0', move_down_cb, &ct);
629 send_to_back_bt = CreateMenuButton(popup,
630 "Send to back", '\0', send_to_back_cb, &ct);
632 CreateMenuSeparator(popup);
634 as_set_bt = CreateMenuButton(popup,
635 "Autoscale by this set", '\0', autoscale_cb, &ct);
637 atext_bt = CreateMenuButton(popup,
638 "Annotate this point", '\0', atext_cb, &ct);
640 CreateMenuSeparator(popup);
642 drop_pt_bt = CreateMenuButton(popup,
643 "Drop this point", '\0', drop_point_cb, &ct);
645 set_locator_bt = CreateMenuButton(popup,
646 "Set locator fixed point", '\0', set_locator_cb, &ct);
647 clear_locator_bt = CreateMenuButton(popup,
648 "Clear locator fixed point", '\0', do_clear_point, &ct);
650 s = q_labeling(ct.q);
651 SetLabel(poplab, s);
652 xfree(s);
653 if (quark_is_last_child(ct.q)) {
654 SetSensitive(bring_to_front_bt, FALSE);
655 SetSensitive(move_up_bt, FALSE);
656 } else {
657 SetSensitive(bring_to_front_bt, TRUE);
658 SetSensitive(move_up_bt, TRUE);
660 if (quark_is_first_child(ct.q)) {
661 SetSensitive(send_to_back_bt, FALSE);
662 SetSensitive(move_down_bt, FALSE);
663 } else {
664 SetSensitive(send_to_back_bt, TRUE);
665 SetSensitive(move_down_bt, TRUE);
668 if ((quark_fid_get(ct.q) == QFlavorFrame && ct.part == 0) ||
669 (quark_fid_get(ct.q) == QFlavorGraph && ct.part == 0)) {
670 ManageChild(atext_bt);
671 } else {
672 UnmanageChild(atext_bt);
674 if (quark_fid_get(ct.q) == QFlavorGraph && ct.part != 1) {
675 ManageChild(set_locator_bt);
676 } else {
677 UnmanageChild(set_locator_bt);
679 if (quark_fid_get(ct.q) == QFlavorGraph && ct.part == 1) {
680 ManageChild(clear_locator_bt);
681 } else {
682 UnmanageChild(clear_locator_bt);
685 if (quark_fid_get(ct.q) == QFlavorSet) {
686 ManageChild(as_set_bt);
687 } else {
688 UnmanageChild(as_set_bt);
690 if (quark_fid_get(ct.q) == QFlavorSet && ct.part >= 0) {
691 ManageChild(drop_pt_bt);
692 } else {
693 UnmanageChild(drop_pt_bt);
696 ShowMenu(popup, event->udata);
699 break;
700 case WHEEL_UP_BUTTON:
701 scroll(drawing_window, TRUE, event->modifiers & CONTROL_MODIFIER);
702 break;
703 case WHEEL_DOWN_BUTTON:
704 scroll(drawing_window, FALSE, event->modifiers & CONTROL_MODIFIER);
705 break;
706 default:
707 break;
709 break;
710 case MOUSE_RELEASE:
711 switch (event->button) {
712 case LEFT_BUTTON:
713 if (event->modifiers & CONTROL_MODIFIER) {
714 x11_dev2VPoint(x, y, &vp);
715 if (on_focus) {
716 view v;
717 Quark *fr = get_parent_frame(graph_get_current(gproject_get_top(gapp->gp)));
718 frame_get_view(fr, &v);
719 switch (on_focus) {
720 case 1:
721 v.xv1 = vp.x;
722 v.yv1 = vp.y;
723 break;
724 case 2:
725 v.xv1 = vp.x;
726 v.yv2 = vp.y;
727 break;
728 case 3:
729 v.xv2 = vp.x;
730 v.yv2 = vp.y;
731 break;
732 case 4:
733 v.xv2 = vp.x;
734 v.yv1 = vp.y;
735 break;
737 frame_set_view(fr, &v);
738 } else
739 if (ct.found) {
740 slide_region(gapp->gui, ct.bbox, x - last_b1down_x, y - last_b1down_y, FALSE);
742 move_target(&ct, &vp);
744 ct.found = FALSE;
746 snapshot_and_update(gapp->gp, TRUE);
748 if (!xstuff->collect_points) {
749 set_cursor(gapp->gui, -1);
751 break;
753 break;
754 case KEY_PRESS:
755 switch (event->key) {
756 case KEY_ESCAPE: /* Esc */
757 abort_action = TRUE;
758 break;
759 case KEY_PLUS: /* "Grey" plus */
760 if (event->modifiers & CONTROL_MODIFIER) {
761 page_zoom_inout(gapp, +1);
763 break;
764 case KEY_MINUS: /* "Grey" minus */
765 if (event->modifiers & CONTROL_MODIFIER) {
766 page_zoom_inout(gapp, -1);
768 break;
769 case KEY_1:
770 if (event->modifiers & CONTROL_MODIFIER) {
771 page_zoom_inout(gapp, 0);
773 break;
775 break;
776 case KEY_RELEASE:
777 if (event->modifiers & CONTROL_MODIFIER) {
778 if (on_focus) {
779 set_cursor(gapp->gui, -1);
780 } else
781 if (ct.found) {
782 slide_region(gapp->gui, ct.bbox, x - last_b1down_x, y - last_b1down_y, FALSE);
783 ct.found = FALSE;
786 break;
787 default:
788 break;
791 if (abort_action && xstuff->collect_points) {
792 /* clear selection */
793 switch (xstuff->sel_type) {
794 case SELECTION_TYPE_RECT:
795 select_region(gapp->gui,
796 x, y, last_b1down_x, last_b1down_y, FALSE);
797 break;
798 case SELECTION_TYPE_VERT:
799 select_vregion(gapp->gui, x, last_b1down_x, FALSE);
800 break;
801 case SELECTION_TYPE_HORZ:
802 select_hregion(gapp->gui, y, last_b1down_y, FALSE);
803 break;
805 /* abort action */
806 xstuff->npoints = 0;
807 xstuff->collect_points = FALSE;
808 set_cursor(gapp->gui, -1);
809 set_left_footer(NULL);
810 } else
811 if (undo_point) {
812 /* previous action */
813 } else
814 if (xstuff->npoints_requested &&
815 xstuff->npoints == xstuff->npoints_requested) {
816 int ret;
817 unsigned int i;
818 VPoint *vps = xmalloc(xstuff->npoints*sizeof(VPoint));
819 for (i = 0; i < xstuff->npoints; i++) {
820 XPoint xp = xstuff->xps[i];
821 x11_dev2VPoint(xp.x, xp.y, &vps[i]);
823 /* return points to caller */
824 ret = xstuff->point_sink(xstuff->npoints, vps, xstuff->sink_data);
825 if (ret != RETURN_SUCCESS) {
826 Beep();
829 xfree(vps);
831 xstuff->npoints_requested = 0;
832 xstuff->collect_points = FALSE;
833 xstuff->npoints = 0;
834 set_cursor(gapp->gui, -1);
836 snapshot_and_update(gapp->gp, TRUE);
840 static int hook(Quark *q, void *udata, QTraverseClosure *closure)
842 if (quark_fid_get(q) == QFlavorGraph) {
843 Quark *pr = get_parent_project(q);
844 VPoint *vp = (VPoint *) udata;
845 view v;
847 closure->descend = FALSE;
849 if (quark_is_active(q) == TRUE &&
850 graph_get_viewport(q, &v) == RETURN_SUCCESS &&
851 is_vpoint_inside(&v, vp, 0.0) == TRUE &&
852 graph_get_current(pr) != q) {
853 switch_current_graph(q);
854 return FALSE;
856 } else
857 if (quark_fid_get(q) == QFlavorFrame && !quark_is_active(q)) {
858 closure->descend = FALSE;
861 return TRUE;
865 * Given the graph quark, find the (non-hidden) graph that contains
866 * the VPoint.
868 Quark *next_graph_containing(Quark *q, VPoint *vp)
870 Quark *pr = get_parent_project(q);
872 quark_traverse(pr, hook, vp);
874 return graph_get_current(pr);
878 * locator on main_panel
880 void update_locator_lab(Quark *cg, VPoint *vpp)
882 static VPoint vp = {0.0, 0.0};
883 view v;
884 GLocator *locator;
885 char buf[256];
887 if (vpp != NULL) {
888 vp = *vpp;
891 if (quark_is_active(cg) == TRUE &&
892 graph_get_viewport(cg, &v) == RETURN_SUCCESS &&
893 is_vpoint_inside(&v, &vp, 0.0) == TRUE &&
894 (locator = graph_get_locator(cg)) != NULL &&
895 locator->type != GLOCATOR_TYPE_NONE) {
896 char bufx[64], bufy[64], *s, *prefix, *sx, *sy;
897 WPoint wp;
898 double wx, wy, xtmp, ytmp;
900 Vpoint2Wpoint(cg, &vp, &wp);
901 wx = wp.x;
902 wy = wp.y;
904 if (locator->pointset) {
905 wx -= locator->origin.x;
906 wy -= locator->origin.y;
907 prefix = "d";
908 } else {
909 prefix = "";
912 switch (locator->type) {
913 case GLOCATOR_TYPE_XY:
914 xtmp = wx;
915 ytmp = wy;
916 sx = "X";
917 sy = "Y";
918 break;
919 case GLOCATOR_TYPE_POLAR:
920 xy2polar(wx, wy, &xtmp, &ytmp);
921 sx = "Phi";
922 sy = "Rho";
923 break;
924 default:
925 return;
927 s = create_fstring(get_parent_project(cg),
928 &locator->fx, xtmp, LFORMAT_TYPE_PLAIN);
929 strcpy(bufx, s);
930 s = create_fstring(get_parent_project(cg),
931 &locator->fy, ytmp, LFORMAT_TYPE_PLAIN);
932 strcpy(bufy, s);
934 sprintf(buf, "%s: %s%s, %s%s = (%s, %s)", QIDSTR(cg),
935 prefix, sx, prefix, sy, bufx, bufy);
936 } else {
937 sprintf(buf, "VX, VY = (%.4f, %.4f)", vp.x, vp.y);
940 set_tracker_string(buf);
943 void switch_current_graph(Quark *gr)
946 if (quark_is_active(gr)) {
947 GraceApp *gapp = gapp_from_quark(gr);
948 Quark *cg = graph_get_current(gproject_get_top(gapp->gp));
950 select_graph(gr);
951 draw_focus(cg);
952 draw_focus(gr);
953 update_all();
954 graph_set_selectors(gr);
955 update_locator_lab(cg, NULL);
959 static int zoom_sink(unsigned int npoints, const VPoint *vps, void *data)
961 GraceApp *gapp = (GraceApp *) data;
962 world w;
963 Quark *cg = graph_get_current(gproject_get_top(gapp->gp));
964 WPoint wp;
966 if (!cg || npoints != 2) {
967 return RETURN_FAILURE;
970 Vpoint2Wpoint(cg, &vps[0], &wp);
971 w.xg1 = wp.x;
972 w.yg1 = wp.y;
973 Vpoint2Wpoint(cg, &vps[1], &wp);
974 w.xg2 = wp.x;
975 w.yg2 = wp.y;
977 if (w.xg1 > w.xg2) {
978 fswap(&w.xg1, &w.xg2);
980 if (w.yg1 > w.yg2) {
981 fswap(&w.yg1, &w.yg2);
984 return graph_set_world(cg, &w);
987 static int zoomx_sink(unsigned int npoints, const VPoint *vps, void *data)
989 GraceApp *gapp = (GraceApp *) data;
990 world w;
991 Quark *cg = graph_get_current(gproject_get_top(gapp->gp));
992 WPoint wp;
994 if (!cg || npoints != 2) {
995 return RETURN_FAILURE;
998 graph_get_world(cg, &w);
1000 Vpoint2Wpoint(cg, &vps[0], &wp);
1001 w.xg1 = wp.x;
1002 Vpoint2Wpoint(cg, &vps[1], &wp);
1003 w.xg2 = wp.x;
1005 if (w.xg1 > w.xg2) {
1006 fswap(&w.xg1, &w.xg2);
1009 return graph_set_world(cg, &w);
1012 static int zoomy_sink(unsigned int npoints, const VPoint *vps, void *data)
1014 GraceApp *gapp = (GraceApp *) data;
1015 world w;
1016 Quark *cg = graph_get_current(gproject_get_top(gapp->gp));
1017 WPoint wp;
1019 if (!cg || npoints != 2) {
1020 return RETURN_FAILURE;
1023 graph_get_world(cg, &w);
1025 Vpoint2Wpoint(cg, &vps[0], &wp);
1026 w.yg1 = wp.y;
1027 Vpoint2Wpoint(cg, &vps[1], &wp);
1028 w.yg2 = wp.y;
1030 if (w.yg1 > w.yg2) {
1031 fswap(&w.yg1, &w.yg2);
1034 return graph_set_world(cg, &w);
1037 static int atext_sink(unsigned int npoints, const VPoint *vps, void *data)
1039 GraceApp *gapp = (GraceApp *) data;
1040 Quark *cg = graph_get_current(gproject_get_top(gapp->gp)), *q;
1041 WPoint wp;
1042 APoint ap;
1044 if (!cg || npoints != 1) {
1045 return RETURN_FAILURE;
1048 if (Vpoint2Wpoint(cg, &vps[0], &wp) == RETURN_SUCCESS &&
1049 is_validWPoint(cg, &wp) == TRUE) {
1050 q = atext_new(cg);
1051 ap.x = wp.x; ap.y = wp.y;
1052 atext_set_ap(q, &ap);
1053 } else {
1054 q = atext_new(gproject_get_top(gapp->gp));
1055 ap.x = vps[0].x; ap.y = vps[0].y;
1056 atext_set_ap(q, &ap);
1059 raise_explorer(gapp->gui, q);
1061 return RETURN_SUCCESS;
1064 void set_action(GUI *gui, unsigned int npoints, int seltype,
1065 CanvasPointSink sink, void *data)
1067 X11Stuff *xstuff = gui->xstuff;
1069 xstuff->npoints = 0;
1070 xstuff->npoints_requested = npoints;
1071 xstuff->point_sink = sink;
1072 xstuff->sink_data = data;
1073 xstuff->sel_type = seltype;
1075 xstuff->collect_points = TRUE;
1077 SetFocus(xstuff->canvas);
1080 /* -------------------------------------------------------------- */
1081 /* canvas_actions */
1082 void set_zoom_cb(Widget but, void *data)
1084 GraceApp *gapp = (GraceApp *) data;
1085 set_cursor(gapp->gui, 0);
1086 set_action(gapp->gui, 2, SELECTION_TYPE_RECT, zoom_sink, gapp);
1089 void set_zoomx_cb(Widget but, void *data)
1091 GraceApp *gapp = (GraceApp *) data;
1092 set_cursor(gapp->gui, 0);
1093 set_action(gapp->gui, 2, SELECTION_TYPE_VERT, zoomx_sink, gapp);
1096 void set_zoomy_cb(Widget but, void *data)
1098 GraceApp *gapp = (GraceApp *) data;
1099 set_cursor(gapp->gui, 0);
1100 set_action(gapp->gui, 2, SELECTION_TYPE_HORZ, zoomy_sink, gapp);
1104 void atext_add_proc(Widget but, void *data)
1106 GraceApp *gapp = (GraceApp *) data;
1107 set_cursor(gapp->gui, 2);
1108 set_action(gapp->gui, 1, SELECTION_TYPE_NONE, atext_sink, gapp);
1109 set_left_footer("Select an anchor point");