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