1 /*
2 * Copyright (c) 2005-2015 Hypertriton, Inc. <http://hypertriton.com/>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23 * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 /*
27 * Visualization widget.
28 */
29
30 #include <agar/core/core.h>
31 #include <agar/gui/gui.h>
32 #include <agar/gui/primitive.h>
33 #include <agar/gui/opengl.h>
34 #include <agar/vg/vg.h>
35 #include <agar/vg/vg_view.h>
36 #include <agar/vg/vg_tools.h>
37 #include <agar/vg/tools.h>
38
39 #include <stdarg.h>
40 #include <string.h>
41
42 static const float scaleFactors[] = {
43 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f,
44 12.0f, 14.0f, 16.0f, 18.0f, 20.0f, 22.0f, 24.0f, 26.0f, 28.0f,
45 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f,
46 200.0f, 300.0f, 400.0f, 600.0f, 700.0f, 800.0f, 900.0f
47 };
48 const int nScaleFactors = sizeof(scaleFactors)/sizeof(scaleFactors[0]);
49
50 VG_View *
VG_ViewNew(void * parent,VG * vg,Uint flags)51 VG_ViewNew(void *parent, VG *vg, Uint flags)
52 {
53 VG_View *vv;
54
55 vv = Malloc(sizeof(VG_View));
56 AG_ObjectInit(vv, &vgViewClass);
57 vv->flags |= flags;
58 vv->vg = vg;
59
60 if (flags & VG_VIEW_HFILL) { AG_ExpandHoriz(vv); }
61 if (flags & VG_VIEW_VFILL) { AG_ExpandVert(vv); }
62
63 AG_ObjectAttach(parent, vv);
64 return (vv);
65 }
66
67 static void
MouseMotion(AG_Event * event)68 MouseMotion(AG_Event *event)
69 {
70 VG_View *vv = AG_SELF();
71 VG_Tool *tool = VG_CURTOOL(vv);
72 int xCurs = AG_INT(1);
73 int yCurs = AG_INT(2);
74 float xRel = (float)AG_INT(3);
75 float yRel = (float)AG_INT(4);
76 int state = AG_INT(5);
77 float x, y;
78 VG_Vector vCt;
79
80 if (vv->mouse.panning) {
81 vv->x += (float)xRel;
82 vv->y += (float)yRel;
83 AG_Redraw(vv);
84 return;
85 }
86 if (vv->vg == NULL)
87 return;
88
89 VG_GetVGCoords(vv, xCurs,yCurs, &vCt);
90 x = vCt.x;
91 y = vCt.y;
92
93 if (tool != NULL && tool->ops->mousemotion != NULL) {
94 if (!(tool->ops->flags & VG_MOUSEMOTION_NOSNAP)) {
95 VG_ApplyConstraints(vv, &vCt);
96 }
97 tool->vCursor = vCt;
98 AG_Redraw(vv);
99 if (tool->ops->mousemotion(tool, vCt,
100 VG_ScaleVector(1.0f/vv->scale,VGVECTOR(xRel,yRel)),
101 state) == 1) {
102 vv->mouse.x = x;
103 vv->mouse.y = y;
104 return;
105 }
106 } else {
107 vv->mouse.x = x;
108 vv->mouse.y = y;
109 }
110 }
111
112 static void
MouseButtonDown(AG_Event * event)113 MouseButtonDown(AG_Event *event)
114 {
115 VG_View *vv = AG_SELF();
116 VG_Tool *tool = VG_CURTOOL(vv);
117 int button = AG_INT(1);
118 int xCurs = AG_INT(2);
119 int yCurs = AG_INT(3);
120 float x, y;
121 VG_Vector vCt;
122
123 if (vv->vg == NULL ||
124 AG_ExecMouseAction(vv, AG_ACTION_ON_BUTTONDOWN, button, xCurs, yCurs) == 1)
125 return;
126
127 VG_GetVGCoords(vv, xCurs,yCurs, &vCt);
128 x = vCt.x;
129 y = vCt.y;
130
131 if (!AG_WidgetIsFocused(vv)) {
132 AG_WidgetFocus(vv);
133 }
134 vv->mouse.x = x;
135 vv->mouse.y = y;
136
137 if (tool != NULL && tool->ops->mousebuttondown != NULL) {
138 if (!(tool->ops->flags & VG_BUTTONDOWN_NOSNAP)) {
139 VG_ApplyConstraints(vv, &vCt);
140 }
141 AG_Redraw(vv);
142 if (tool->ops->mousebuttondown(tool, vCt, button) == 1)
143 return;
144 }
145 if (vv->btndown_ev != NULL)
146 AG_PostEventByPtr(NULL, vv, vv->btndown_ev, "%i,%f,%f",
147 button, x, y);
148 }
149
150 static void
MouseButtonUp(AG_Event * event)151 MouseButtonUp(AG_Event *event)
152 {
153 VG_View *vv = AG_SELF();
154 VG_Tool *tool = VG_CURTOOL(vv);
155 int button = AG_INT(1);
156 int xCurs = AG_INT(2);
157 int yCurs = AG_INT(3);
158 float x, y;
159 VG_Vector vCt;
160
161 if (vv->vg == NULL ||
162 AG_ExecMouseAction(vv, AG_ACTION_ON_BUTTONUP, button, xCurs, yCurs) == 1)
163 return;
164
165 VG_GetVGCoords(vv, xCurs,yCurs, &vCt);
166 x = vCt.x;
167 y = vCt.y;
168
169 if (tool != NULL && tool->ops->mousebuttonup != NULL) {
170 if (!(tool->ops->flags & VG_BUTTONUP_NOSNAP)) {
171 VG_ApplyConstraints(vv, &vCt);
172 }
173 AG_Redraw(vv);
174 if (tool->ops->mousebuttonup(tool, vCt, button) == 1)
175 return;
176 }
177 if (vv->btnup_ev != NULL) {
178 AG_PostEventByPtr(NULL, vv, vv->btnup_ev, "%i,%f,%f",
179 button, x, y);
180 AG_Redraw(vv);
181 }
182 }
183
184 static void
KeyDown(AG_Event * event)185 KeyDown(AG_Event *event)
186 {
187 VG_View *vv = AG_SELF();
188 VG_Tool *tool = VG_CURTOOL(vv);
189 int sym = AG_INT(1);
190 int mod = AG_INT(2);
191 Uint32 unicode = (Uint)AG_ULONG(3);
192 VG_ToolCommand *cmd;
193
194 if (vv->vg == NULL)
195 return;
196 if (AG_ExecKeyAction(vv, AG_ACTION_ON_KEYDOWN, sym, mod))
197 return;
198
199 if (tool == NULL) {
200 return;
201 }
202 AG_Redraw(vv);
203 if (tool->ops->keydown != NULL &&
204 tool->ops->keydown(tool, sym, mod, unicode) == 1) {
205 return;
206 }
207 TAILQ_FOREACH(cmd, &tool->cmds, cmds) {
208 if (cmd->kSym == sym &&
209 (cmd->kMod == AG_KEYMOD_NONE || mod & cmd->kMod)) {
210 AG_PostEventByPtr(NULL, tool->vgv, cmd->fn, "%p", tool);
211 AG_Redraw(vv);
212 }
213 }
214 }
215
216 static void
KeyUp(AG_Event * event)217 KeyUp(AG_Event *event)
218 {
219 VG_View *vv = AG_SELF();
220 VG_Tool *tool = VG_CURTOOL(vv);
221 int sym = AG_INT(1);
222 int mod = AG_INT(2);
223 Uint32 unicode = (Uint32)AG_ULONG(3);
224
225 if (vv->vg == NULL)
226 return;
227 if (AG_ExecKeyAction(vv, AG_ACTION_ON_KEYUP, sym, mod))
228 return;
229
230 AG_Redraw(vv);
231
232 if (tool != NULL &&
233 tool->ops->keyup != NULL)
234 tool->ops->keyup(tool, sym, mod, unicode);
235 }
236
237 static void
OnShow(AG_Event * event)238 OnShow(AG_Event *event)
239 {
240 VG_View *vv = AG_SELF();
241
242 vv->x = AGWIDGET(vv)->w/2.0f;
243 vv->y = AGWIDGET(vv)->h/2.0f;
244 }
245
246 static void
UpdateGridIntervals(VG_View * vv)247 UpdateGridIntervals(VG_View *vv)
248 {
249 int i;
250
251 for (i = 0; i < vv->nGrids; i++) {
252 VG_Grid *grid = &vv->grid[i];
253
254 grid->ivalView = (int)VG_Rint(((float)grid->ival)/vv->wPixel);
255
256 if (grid->ivalView < 6) {
257 grid->flags |= VG_GRID_UNDERSIZE;
258 } else {
259 grid->flags &= ~(VG_GRID_UNDERSIZE);
260 }
261 }
262 }
263
264 static void
ZoomInOut(AG_Event * event)265 ZoomInOut(AG_Event *event)
266 {
267 VG_View *vv = AG_SELF();
268 int inc = AG_INT(1);
269
270 VG_ViewSetScalePreset(vv, (vv->scaleIdx += inc));
271 VG_Status(vv, _("Scale: 1:%.0f"), vv->scale);
272 }
273
274 static void
ZoomInMax(AG_Event * event)275 ZoomInMax(AG_Event *event)
276 {
277 VG_View *vv = AG_SELF();
278
279 VG_ViewSetScalePreset(vv, nScaleFactors-1);
280 VG_Status(vv, _("Scale: 1:%.0f"), vv->scale);
281 }
282
283 static void
ZoomOutMax(AG_Event * event)284 ZoomOutMax(AG_Event *event)
285 {
286 VG_View *vv = AG_SELF();
287
288 VG_ViewSetScalePreset(vv, 0);
289 VG_Status(vv, _("Scale: 1:%.0f"), vv->scale);
290 }
291
292 static void
SetScale(AG_Event * event)293 SetScale(AG_Event *event)
294 {
295 VG_View *vv = AG_SELF();
296 int n = AG_INT(1);
297
298 VG_ViewSetScalePreset(vv, n);
299 VG_Status(vv, _("Scale: 1:%.0f"), vv->scale);
300 }
301
302 static void
Init(void * obj)303 Init(void *obj)
304 {
305 VG_View *vv = obj;
306
307 WIDGET(vv)->flags |= AG_WIDGET_FOCUSABLE;
308
309 vv->flags = 0;
310 vv->vg = NULL;
311 vv->draw_ev = NULL;
312 vv->scale_ev = NULL;
313 vv->keydown_ev = NULL;
314 vv->btndown_ev = NULL;
315 vv->keyup_ev = NULL;
316 vv->btnup_ev = NULL;
317 vv->motion_ev = NULL;
318 vv->x = 0.0f;
319 vv->y = 0.0f;
320 vv->scaleIdx = 0;
321 vv->scale = scaleFactors[0];
322 vv->scaleMin = 1.0f;
323 vv->scaleMax = 1e6f;
324 vv->wPixel = 1.0f;
325 vv->snap_mode = VG_GRID;
326 vv->ortho_mode = VG_NO_ORTHO;
327 vv->mouse.x = 0.0f;
328 vv->mouse.y = 0.0f;
329 vv->mouse.panning = 0;
330 vv->curtool = NULL;
331 vv->deftool = NULL;
332 vv->status[0] = '\0';
333 vv->tCache = AG_TextCacheNew(vv, 64, 2);
334 vv->editAreas = NULL;
335 vv->nEditAreas = 0;
336 vv->r = AG_RECT(0,0,0,0);
337 TAILQ_INIT(&vv->tools);
338
339 vv->nGrids = 0;
340 VG_ViewSetGrid(vv, 0, VG_GRID_POINTS, 8, VG_GetColorRGB(100,100,100));
341 VG_ViewSetScale(vv, 0);
342
343 AG_AddEvent(vv, "widget-shown", OnShow, NULL);
344 AG_SetEvent(vv, "mouse-motion", MouseMotion, NULL);
345 AG_SetEvent(vv, "mouse-button-down", MouseButtonDown, NULL);
346 AG_SetEvent(vv, "mouse-button-up", MouseButtonUp, NULL);
347 AG_SetEvent(vv, "key-down", KeyDown, NULL);
348 AG_SetEvent(vv, "key-up", KeyUp, NULL);
349
350 AG_ActionSetInt(vv, "Enable panning", &vv->mouse.panning, 1);
351 AG_ActionSetInt(vv, "Disable panning", &vv->mouse.panning, 0);
352 AG_ActionFn(vv, "Zoom in", ZoomInOut, "%i", 1);
353 AG_ActionFn(vv, "Zoom out", ZoomInOut, "%i", -1);
354 AG_ActionFn(vv, "Zoom in maximum", ZoomInMax, NULL);
355 AG_ActionFn(vv, "Zoom out maximum", ZoomOutMax, NULL);
356 AG_ActionFn(vv, "Scale 1:1", SetScale, "%i", 0);
357 AG_ActionFn(vv, "Scale 1:2", SetScale, "%i", 1);
358 AG_ActionFn(vv, "Scale 1:3", SetScale, "%i", 2);
359 AG_ActionFn(vv, "Scale 1:9", SetScale, "%i", 8);
360
361 AG_ActionOnButtonDown(vv, AG_MOUSE_MIDDLE, "Enable panning");
362 AG_ActionOnButtonUp(vv, AG_MOUSE_MIDDLE, "Disable panning");
363 AG_ActionOnButton(vv, AG_MOUSE_WHEELUP, "Zoom in");
364 AG_ActionOnButton(vv, AG_MOUSE_WHEELDOWN, "Zoom out");
365 AG_ActionOnKeyDown(vv, AG_KEY_1, AG_KEYMOD_ANY, "Scale 1:1");
366 AG_ActionOnKeyDown(vv, AG_KEY_2, AG_KEYMOD_ANY, "Scale 1:2");
367 AG_ActionOnKeyDown(vv, AG_KEY_3, AG_KEYMOD_ANY, "Scale 1:3");
368 AG_ActionOnKeyDown(vv, AG_KEY_9, AG_KEYMOD_ANY, "Scale 1:9");
369
370 #ifdef AG_DEBUG
371 AG_BindFloat(vv, "x", &vv->x);
372 AG_BindFloat(vv, "y", &vv->y);
373 AG_BindFloat(vv, "scale", &vv->scale);
374 AG_BindFloat(vv, "wPixel", &vv->wPixel);
375 AG_BindInt(vv, "pointSelRadius", &vv->pointSelRadius);
376 #endif /* AG_DEBUG */
377 }
378
379 static void
Destroy(void * obj)380 Destroy(void *obj)
381 {
382 VG_View *vv = obj;
383
384 AG_TextCacheDestroy(vv->tCache);
385 }
386
387 /* Change the VG being displayed. */
388 void
VG_ViewSetVG(VG_View * vv,VG * vg)389 VG_ViewSetVG(VG_View *vv, VG *vg)
390 {
391 AG_ObjectLock(vv);
392 if (vv->vg != vg) {
393 vv->vg = vg;
394 VG_ViewSelectTool(vv, NULL, NULL);
395 }
396 AG_ObjectUnlock(vv);
397 AG_Redraw(vv);
398 }
399
400 /* Set the snapping constraint. */
401 void
VG_ViewSetSnapMode(VG_View * vv,enum vg_snap_mode mode)402 VG_ViewSetSnapMode(VG_View *vv, enum vg_snap_mode mode)
403 {
404 AG_ObjectLock(vv);
405 vv->snap_mode = mode;
406 AG_ObjectUnlock(vv);
407 }
408
409 /* Set the orthogonal constraint. */
410 void
VG_ViewSetOrthoMode(VG_View * vv,enum vg_ortho_mode mode)411 VG_ViewSetOrthoMode(VG_View *vv, enum vg_ortho_mode mode)
412 {
413 AG_ObjectLock(vv);
414 vv->ortho_mode = mode;
415 AG_ObjectUnlock(vv);
416 }
417
418 /* Set the parameters of the specified grid. */
419 void
VG_ViewSetGrid(VG_View * vv,int idx,enum vg_grid_type type,int ival,VG_Color color)420 VG_ViewSetGrid(VG_View *vv, int idx, enum vg_grid_type type, int ival,
421 VG_Color color)
422 {
423 if (idx+1 > VG_GRIDS_MAX)
424 AG_FatalError("Too many grids");
425
426 AG_ObjectLock(vv);
427 vv->grid[idx].type = type;
428 vv->grid[idx].ival = ival;
429 vv->grid[idx].ivalView = 0;
430 vv->grid[idx].color = color;
431 vv->grid[idx].flags = 0;
432 if (idx == 0) {
433 vv->pointSelRadius = vv->grid[0].ival/2;
434 }
435 if (idx >= vv->nGrids) {
436 vv->nGrids = idx+1;
437 }
438 UpdateGridIntervals(vv);
439 AG_ObjectUnlock(vv);
440 AG_Redraw(vv);
441 }
442
443 /* Register a "draw" callback. */
444 void
VG_ViewDrawFn(VG_View * vv,AG_EventFn fn,const char * fmt,...)445 VG_ViewDrawFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
446 {
447 AG_ObjectLock(vv);
448 vv->draw_ev = AG_SetVoidFn(vv, fn, NULL);
449 AG_EVENT_GET_ARGS(vv->draw_ev, fmt);
450 AG_ObjectUnlock(vv);
451 }
452
453 /* Register a "resize" callback. */
454 void
VG_ViewScaleFn(VG_View * vv,AG_EventFn fn,const char * fmt,...)455 VG_ViewScaleFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
456 {
457 AG_ObjectLock(vv);
458 vv->scale_ev = AG_SetVoidFn(vv, fn, NULL);
459 AG_EVENT_GET_ARGS(vv->scale_ev, fmt);
460 AG_ObjectUnlock(vv);
461 }
462
463 /* Register a keydown callback. */
464 void
VG_ViewKeydownFn(VG_View * vv,AG_EventFn fn,const char * fmt,...)465 VG_ViewKeydownFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
466 {
467 AG_ObjectLock(vv);
468 vv->keydown_ev = AG_SetEvent(vv, "key-down", fn, NULL);
469 AG_EVENT_GET_ARGS(vv->keydown_ev, fmt);
470 AG_ObjectUnlock(vv);
471 }
472
473 /* Register a keyup callback. */
474 void
VG_ViewKeyupFn(VG_View * vv,AG_EventFn fn,const char * fmt,...)475 VG_ViewKeyupFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
476 {
477 AG_ObjectLock(vv);
478 vv->keyup_ev = AG_SetEvent(vv, "key-up", fn, NULL);
479 AG_EVENT_GET_ARGS(vv->keyup_ev, fmt);
480 AG_ObjectUnlock(vv);
481 }
482
483 /* Register a mousebuttondown callback. */
484 void
VG_ViewButtondownFn(VG_View * vv,AG_EventFn fn,const char * fmt,...)485 VG_ViewButtondownFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
486 {
487 AG_ObjectLock(vv);
488 vv->btndown_ev = AG_SetVoidFn(vv, fn, NULL);
489 AG_EVENT_GET_ARGS(vv->btndown_ev, fmt);
490 AG_ObjectUnlock(vv);
491 }
492
493 /* Register a mousebuttonup callback. */
494 void
VG_ViewButtonupFn(VG_View * vv,AG_EventFn fn,const char * fmt,...)495 VG_ViewButtonupFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
496 {
497 AG_ObjectLock(vv);
498 vv->btnup_ev = AG_SetEvent(vv, "mouse-button-up", fn, NULL);
499 AG_EVENT_GET_ARGS(vv->btnup_ev, fmt);
500 AG_ObjectUnlock(vv);
501 }
502
503 /* Register a mousemotion callback. */
504 void
VG_ViewMotionFn(VG_View * vv,AG_EventFn fn,const char * fmt,...)505 VG_ViewMotionFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
506 {
507 AG_ObjectLock(vv);
508 vv->motion_ev = AG_SetEvent(vv, "mouse-motion", fn, NULL);
509 AG_EVENT_GET_ARGS(vv->motion_ev, fmt);
510 AG_ObjectUnlock(vv);
511 }
512
513 static void
SizeRequest(void * obj,AG_SizeReq * r)514 SizeRequest(void *obj, AG_SizeReq *r)
515 {
516 r->w = 320;
517 r->h = 240;
518 }
519
520 static int
SizeAllocate(void * obj,const AG_SizeAlloc * a)521 SizeAllocate(void *obj, const AG_SizeAlloc *a)
522 {
523 VG_View *vv = obj;
524
525 if (a->w < 16 || a->h < 16) {
526 return (-1);
527 }
528 vv->r.w = a->w;
529 vv->r.h = a->h;
530 return (0);
531 }
532
533 static __inline__ void
DrawGrid(VG_View * vv,const VG_Grid * grid)534 DrawGrid(VG_View *vv, const VG_Grid *grid)
535 {
536 int x, x0, y, ival;
537
538 if (grid->flags & (VG_GRID_HIDE|VG_GRID_UNDERSIZE))
539 return;
540
541 ival = grid->ivalView;
542 #ifdef HAVE_OPENGL
543 if (AGDRIVER_CLASS(WIDGET(vv)->drv)->flags & AG_DRIVER_OPENGL) {
544 x0 = WIDGET(vv)->rView.x1 + (int)(vv->x)%ival;
545 y = WIDGET(vv)->rView.y1 + (int)(vv->y)%ival;
546 glBegin(GL_POINTS);
547 glColor3ub(grid->color.r, grid->color.g, grid->color.b);
548 for (; y < WIDGET(vv)->rView.y2; y += ival) {
549 for (x = x0; x < WIDGET(vv)->rView.x2; x += ival)
550 glVertex2s(x, y);
551 }
552 glEnd();
553 } else
554 #endif
555 {
556 AG_Color c;
557
558 x0 = (int)(vv->x)%ival;
559 y = (int)(vv->y)%ival;
560 c = VG_MapColorRGB(grid->color);
561 for (; y < WIDGET(vv)->rView.y2; y += ival) {
562 for (x = x0; x < WIDGET(vv)->rView.x2; x += ival)
563 AG_PutPixel(vv, x,y, c);
564 }
565 }
566 }
567
568 #ifdef AG_DEBUG
569 static void
DrawNodeExtent(VG_Node * vn,VG_View * vv)570 DrawNodeExtent(VG_Node *vn, VG_View *vv)
571 {
572 AG_Rect rExt;
573 VG_Vector a, b;
574
575 if (vn->ops->extent == NULL) {
576 return;
577 }
578 vn->ops->extent(vn, vv, &a, &b);
579
580 VG_GetViewCoords(vv, a, &rExt.x, &rExt.y);
581 rExt.w = (int)((b.x - a.x)*vv->scale);
582 rExt.h = (int)((b.y - a.y)*vv->scale);
583 AG_DrawRectOutline(vv, rExt, AG_ColorRGB(250,0,0));
584 }
585 #endif /* AG_DEBUG */
586
587 static void
DrawNode(VG * vg,VG_Node * vn,VG_View * vv)588 DrawNode(VG *vg, VG_Node *vn, VG_View *vv)
589 {
590 VG_Node *vnChld;
591 VG_Color colorSave;
592
593 VG_PushMatrix(vg);
594 VG_MultMatrix(&vg->T[vg->nT-1], &vn->T);
595
596 VG_FOREACH_CHLD(vnChld, vn, vg_node)
597 DrawNode(vg, vnChld, vv);
598 #ifdef AG_DEBUG
599 if (vv->flags & VG_VIEW_EXTENTS)
600 DrawNodeExtent(vn, vv);
601 #endif
602 colorSave = vn->color;
603 if (vn->flags & VG_NODE_SELECTED) {
604 VG_BlendColors(&vn->color, vg->selectionColor);
605 }
606 if (vn->flags & VG_NODE_MOUSEOVER) {
607 VG_BlendColors(&vn->color, vg->mouseoverColor);
608 }
609 vn->ops->draw(vn, vv);
610 vn->color = colorSave;
611
612 VG_PopMatrix(vg);
613 }
614
615 static void
Draw(void * obj)616 Draw(void *obj)
617 {
618 VG_View *vv = obj;
619 VG *vg = vv->vg;
620 int su, i;
621
622 if (vg == NULL)
623 return;
624
625 if (!(vv->flags & VG_VIEW_DISABLE_BG))
626 AG_DrawRect(vv, vv->r, VG_MapColorRGBA(vg->fillColor));
627
628 AG_PushClipRect(vv, vv->r);
629
630 if (vv->flags & VG_VIEW_GRID)
631 for (i = 0; i < vv->nGrids; i++)
632 DrawGrid(vv, &vv->grid[i]);
633
634 VG_Lock(vg);
635
636 if (vv->curtool != NULL && vv->curtool->ops->predraw != NULL) {
637 vv->curtool->ops->predraw(vv->curtool, vv);
638 }
639 if (vv->draw_ev != NULL) {
640 vv->draw_ev->fn.fnVoid(vv->draw_ev);
641 }
642 if (vv->curtool != NULL && vv->curtool->ops->postdraw != NULL) {
643 vv->curtool->ops->postdraw(vv->curtool, vv);
644 }
645
646 DrawNode(vg, vg->root, vv);
647 VG_Unlock(vg);
648
649 if (vv->status[0] != '\0') {
650 AG_PushTextState();
651 AG_TextColor(WCOLOR(vg,TEXT_COLOR));
652 if ((su = AG_TextCacheGet(vv->tCache, vv->status)) != -1) {
653 AG_WidgetBlitSurface(vv, su,
654 0,
655 HEIGHT(vv) - WSURFACE(vv,su)->h);
656 }
657 AG_PopTextState();
658 }
659 AG_PopClipRect(vv);
660 }
661
662 /* Select a new tool to use. */
663 void
VG_ViewSelectTool(VG_View * vv,void * pTool,void * p)664 VG_ViewSelectTool(VG_View *vv, void *pTool, void *p)
665 {
666 VG_Tool *ntool = pTool;
667 int i;
668
669 AG_ObjectLock(vv);
670
671 if (ntool == NULL || !(ntool->ops->flags & VG_NOEDITCLEAR)) {
672 for (i = 0; i < vv->nEditAreas; i++) {
673 AG_ObjectFreeChildren(vv->editAreas[i]);
674 AG_WidgetUpdate(vv->editAreas[i]);
675 }
676 }
677 if (vv->curtool != NULL) {
678 if (ntool != NULL && ntool->ops == vv->curtool->ops) {
679 goto out;
680 }
681 if (vv->curtool->editWin != NULL) {
682 AG_WindowHide(vv->curtool->editWin);
683 }
684 if (vv->curtool->ops->deselected != NULL) {
685 vv->curtool->ops->deselected(ntool, vv);
686 }
687 vv->curtool->selected = 0;
688 }
689
690 vv->curtool = ntool;
691 if (ntool != NULL) {
692 ntool->selected = 1;
693 ntool->p = p;
694 ntool->vgv = vv;
695
696 if (ntool->editWin != NULL) {
697 AG_WindowShow(ntool->editWin);
698 }
699 if (ntool->ops->edit != NULL && vv->nEditAreas > 0) {
700 AG_ObjectAttach(vv->editAreas[0],
701 ntool->ops->edit(ntool,vv));
702 AG_WidgetUpdate(vv->editAreas[0]);
703 }
704
705 VG_Status(vv, _("Tool: %s"), ntool->ops->name);
706 if (ntool->ops->selected != NULL)
707 ntool->ops->selected(ntool, vv);
708 } else {
709 VG_StatusS(vv, NULL);
710 }
711 out:
712 AG_ObjectUnlock(vv);
713 AG_Redraw(vv);
714 }
715
716 /* Generic event handler for tool selection. */
717 void
VG_ViewSelectToolEv(AG_Event * event)718 VG_ViewSelectToolEv(AG_Event *event)
719 {
720 VG_View *vv = AG_PTR(1);
721
722 if (!AG_WidgetIsFocused(vv)) {
723 AG_WidgetFocus(vv);
724 }
725 VG_ViewSelectTool(vv, AG_PTR(2), AG_PTR(3));
726 }
727
728 /* VG_View must be locked */
729 VG_Tool *
VG_ViewFindTool(VG_View * vv,const char * name)730 VG_ViewFindTool(VG_View *vv, const char *name)
731 {
732 VG_Tool *tool;
733
734 TAILQ_FOREACH(tool, &vv->tools, tools) {
735 if (strcmp(tool->ops->name, name) == 0)
736 return (tool);
737 }
738 AG_SetError("No such tool: %s", name);
739 return (NULL);
740 }
741
742 /* VG_View must be locked */
743 VG_Tool *
VG_ViewFindToolByOps(VG_View * vv,const VG_ToolOps * ops)744 VG_ViewFindToolByOps(VG_View *vv, const VG_ToolOps *ops)
745 {
746 VG_Tool *tool;
747
748 TAILQ_FOREACH(tool, &vv->tools, tools) {
749 if (tool->ops == ops)
750 return (tool);
751 }
752 AG_SetError("No such tool: %p", ops);
753 return (NULL);
754 }
755
756 /* Register a new VG_Tool class. */
757 VG_Tool *
VG_ViewRegTool(VG_View * vv,const VG_ToolOps * ops,void * p)758 VG_ViewRegTool(VG_View *vv, const VG_ToolOps *ops, void *p)
759 {
760 VG_Tool *t;
761
762 t = Malloc(ops->len);
763 t->ops = ops;
764 t->vgv = vv;
765 t->p = p;
766
767 AG_ObjectLock(vv);
768 VG_ToolInit(t);
769 TAILQ_INSERT_TAIL(&vv->tools, t, tools);
770 AG_ObjectUnlock(vv);
771 return (t);
772 }
773
774 /* Set the minimum allowed display scale factor. */
775 void
VG_ViewSetScaleMin(VG_View * vv,float scaleMin)776 VG_ViewSetScaleMin(VG_View *vv, float scaleMin)
777 {
778 AG_ObjectLock(vv);
779 vv->scaleMin = MAX(scaleMin,1e-6f);
780 AG_ObjectUnlock(vv);
781 }
782
783 /* Set the maximum allowed display scale factor. */
784 void
VG_ViewSetScaleMax(VG_View * vv,float scaleMax)785 VG_ViewSetScaleMax(VG_View *vv, float scaleMax)
786 {
787 AG_ObjectLock(vv);
788 vv->scaleMax = scaleMax;
789 AG_ObjectUnlock(vv);
790 }
791
792 /* Set the display scale factor explicitely. */
793 void
VG_ViewSetScale(VG_View * vv,float c)794 VG_ViewSetScale(VG_View *vv, float c)
795 {
796 float scalePrev;
797
798 AG_ObjectLock(vv);
799 scalePrev = vv->scale;
800
801 /* Set the specified scaling factor. */
802 /* vv->scaleIdx = idx; XXX */
803 vv->scale = c;
804 if (vv->scale < vv->scaleMin) { vv->scale = vv->scaleMin; }
805 if (vv->scale > vv->scaleMax) { vv->scale = vv->scaleMax; }
806
807 /* Update all values dependent on VG's representation of a pixel. */
808 vv->wPixel = 1.0/vv->scale;
809 vv->x *= (vv->scale/scalePrev);
810 vv->y *= (vv->scale/scalePrev);
811 vv->pointSelRadius = vv->grid[0].ival/2;
812 UpdateGridIntervals(vv);
813
814 AG_ObjectUnlock(vv);
815 AG_Redraw(vv);
816 }
817
818 /* Set the display scale factor by preset index. */
819 void
VG_ViewSetScalePreset(VG_View * vv,int idx)820 VG_ViewSetScalePreset(VG_View *vv, int idx)
821 {
822 if (idx < 0) { idx = 0; }
823 else if (idx >= nScaleFactors) { idx = nScaleFactors-1; }
824 VG_ViewSetScale(vv, scaleFactors[idx]);
825 }
826
827 /* Set the specified tool as default. */
828 void
VG_ViewSetDefaultTool(VG_View * vv,VG_Tool * tool)829 VG_ViewSetDefaultTool(VG_View *vv, VG_Tool *tool)
830 {
831 AG_ObjectLock(vv);
832 vv->deftool = tool;
833 AG_ObjectUnlock(vv);
834 }
835
836 /* Set the status line text (C string). */
837 void
VG_StatusS(VG_View * vv,const char * s)838 VG_StatusS(VG_View *vv, const char *s)
839 {
840 AG_ObjectLock(vv);
841 if (s != NULL) {
842 Strlcpy(vv->status, s, sizeof(vv->status));
843 } else {
844 vv->status[0] = '\0';
845 }
846 AG_ObjectUnlock(vv);
847 AG_Redraw(vv);
848 }
849
850 /* Set the status line text (format string). */
851 void
VG_Status(VG_View * vv,const char * fmt,...)852 VG_Status(VG_View *vv, const char *fmt, ...)
853 {
854 va_list ap;
855
856 AG_ObjectLock(vv);
857 if (fmt != NULL) {
858 va_start(ap, fmt);
859 Vsnprintf(vv->status, sizeof(vv->status), fmt, ap);
860 va_end(ap);
861 } else {
862 vv->status[0] = '\0';
863 }
864 AG_ObjectUnlock(vv);
865 AG_Redraw(vv);
866 }
867
868 /* Register a new container widget to associate with the tool. */
869 Uint
VG_AddEditArea(VG_View * vv,void * widget)870 VG_AddEditArea(VG_View *vv, void *widget)
871 {
872 Uint name;
873
874 AG_ObjectLock(vv);
875 vv->editAreas = Realloc(vv->editAreas, (vv->nEditAreas+1) *
876 sizeof(AG_Widget *));
877 name = vv->nEditAreas++;
878 vv->editAreas[name] = widget;
879 AG_ObjectUnlock(vv);
880 return (name);
881 }
882
883 /* Destroy widgets attached to all edit areas. */
884 void
VG_ClearEditAreas(VG_View * vv)885 VG_ClearEditAreas(VG_View *vv)
886 {
887 Uint i;
888
889 AG_ObjectLock(vv);
890 for (i = 0; i < vv->nEditAreas; i++) {
891 AG_Widget *editArea = vv->editAreas[i];
892
893 AG_ObjectFreeChildren(editArea);
894 AG_WidgetUpdate(editArea);
895 AG_WidgetHideAll(editArea);
896 }
897 AG_ObjectUnlock(vv);
898 }
899
900 /* Create GUI elements for editing the parameters of vn. */
901 void
VG_EditNode(VG_View * vv,Uint editArea,VG_Node * vn)902 VG_EditNode(VG_View *vv, Uint editArea, VG_Node *vn)
903 {
904 void *wEdit;
905
906 if (vv->nEditAreas > editArea &&
907 vn->ops->edit != NULL &&
908 (wEdit = vn->ops->edit(vn, vv)) != NULL) {
909 AG_ObjectFreeChildren(vv->editAreas[editArea]);
910 AG_ObjectAttach(vv->editAreas[editArea], wEdit);
911 AG_WidgetUpdate(vv->editAreas[editArea]);
912 AG_WidgetShowAll(vv->editAreas[editArea]);
913 }
914 }
915
916 /* Render a mapped surface at the specified coordinates and rotation. */
917 void
VG_DrawSurface(VG_View * vv,int x,int y,float degs,int su)918 VG_DrawSurface(VG_View *vv, int x, int y, float degs, int su)
919 {
920 #ifdef HAVE_OPENGL
921 if (AGDRIVER_CLASS(WIDGET(vv)->drv)->flags & AG_DRIVER_OPENGL) {
922 glPushMatrix();
923 glTranslatef((float)(AGWIDGET(vv)->rView.x1 + x),
924 (float)(AGWIDGET(vv)->rView.y1 + y),
925 0.0f);
926 if (degs != 0.0f) {
927 glRotatef(degs, 0.0f, 0.0f, 1.0f);
928 }
929 AG_WidgetBlitSurfaceGL(vv, su,
930 WSURFACE(vv,su)->w,
931 WSURFACE(vv,su)->h);
932 glPopMatrix();
933 } else
934 #endif /* HAVE_OPENGL */
935 {
936 AG_WidgetBlitSurface(vv, su, x, y);
937 }
938 }
939
940 AG_WidgetClass vgViewClass = {
941 {
942 "Agar(Widget):VG(View)",
943 sizeof(VG_View),
944 { 0,0 },
945 Init,
946 NULL, /* free */
947 Destroy,
948 NULL, /* load */
949 NULL, /* save */
950 NULL /* edit */
951 },
952 Draw,
953 SizeRequest,
954 SizeAllocate
955 };
956