1 /*
2 * $Id: xfancy.c,v 1.7 2008-10-28 04:02:30 dhmunro Exp $
3 * Implement the basic X windows engine for GIST.
4 */
5 /* Copyright (c) 2005, The Regents of the University of California.
6 * All rights reserved.
7 * This file is part of yorick (http://yorick.sourceforge.net).
8 * Read the accompanying LICENSE file for details.
9 */
10
11 #include "xfancy.h"
12 #include "draw.h"
13
14 #include <string.h>
15
16 /* possibly should just include stdio.h, math.h */
17 extern int sprintf(char *s, const char *format, ...);
18 extern double log10(double);
19
20 #ifndef NO_EXP10
21 extern double exp10(double);
22 #else
23 # define exp10(x) pow(10.,x)
24 extern double pow(double,double);
25 #endif
26
27 /* ------------------------------------------------------------------------ */
28
29 static void HandleExpose(Engine *engine, Drauing *drawing, int *xy);
30 static void HandleClick(Engine *e,int b,int md,int x,int y, unsigned long ms);
31 static void HandleMotion(Engine *e,int md,int x,int y);
32 static void HandleKey(Engine *e,int k,int md);
33
34 static void RedrawMessage(FXEngine *fxe);
35 static void MovePointer(FXEngine *fxe, Drauing *drawing,
36 int md,int x,int y);
37 static void PressZoom(FXEngine *fxe, Drauing *drawing,
38 int b,int md,int x,int y, unsigned long ms);
39 static void ReleaseZoom(FXEngine *fxe, Drauing *drawing,
40 int b,int md,int x,int y, unsigned long ms);
41
42 static void redraw_seps(FXEngine *fxe);
43 static void check_clipping(FXEngine *fxe);
44 static void RedrawButton(FXEngine *fxe);
45 static void EnterButton(FXEngine *fxe);
46 static void LeaveButton(FXEngine *fxe);
47 static void PressButton(FXEngine *fxe,
48 int b,int md,int x,int y, unsigned long ms);
49 static void ReleaseButton(FXEngine *fxe, Drauing *drawing,
50 int b,int md,int x,int y, unsigned long ms);
51
52 static void ButtonAction(FXEngine *fxe, Drauing *drawing);
53 static void HighlightButton(FXEngine *fxe);
54 static void UnHighlightButton(FXEngine *fxe);
55
56 static void ResetZoom(FXEngine *fxe);
57 static void DoZoom(GpReal factor, GpReal w0, GpReal w1,
58 GpReal *wmin, GpReal *wmax);
59 static void AltZoom(GpReal w0, GpReal w1, GpReal *wmin, GpReal *wmax);
60 static int FindSystem(FXEngine *fxe, Drauing *drawing, int x, int y,
61 GeSystem **system, GpReal *xr, GpReal *yr);
62 static void Find1System(FXEngine *fxe, Drauing *drawing, int iSystem,
63 int x, int y, GeSystem **system,
64 GpReal *xr, GpReal *yr);
65 static GeSystem *GetSystemN(Drauing *drawing, int n);
66 static int FindAxis(GeSystem *system, GpReal x, GpReal y);
67 static void FindCoordinates(GeSystem *system, GpReal xNDC, GpReal yNDC,
68 GpReal *xWC, GpReal *yWC);
69 static GpReal GetFormat(char *format, GpReal w,
70 GpReal wmin, GpReal wmax, int isLog);
71 static void DrawRubber(FXEngine *fxe, int x, int y);
72
73 /* ------------------------------------------------------------------------ */
74
75 Engine *
GpFXEngine(char * name,int landscape,int dpi,char * displayName)76 GpFXEngine(char *name, int landscape, int dpi, char *displayName)
77 {
78 p_scr *s = g_connect(displayName);
79 int topWidth= DefaultTopWidth(dpi); /* not including button, message */
80 int topHeight= DefaultTopHeight(dpi);
81 GpTransform toPixels;
82 int x, y;
83 FXEngine *fxe;
84 int heightButton, widthButton, baseline;
85 int width, height, ascent, descent, hints;
86
87 if (!s) return 0;
88 descent = p_txheight(s, P_GUI_FONT, 15, &ascent);
89 descent -= ascent;
90 width = p_txwidth(s, "System", 6, P_GUI_FONT, 15);
91 baseline = ascent+2;
92 heightButton = baseline+descent+4; /* leave at least 2 lines
93 * above and below text */
94 widthButton = width+8; /* leave at least 4 pixels before and after */
95
96 /* set toPixels as by SetXTransform(&toPixels, landscape, dpi) */
97 toPixels.viewport = landscape? gLandscape : gPortrait;
98 toPixels.window.xmin = 0.0;
99 toPixels.window.xmax = PixelsPerNDC(dpi)*toPixels.viewport.xmax;
100 toPixels.window.ymin = PixelsPerNDC(dpi)*toPixels.viewport.ymax;
101 toPixels.window.ymax = 0.0;
102 width = (int)toPixels.window.xmax;
103 height = (int)toPixels.window.ymin;
104 x = (width-topWidth)/2;
105 if (landscape) y = (height-topHeight)/2;
106 else y = (width-topHeight)/2;
107 if (y<0) y = 0;
108 if (x<0) x = 0;
109 fxe = (FXEngine *)GxEngine(s, name, &toPixels, -x,-y,heightButton+2,0,
110 sizeof(FXEngine));
111
112 fxe->xe.wtop = topWidth;
113 fxe->xe.htop = topHeight;
114 /* possibly want optional P_RGBMODEL as well */
115 hints = (gist_private_map?P_PRIVMAP:0) | (gist_input_hint?0:P_NOKEY) |
116 (gist_rgb_hint?P_RGBMODEL:0);
117 fxe->xe.win = fxe->xe.w = gx_parent?
118 p_subwindow(s, topWidth, topHeight+heightButton+2,
119 gx_parent, gx_xloc, gx_yloc, P_BG, hints, fxe) :
120 p_window(s, topWidth, topHeight+heightButton+2, name, P_BG, hints, fxe);
121 gx_parent = 0;
122 if (!fxe->xe.win) {
123 GpDelEngine(&fxe->xe.e);
124 return 0;
125 }
126
127 /* set the extra information in the fxe structure */
128 fxe->baseline = baseline;
129 fxe->heightButton = heightButton;
130 fxe->widthButton = widthButton;
131
132 fxe->xmv = fxe->ymv = -1;
133 fxe->pressed = 0;
134 fxe->buttonState = 0;
135 fxe->iSystem = -1;
136 strcpy(fxe->msgText, "Press 1, 2, 3 to zoom in, pan, zoom out");
137 fxe->msglen = 0;
138 fxe->zoomState = fxe->zoomSystem = fxe->zoomAxis = 0;
139 fxe->zoomX = fxe->zoomY = 0.0;
140
141 /* set the fancy event handlers */
142 GxInput((Engine *)fxe, &HandleExpose, &HandleClick, &HandleMotion,
143 &HandleKey);
144
145 return (Engine *)fxe;
146 }
147
148 /* ------------------------------------------------------------------------ */
149
150 static void
HandleExpose(Engine * engine,Drauing * drawing,int * xy)151 HandleExpose(Engine *engine, Drauing *drawing, int *xy)
152 {
153 FXEngine *fxe = (FXEngine *)engine;
154 redraw_seps(fxe);
155 RedrawButton(fxe);
156 RedrawMessage(fxe);
157 if (!xy || xy[3]>=fxe->xe.topMargin)
158 GxExpose(engine, drawing, xy);
159 }
160
161 static int xf_region(FXEngine *fxe, int x, int y);
162
163 static void
HandleClick(Engine * e,int b,int md,int x,int y,unsigned long ms)164 HandleClick(Engine *e,int b,int md,int x,int y, unsigned long ms)
165 {
166 FXEngine *fxe = (FXEngine *)e;
167 if (x!=fxe->xmv || y!=fxe->ymv)
168 HandleMotion(e, md, x, y);
169 if (md & (1<<(b+2))) { /* this is button release */
170 if (fxe->pressed==1)
171 ReleaseButton(fxe, fxe->xe.e.drawing, b, md, x, y, ms);
172 else if (fxe->pressed==2)
173 ReleaseZoom(fxe, fxe->xe.e.drawing, b, md, x, y, ms);
174
175 } else { /* this is button press */
176 int region = xf_region(fxe, x, y);
177 if (region == 3)
178 PressZoom(fxe, fxe->xe.e.drawing, b, md, x, y, ms);
179 else if (region == 1)
180 PressButton(fxe, b, md, x, y, ms);
181 }
182 }
183
184 static void
HandleMotion(Engine * e,int md,int x,int y)185 HandleMotion(Engine *e,int md,int x,int y)
186 {
187 FXEngine *fxe = (FXEngine *)e;
188 int region = xf_region(fxe, x, y);
189 int old_region = xf_region(fxe, fxe->xmv, fxe->ymv);
190 if (!fxe->pressed) {
191 if (region != old_region) {
192 if (old_region==3) p_cursor(fxe->xe.win, P_SELECT);
193 else if (region==3) p_cursor(fxe->xe.win, P_CROSSHAIR);
194 }
195 if (region==1) {
196 if (old_region!=1) EnterButton(fxe);
197 } else {
198 if (old_region==1) LeaveButton(fxe);
199 }
200 }
201 if (fxe->pressed==1) {
202 if (region!=1 && old_region==1)
203 LeaveButton(fxe);
204 } else if (region==3 || fxe->pressed==2) {
205 MovePointer(fxe, fxe->xe.e.drawing, md, x, y);
206 }
207 fxe->xmv = x;
208 fxe->ymv = y;
209 }
210
211 static int
xf_region(FXEngine * fxe,int x,int y)212 xf_region(FXEngine *fxe, int x, int y)
213 {
214 if (y>=0 && x>=0 && x<fxe->xe.leftMargin+fxe->xe.wtop) {
215 y -= fxe->xe.topMargin;
216 if (y < 0)
217 return (x < fxe->widthButton)? 1 : 2;
218 else if (y < fxe->xe.htop)
219 return 3;
220 }
221 return 0;
222 }
223
224 static void
HandleKey(Engine * e,int k,int md)225 HandleKey(Engine *e,int k,int md)
226 {
227 FXEngine *fxe = (FXEngine *)e;
228 if (!fxe->pressed && !fxe->buttonState) {
229 if (!fxe->msglen) fxe->msgText[0] = '\0';
230 if (k>=' ' && k<'\177') {
231 /* append printing characters */
232 if (fxe->msglen>=94) fxe->msglen = 0; /* crude overflow handling */
233 fxe->msgText[fxe->msglen++] = k;
234 fxe->msgText[fxe->msglen] = '\0';
235 } else if (k=='\177' || k=='\010') {
236 /* delete or backspace kills char */
237 if (fxe->msglen)
238 fxe->msgText[--fxe->msglen] = '\0';
239 } else if (k=='\027') {
240 /* C-w kills word */
241 int n = fxe->msglen;
242 char c = n? fxe->msgText[n-1] : '\0';
243 if (c<'0' || (c>'9' && c<'A') || (c>'Z' && c<'a' && c!='_') || c>'z') {
244 if (fxe->msglen)
245 fxe->msgText[--fxe->msglen] = '\0';
246 } else {
247 while (--n) {
248 c = fxe->msgText[n-1];
249 if (c<'0' || (c>'9' && c<'A') || (c>'Z' && c<'a' && c!='_') ||
250 c>'z') break;
251 }
252 fxe->msgText[n] = '\0';
253 fxe->msglen = n;
254 }
255 } else if (k=='\025') {
256 /* C-u kills line */
257 fxe->msgText[0] = '\0';
258 fxe->msglen = 0;
259 } else if (k=='\012' || k=='\015') {
260 /* linefeed or carriage return sends line to interpreter */
261 int n = fxe->msglen;
262 fxe->msgText[n] = '\0';
263 if (g_on_keyline) g_on_keyline(fxe->msgText);
264 fxe->msgText[0] = '\0';
265 fxe->msglen = 0;
266 }
267 RedrawMessage(fxe);
268 }
269 }
270
271 /* ------------------------------------------------------------------------ */
272
273 static void
check_clipping(FXEngine * fxe)274 check_clipping(FXEngine *fxe)
275 {
276 p_win *w = fxe->xe.win;
277 if (w) {
278 if (fxe->xe.clipping) {
279 p_clip(w, 0, 0, 0, 0);
280 fxe->xe.clipping = 0;
281 }
282 }
283 }
284
285 static void
redraw_seps(FXEngine * fxe)286 redraw_seps(FXEngine *fxe)
287 {
288 p_win *w = fxe->xe.win;
289 if (w) {
290 int xpage = (int)fxe->xe.swapped.window.xmax;
291 int ypage = (int)fxe->xe.swapped.window.ymin;
292 int x[4], y[4];
293 check_clipping(fxe);
294 p_color(w, P_FG);
295 if (fxe->xe.leftMargin+fxe->xe.wtop > xpage) {
296 p_pen(w, 4, P_SOLID);
297 x[0] = x[1] = xpage+2;
298 y[0] = 0; y[1] = ypage+2;
299 p_i_pnts(w, x, y, 2);
300 p_segments(w);
301 }
302 if (fxe->xe.topMargin+fxe->xe.htop > ypage) {
303 p_pen(w, 4, P_SOLID);
304 x[0] = 0; x[1] = xpage+2;
305 y[0] = y[1] = ypage+2;
306 p_i_pnts(w, x, y, 2);
307 p_segments(w);
308 }
309 p_pen(w, 1, P_SOLID);
310 x[0] = 0; x[1] = fxe->xe.wtop;
311 y[0] = y[1] = fxe->xe.topMargin-1;
312 x[2] = x[3] = fxe->widthButton;
313 y[2] = 0; y[3] = fxe->xe.topMargin-1;
314 p_i_pnts(w, x, y, 4);
315 p_segments(w);
316 }
317 }
318
319 static void
RedrawButton(FXEngine * fxe)320 RedrawButton(FXEngine *fxe)
321 {
322 p_win *w = fxe->xe.win;
323 if (w) {
324 int fg = (fxe->buttonState==2)? P_BG : P_FG;
325 int bg = (fxe->buttonState==2)? P_FG : P_BG;
326 check_clipping(fxe);
327 p_color(w, bg);
328 p_rect(w, 0, 0, fxe->widthButton, fxe->xe.topMargin-1, 0);
329 if (fxe->buttonState) HighlightButton(fxe);
330 else p_color(w, fg);
331 p_font(w, P_GUI_FONT, 15, 0);
332 p_text(w, 3, fxe->baseline, "System", 6);
333 }
334 }
335
336 static void
HighlightButton(FXEngine * fxe)337 HighlightButton(FXEngine *fxe)
338 {
339 p_win *w = fxe->xe.win;
340 if (w && fxe->buttonState) {
341 check_clipping(fxe);
342 p_color(w, (fxe->buttonState==2)? P_BG : P_FG);
343 p_pen(w, 3, P_SOLID);
344 p_rect(w, 1, 1, fxe->widthButton-2, fxe->xe.topMargin-3, 1);
345 }
346 }
347
348 static void
UnHighlightButton(FXEngine * fxe)349 UnHighlightButton(FXEngine *fxe)
350 {
351 p_win *w = fxe->xe.win;
352 if (w) {
353 check_clipping(fxe);
354 p_color(w, (fxe->buttonState==2)? P_FG : P_BG);
355 p_pen(w, 3, P_SOLID);
356 p_rect(w, 1, 1, fxe->widthButton-2, fxe->xe.topMargin-3, 1);
357 }
358 }
359
360 static void
EnterButton(FXEngine * fxe)361 EnterButton(FXEngine *fxe)
362 {
363 if (fxe->buttonState == 0) {
364 fxe->buttonState = 1;
365 HighlightButton(fxe);
366 } else if (fxe->buttonState != 0) {
367 fxe->buttonState = 0;
368 RedrawButton(fxe);
369 }
370 }
371
372 static void
LeaveButton(FXEngine * fxe)373 LeaveButton(FXEngine *fxe)
374 {
375 int state = fxe->buttonState;
376 fxe->buttonState = fxe->pressed = 0;
377 if (state==1) UnHighlightButton(fxe);
378 else if (state) RedrawButton(fxe);
379 }
380
381 static void
PressButton(FXEngine * fxe,int b,int md,int x,int y,unsigned long ms)382 PressButton(FXEngine *fxe,int b,int md,int x,int y, unsigned long ms)
383 {
384 if (!(md&0370) && fxe->buttonState==1) {
385 fxe->buttonState = 2;
386 fxe->pressed = 1;
387 RedrawButton(fxe);
388 } else {
389 LeaveButton(fxe);
390 }
391 }
392
393 static void
ReleaseButton(FXEngine * fxe,Drauing * drawing,int b,int md,int x,int y,unsigned long ms)394 ReleaseButton(FXEngine *fxe, Drauing *drawing,
395 int b,int md,int x,int y, unsigned long ms)
396 {
397 if (fxe->buttonState==2) {
398 fxe->buttonState = 1;
399 fxe->pressed = 0;
400 RedrawButton(fxe);
401 ButtonAction(fxe, drawing);
402 } else {
403 LeaveButton(fxe);
404 }
405 }
406
407 /* ------------------------------------------------------------------------ */
408
ButtonAction(FXEngine * fxe,Drauing * drawing)409 static void ButtonAction(FXEngine *fxe, Drauing *drawing)
410 {
411 int nSystems= drawing? drawing->nSystems : 0;
412 int iSystem= fxe->iSystem;
413 iSystem++;
414 if (iSystem<=nSystems) fxe->iSystem= iSystem;
415 else fxe->iSystem= iSystem= -1;
416 sprintf(fxe->msgText, "%s%2d", iSystem>=0?"=":":",
417 iSystem>=0 ? iSystem : 0);
418 RedrawMessage(fxe);
419 }
420
421 /* ------------------------------------------------------------------------ */
422
RedrawMessage(FXEngine * fxe)423 static void RedrawMessage(FXEngine *fxe)
424 {
425 p_win *w = fxe->xe.win;
426 if (w) {
427 char *msg= fxe->msgText;
428 int len= (int)strlen(msg);
429 check_clipping(fxe);
430 /* NOTE: may need to animate this if flickering is annoying */
431 p_color(w, P_BG);
432 p_rect(w, fxe->widthButton+1, 0, fxe->xe.wtop, fxe->xe.topMargin-2, 0);
433 p_color(w, P_FG);
434 p_font(w, P_GUI_FONT, 15, 0);
435 p_text(w, fxe->widthButton+4, fxe->baseline, msg, len);
436 }
437 }
438
439 static char stdFormat[] = "%7.4f";
440 static int rubberBanding = 0;
441 static int anchorX, anchorY, oldX, oldY;
442
443
444 /* Variables to store coordinate system and mouse coordinates after
445 last mouse motion. */
446 int gxCurrentSys = -1;
447 GpReal gxCurrentX = 0, gxCurrentY = 0;
448
449 static void
MovePointer(FXEngine * fxe,Drauing * drawing,int md,int x,int y)450 MovePointer(FXEngine *fxe, Drauing *drawing,
451 int md,int x,int y)
452 {
453 int iSystem = fxe->iSystem;
454 int locked, logX = 0, logY = 0;
455 char format[24]; /* e.g.- "%s%2d (%11.3e, %11.3e)" */
456 char xFormat[16], yFormat[16], *f1, *f2;
457 GpReal xWC, yWC;
458 GeSystem *system;
459
460 if (!drawing || fxe->buttonState) return;
461
462 /* find the system number and world coordinates */
463 if (iSystem >= 0) {
464 /* coordinate system is locked */
465 Find1System(fxe, drawing, iSystem, x, y, &system, &xWC, &yWC);
466 if (!system) iSystem = 0;
467 locked = 1;
468 } else {
469 /* select coordinate system under pointer */
470 iSystem = FindSystem(fxe, drawing, x, y, &system, &xWC, &yWC);
471 locked = 0;
472 }
473
474 if (!fxe->msglen) {
475 if (system) {
476 logX = system->flags&D_LOGX;
477 logY = system->flags&D_LOGY;
478 xWC = GetFormat(xFormat, xWC, system->trans.window.xmin,
479 system->trans.window.xmax, logX);
480 f1 = xFormat;
481 yWC = GetFormat(yFormat, yWC, system->trans.window.ymin,
482 system->trans.window.ymax, logY);
483 f2 = yFormat;
484 } else {
485 f1 = f2 = stdFormat;
486 }
487 sprintf(format, "%%s%%2d (%s, %s)", f1, f2);
488 sprintf(fxe->msgText, format, locked? "=" : ":", iSystem, xWC, yWC);
489
490 /* Save mouse coordinates. */
491 gxCurrentX = xWC;
492 gxCurrentY = yWC;
493 gxCurrentSys = iSystem;
494 gxCurrentEngine = (Engine *)fxe;
495
496 RedrawMessage(fxe);
497 }
498 if (rubberBanding) DrawRubber(fxe, x, y);
499 }
500
501 GpReal gxZoomFactor= 1.5;
502
503 static int (*PtClCallBack)(Engine *engine, int system,
504 int release, GpReal x, GpReal y,
505 int butmod, GpReal xn, GpReal yn) = 0;
506 static int ptClStyle = 0, ptClSystem = 0, ptClCount = 0;
507
508 static unsigned int cShapes[3] = { P_EW, P_NS, P_NSEW };
509
510 static void
PressZoom(FXEngine * fxe,Drauing * drawing,int b,int md,int x,int y,unsigned long ms)511 PressZoom(FXEngine *fxe, Drauing *drawing,
512 int b,int md,int x,int y, unsigned long ms)
513 {
514 if (!drawing || !fxe->xe.win) return;
515 if (fxe->zoomState == 0 && fxe->pressed == 0) {
516 /* record button number as zoomState */
517 if ((b==1)? (md&P_SHIFT) : (b==2)) fxe->zoomState = 2;
518 else if ((b==1)? (md&P_META) : (b==3)) fxe->zoomState = 3;
519 else if (b==1) fxe->zoomState = 1;
520 if (fxe->zoomState) {
521 if (PtClCallBack)
522 fxe->zoomState |= 8;
523 else if (md&P_CONTROL)
524 fxe->zoomState |= 4;
525 }
526 if (fxe->zoomState) {
527 /* record system and axis, x and y, change cursor */
528 GeSystem *system;
529 int iSystem, axis;
530 if (!PtClCallBack || ptClSystem<0) {
531 iSystem = FindSystem(fxe, drawing, x, y,
532 &system, &fxe->zoomX, &fxe->zoomY);
533 axis = FindAxis(system, fxe->zoomX, fxe->zoomY);
534 } else {
535 iSystem = ptClSystem;
536 Find1System(fxe, drawing, iSystem, x, y,
537 &system, &fxe->zoomX, &fxe->zoomY);
538 if (!system) iSystem = ptClSystem = 0;
539 axis = 3;
540 }
541 fxe->zoomSystem = iSystem;
542
543 fxe->pressed = 2;
544
545 if (PtClCallBack) {
546 GpXYMap *map = &fxe->xe.e.map; /* NDC->VDC (x,y) mapping */
547 GpReal xNDC = ((GpReal)x - map->x.offset)/map->x.scale;
548 GpReal yNDC = ((GpReal)y - map->y.offset)/map->y.scale;
549 int button = fxe->zoomState&3;
550 ptClCount--;
551 if (PtClCallBack(&fxe->xe.e, iSystem, 0,
552 fxe->zoomX, fxe->zoomY,
553 button, xNDC, yNDC)) {
554 /* callback routine signals abort */
555 ResetZoom(fxe);
556 return;
557 }
558 }
559
560 if ((fxe->zoomAxis = axis)) {
561 if (fxe->zoomState<4) {
562 p_cursor(fxe->xe.win, cShapes[axis-1]);
563 } else if (!PtClCallBack || ptClStyle) {
564 fxe->zoomAxis = 3;
565 p_cursor(fxe->xe.win, P_SELECT);
566 anchorX = oldX = x;
567 anchorY = oldY = y;
568 rubberBanding = PtClCallBack? ptClStyle : 1;
569 DrawRubber(fxe, anchorX, anchorY);
570 }
571 } else if (!PtClCallBack) {
572 /* no such thing as zoom or pan outside of all systems */
573 fxe->zoomState = 0;
574 }
575 }
576
577 } else {
578 /* abort if 2nd button pressed */
579 fxe->pressed = 0;
580 ResetZoom(fxe);
581 }
582 }
583
584 static void
ReleaseZoom(FXEngine * fxe,Drauing * drawing,int b,int md,int ix,int iy,unsigned long ms)585 ReleaseZoom(FXEngine *fxe, Drauing *drawing,
586 int b,int md,int ix,int iy, unsigned long ms)
587 {
588 int zoomState = fxe->zoomState;
589 if (zoomState) {
590 /* perform the indicated zoom operation */
591 int iSystem = fxe->zoomSystem;
592 GeSystem *system = GetSystemN(drawing, iSystem);
593 GpXYMap *map = &fxe->xe.e.map; /* NDC->VDC (x,y) mapping */
594 GpReal x, xNDC = ((GpReal)ix - map->x.offset)/map->x.scale;
595 GpReal y, yNDC = ((GpReal)iy - map->y.offset)/map->y.scale;
596
597 /* get current pointer position (in world coordinates) */
598 if (system &&
599 /* be sure system has been scanned if limits extreme */
600 (!(system->rescan || system->unscanned>=0) || !GdScan(system))) {
601 FindCoordinates(system, xNDC, yNDC, &x, &y);
602 } else {
603 x = xNDC;
604 y = yNDC;
605 iSystem = ptClSystem = 0;
606 }
607
608 if (!PtClCallBack) {
609 int axis = fxe->zoomAxis;
610 GpReal factor = 1.0;
611
612 if (!iSystem) {
613 fxe->pressed = 0;
614 ResetZoom(fxe);
615 return;
616 }
617
618 if (zoomState==1) factor = 1.0/gxZoomFactor;
619 else if (zoomState==3) factor = gxZoomFactor;
620
621 /* the redraw triggered here can mess up a rubber band box/line */
622 if (rubberBanding) {
623 DrawRubber(fxe, anchorX, anchorY);
624 rubberBanding = 0;
625 }
626
627 /* switch to current drawing and engine temporarily, then save
628 limits if this is first mouse-driven zoom */
629 GdSetDrawing(drawing);
630 GpPreempt((Engine *)fxe);
631 if (!(system->flags&D_ZOOMED)) GdSaveLimits(0);
632
633 if (zoomState<4) {
634 if (axis&1) DoZoom(factor, fxe->zoomX, x,
635 &system->trans.window.xmin,
636 &system->trans.window.xmax);
637 if (axis&2) DoZoom(factor, fxe->zoomY, y,
638 &system->trans.window.ymin,
639 &system->trans.window.ymax);
640 } else {
641 if (axis&1) AltZoom(fxe->zoomX, x, &system->trans.window.xmin,
642 &system->trans.window.xmax);
643 if (axis&2) AltZoom(fxe->zoomY, y, &system->trans.window.ymin,
644 &system->trans.window.ymax);
645 }
646 system->flags&= ~(D_XMIN | D_XMAX | D_YMIN | D_YMAX);
647 system->flags|= D_ZOOMED;
648 system->rescan= 1;
649
650 /* The redraw must be done immediately -- nothing else triggers it. */
651 GdDraw(1);
652 GpPreempt(0); /* not correct if zoomed during a preempt... */
653 GdSetDrawing(0);
654
655 } else {
656 int modifier = 0;
657 if (md&P_SHIFT) modifier|= 1;
658 /* else if (state&LockMask) modifier|= 2; */
659 else if (md&P_CONTROL) modifier|= 4;
660 else if (md&P_META) modifier|= 8;
661 else if (md&P_ALT) modifier|= 16;
662 else if (md&P_COMPOSE) modifier|= 32;
663 else if (md&P_KEYPAD) modifier|= 64;
664 /* else if (state&Mod5Mask) modifier|= 128; */
665 ptClCount--;
666 PtClCallBack(&fxe->xe.e, iSystem, 1, x, y, modifier, xNDC, yNDC);
667 }
668
669 /* free the zoom/pan cursor and reset zoomState */
670 fxe->pressed = 0;
671 ResetZoom(fxe);
672 } else if (fxe->pressed==2) {
673 fxe->pressed = 0;
674 }
675 }
676
677 static void
ResetZoom(FXEngine * fxe)678 ResetZoom(FXEngine *fxe)
679 {
680 int (*cback)(Engine *engine, int system,
681 int release, GpReal x, GpReal y,
682 int butmod, GpReal xn, GpReal yn) = PtClCallBack;
683 /* free the zoom cursor and reset the zoom state */
684 if (rubberBanding) {
685 DrawRubber(fxe, anchorX, anchorY);
686 rubberBanding = 0;
687 }
688 if (fxe->zoomState && fxe->xe.win)
689 p_cursor(fxe->xe.win, P_CROSSHAIR);
690 fxe->zoomState = 0;
691 PtClCallBack = 0;
692 if (cback) cback(0, -1, -1, 0., 0., -1, 0., 0.);
693 }
694
DoZoom(GpReal factor,GpReal w0,GpReal w1,GpReal * wmin,GpReal * wmax)695 static void DoZoom(GpReal factor, GpReal w0, GpReal w1,
696 GpReal *wmin, GpReal *wmax)
697 {
698 GpReal wn= *wmin;
699 GpReal wx= *wmax;
700 /* With origin at the release point, expand the scale, then put this origin
701 at the press point. This is a translation (pan) if factor==1.0. */
702 *wmin= w0-factor*(w1-wn);
703 *wmax= w0+factor*(wx-w1);
704 }
705
AltZoom(GpReal w0,GpReal w1,GpReal * wmin,GpReal * wmax)706 static void AltZoom(GpReal w0, GpReal w1, GpReal *wmin, GpReal *wmax)
707 {
708 GpReal wn= *wmin;
709 GpReal wx= *wmax;
710 /* Expand marked interval to full scale. */
711 if (wn<wx) {
712 if (w0<w1) { *wmin= w0; *wmax= w1; }
713 else if (w0>w1) { *wmin= w1; *wmax= w0; }
714 else {
715 wx= wx-wn;
716 if (!wx) { if (wn) wx=0.0001*wn; else wx= 1.e-6; }
717 else wx*= 0.01;
718 *wmin= w0-wx; *wmax= w1+wx;
719 }
720 } else {
721 if (w0<w1) { *wmin= w1; *wmax= w0; }
722 else if (w0>w1) { *wmin= w0; *wmax= w1; }
723 else {
724 wx= wn-wx;
725 if (!wx) { if (wn) wx=0.0001*wn; else wx= 1.e-6; }
726 else wx*= 0.01;
727 *wmin= w0+wx; *wmax= w1-wx;
728 }
729 }
730 }
731
FindSystem(FXEngine * fxe,Drauing * drawing,int x,int y,GeSystem ** system,GpReal * xr,GpReal * yr)732 static int FindSystem(FXEngine *fxe, Drauing *drawing, int x, int y,
733 GeSystem **system, GpReal *xr, GpReal *yr)
734 {
735 GeSystem *sys= drawing->systems, *thesys=sys;
736 int nSystems= drawing->nSystems;
737 GpXYMap *map= &fxe->xe.e.map; /* NDC->VDC (x,y) mapping */
738 GpReal xn, yn;
739 GpBox *box;
740 int i, iSystem=0;
741 GpReal min=9., tmp; /* assume all viewports have area<9 */
742 if (fxe->xe.w != fxe->xe.win) { /* adjust for animation mode margins */
743 x -= fxe->xe.a_x;
744 y -= fxe->xe.a_y;
745 }
746 xn = ((GpReal)x - map->x.offset)/map->x.scale;
747 yn = ((GpReal)y - map->y.offset)/map->y.scale;
748 for (i=nSystems ; i>0 ; i--) {
749 sys= (GeSystem *)sys->el.prev;
750 if (!sys->elements ||
751 /* be sure system has been scanned if limits extreme */
752 ((sys->rescan || sys->unscanned>=0) &&
753 GdScan(sys))) continue;
754 box= &sys->trans.viewport;
755 if (xn>=box->xmin && xn<=box->xmax && yn>=box->ymin && yn<=box->ymax)
756 {
757 tmp= (box->xmax-box->xmin)*(box->ymax-box->ymin);
758 if(tmp<0) tmp= -tmp;
759 if(tmp<min) {
760 min= tmp;
761 iSystem= i;
762 thesys= sys;
763 }
764 }
765 }
766 if (!iSystem) { /* look for nearest axis */
767 min= 9.; /* now assume mouse is closer to an axis than 9 units */
768 for (i=nSystems ; i>0 ; i--) {
769 sys= (GeSystem *)sys->el.prev;
770 if (!sys->elements) continue;
771 box= &sys->trans.viewport;
772 if (yn>=box->ymin && yn<=box->ymax) {
773 tmp= xn-box->xmax;
774 if(tmp<min && tmp>0) { min= tmp; iSystem= i; thesys= sys; }
775 tmp= box->xmin-xn;
776 if(tmp<min && tmp>0) { min= tmp; iSystem= i; thesys= sys; }
777 }
778 if (xn>=box->xmin && xn<=box->xmax) {
779 tmp= yn-box->ymax;
780 if(tmp<min && tmp>0) { min= tmp; iSystem= i; thesys= sys; }
781 tmp= box->ymin-yn;
782 if(tmp<min && tmp>0) { min= tmp; iSystem= i; thesys= sys; }
783 }
784 }
785 }
786 if (iSystem) {
787 sys= thesys;
788 *system= sys;
789 FindCoordinates(sys, xn, yn, xr, yr);
790 } else {
791 *system= 0;
792 *xr= xn; *yr= yn;
793 }
794 return iSystem;
795 }
796
Find1System(FXEngine * fxe,Drauing * drawing,int iSystem,int x,int y,GeSystem ** system,GpReal * xr,GpReal * yr)797 static void Find1System(FXEngine *fxe, Drauing *drawing, int iSystem,
798 int x, int y, GeSystem **system,
799 GpReal *xr, GpReal *yr)
800 {
801 GpXYMap *map= &fxe->xe.e.map; /* NDC->VDC (x,y) mapping */
802 GpReal xn, yn;
803 GeSystem *sys= GetSystemN(drawing, iSystem);
804 if (fxe->xe.w != fxe->xe.win) { /* adjust for animation mode margins */
805 x -= fxe->xe.a_x;
806 y -= fxe->xe.a_y;
807 }
808 xn = ((GpReal)x - map->x.offset)/map->x.scale;
809 yn = ((GpReal)y - map->y.offset)/map->y.scale;
810 if (sys && (!(sys->rescan || sys->unscanned>=0) ||
811 !GdScan(sys))) {
812 FindCoordinates(sys, xn, yn, xr, yr);
813 *system= sys;
814 } else {
815 *xr= xn;
816 *yr= yn;
817 *system= 0;
818 }
819 }
820
GetSystemN(Drauing * drawing,int n)821 static GeSystem *GetSystemN(Drauing *drawing, int n)
822 {
823 if (n<=0 || n>drawing->nSystems) return 0;
824 else {
825 GeSystem *sys= drawing->systems;
826 while (--n) sys= (GeSystem *)sys->el.next;
827 return (sys && sys->elements)? sys : 0;
828 }
829 }
830
FindAxis(GeSystem * system,GpReal x,GpReal y)831 static int FindAxis(GeSystem *system, GpReal x, GpReal y)
832 {
833 if (system) {
834 int xout= (x==system->trans.window.xmin || x==system->trans.window.xmax);
835 int yout= (y==system->trans.window.ymin || y==system->trans.window.ymax);
836 if (xout) return yout? 3 : 2;
837 else return yout? 1 : 3;
838 }
839 return 0;
840 }
841
FindCoordinates(GeSystem * system,GpReal xNDC,GpReal yNDC,GpReal * xWC,GpReal * yWC)842 static void FindCoordinates(GeSystem *system, GpReal xNDC, GpReal yNDC,
843 GpReal *xWC, GpReal *yWC)
844 {
845 GpReal x, y;
846 GpXYMap map;
847 GpSetMap(&system->trans.viewport, &system->trans.window, &map);
848 x= map.x.scale*xNDC + map.x.offset;
849 y= map.y.scale*yNDC + map.y.offset;
850 if (system->trans.window.xmin<system->trans.window.xmax) {
851 if (x<system->trans.window.xmin) x= system->trans.window.xmin;
852 else if (x>system->trans.window.xmax) x= system->trans.window.xmax;
853 } else {
854 if (x<system->trans.window.xmax) x= system->trans.window.xmax;
855 else if (x>system->trans.window.xmin) x= system->trans.window.xmin;
856 }
857 if (system->trans.window.ymin<system->trans.window.ymax) {
858 if (y<system->trans.window.ymin) y= system->trans.window.ymin;
859 else if (y>system->trans.window.ymax) y= system->trans.window.ymax;
860 } else {
861 if (y<system->trans.window.ymax) y= system->trans.window.ymax;
862 else if (y>system->trans.window.ymin) y= system->trans.window.ymin;
863 }
864 *xWC= x;
865 *yWC= y;
866 }
867
GetFormat(char * format,GpReal w,GpReal wmin,GpReal wmax,int isLog)868 static GpReal GetFormat(char *format, GpReal w,
869 GpReal wmin, GpReal wmax, int isLog)
870 {
871 GpReal delta;
872
873 if (isLog) {
874 w= exp10(w);
875 wmin= exp10(wmin);
876 wmax= exp10(wmax);
877 }
878 delta= wmax-wmin;
879 if (delta<0.0) delta= -delta;
880 if (wmin<0.0) wmin= -wmin;
881 if (wmax<0.0) wmax= -wmax;
882 if (wmax<wmin) {
883 wmax= wmin;
884 wmin-= delta;
885 }
886 if (!isLog) wmin= wmax; /* only for deciding between e and f format */
887
888 if (wmax>=10000.0 || wmin<0.001) {
889 /* use e format */
890 int n;
891 if (delta>0.0) n= 3+(int)log10(wmax/delta + 0.50001);
892 else n= 3;
893 sprintf(format, "%%%d.%de", 8+n, n);
894 } else {
895 /* use f format */
896 int pre, post;
897 if (wmax>1000.0) pre= 4;
898 else if (wmax>100.0) pre= 3;
899 else if (wmax>10.0) pre= 2;
900 else pre= 1;
901 if (delta<0.1 && delta>0.0) post= 3-(int)log10(delta);
902 else if (isLog && wmin<0.01) post= 5;
903 else post= 4;
904 sprintf(format, "%%%d.%df", pre+post+3, post);
905 }
906
907 return w;
908 }
909
910 /* ------------------------------------------------------------------------ */
911
912 static void
DrawRubber(FXEngine * fxe,int x,int y)913 DrawRubber(FXEngine *fxe, int x, int y)
914 {
915 p_win *w = fxe->xe.win;
916 int iPass= 2;
917
918 if (!w) return;
919
920 p_color(w, P_XOR);
921 p_pen(w, 1, P_SOLID);
922
923 /* first undraw previous box or line, then draw new box or line */
924 while (iPass--) {
925 if (anchorX!=oldX || anchorY!=oldY) {
926 if (rubberBanding==1) {
927 /* this is a rubber box */
928 p_rect(w, anchorX, anchorY, oldX, oldY, 1);
929 } else {
930 /* this is a rubber line */
931 int x[2], y[2];
932 x[0] = anchorX; x[1] = oldX;
933 y[0] = anchorY; y[1] = oldY;
934 p_i_pnts(w, x, y, 2);
935 p_lines(w);
936 }
937 }
938 oldX = x;
939 oldY = y;
940 }
941 }
942
943 /* ------------------------------------------------------------------------ */
944
GxPointClick(Engine * engine,int style,int system,int (* CallBack)(Engine * engine,int system,int release,GpReal x,GpReal y,int butmod,GpReal xn,GpReal yn))945 int GxPointClick(Engine *engine, int style, int system,
946 int (*CallBack)(Engine *engine, int system,
947 int release, GpReal x, GpReal y,
948 int butmod, GpReal xn, GpReal yn))
949 {
950 XEngine *xeng= GisXEngine(engine);
951 if (!xeng || !xeng->w) return 1;
952
953 /* set up state variables for point-and-click sequence */
954 if (!(PtClCallBack= CallBack)) return 1;
955 if (style==1 || style==2) ptClStyle= style;
956 else ptClStyle= 0;
957 if (system<0) ptClSystem= -1;
958 else ptClSystem= system;
959 ptClCount= 2;
960
961 return 0;
962 }
963
964 /* ------------------------------------------------------------------------ */
965