Icons are changed
[gnuplot] / src / mouse.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("$Id: mouse.c,v 1.85.2.8 2009/06/05 00:32:52 sfeam Exp $"); }
3 #endif
4
5 /* GNUPLOT - mouse.c */
6
7 /* driver independent mouse part. */
8
9 /*[
10  * Permission to use, copy, and distribute this software and its
11  * documentation for any purpose with or without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and
13  * that both that copyright notice and this permission notice appear
14  * in supporting documentation.
15  *
16  * Permission to modify the software is granted, but not the right to
17  * distribute the complete modified source code.  Modifications are to
18  * be distributed as patches to the released version.  Permission to
19  * distribute binaries produced by compiling modified sources is granted,
20  * provided you
21  *   1. distribute the corresponding source modifications from the
22  *    released version in the form of a patch file along with the binaries,
23  *   2. add special version identification to distinguish your version
24  *    in addition to the base release version number,
25  *   3. provide your name and address as the primary contact for the
26  *    support of your modified version, and
27  *   4. retain our contact information in regard to use of the base
28  *    software.
29  * Permission to distribute the released version of the source code along
30  * with corresponding source modifications in the form of a patch file is
31  * granted with same provisions 2 through 4 for binary distributions.
32  *
33  * This software is provided "as is" without express or implied warranty
34  * to the extent permitted by applicable law.
35 ]*/
36
37 /*
38  * AUTHORS
39  *
40  *   Original Software (October 1999 - January 2000):
41  *     Pieter-Tjerk de Boer <ptdeboer@cs.utwente.nl>
42  *     Petr Mikulik <mikulik@physics.muni.cz>
43  *     Johannes Zellner <johannes@zellner.org>
44  */
45
46 #include "syscfg.h"
47 #include "stdfn.h"
48 #include "gp_types.h"
49
50 #define _MOUSE_C                /* FIXME HBB 20010207: violates Codestyle */
51 #ifdef USE_MOUSE                /* comment out whole file, otherwise... */
52
53 #include "mouse.h"
54 #include "pm3d.h"
55 #include "alloc.h"
56 #include "axis.h"
57 #include "command.h"
58 #include "datafile.h"
59 #include "gadgets.h"
60 #include "gp_time.h"
61 #include "graphics.h"
62 #include "graph3d.h"
63 #include "plot3d.h"
64 #include "readline.h"
65 #include "term_api.h"
66 #include "util3d.h"
67 #include "hidden3d.h"
68
69 #ifdef _Windows
70 # include "win/winmain.h"
71 #endif
72
73 #ifdef OS2
74 #include "os2/pm_msgs.h"
75 #endif
76
77 /********************** variables ***********************************************************/
78
79 mouse_setting_t mouse_setting = {
80 #ifdef OS2
81     0 /* don't start with mouse on default -- clashes with arrow keys on command line */,
82 #else
83     1 /* start with mouse on by default */,
84 #endif
85     300 /* ms */,
86     1, 0, 0, 0, 0,
87     "% #g",
88     "point pt 1"
89 };
90
91 /* "usual well-known" keycodes, i.e. those not listed in special_keys in mouse.h
92 */
93 static const struct gen_table usual_special_keys[] =
94 {
95     { "BackSpace", GP_BackSpace},
96     { "Tab", GP_Tab},
97     { "KP_Enter", GP_KP_Enter},
98     { "Return", GP_Return},
99     { "Escape", GP_Escape},
100     { "Delete", GP_Delete},
101     { NULL, 0}
102 };
103
104 /* the status of the shift, ctrl and alt keys
105 */
106 static int modifier_mask = 0;
107
108 /* Structure for the ruler: on/off, position,...
109  */
110 static struct {
111     TBOOLEAN on;
112     double x, y, x2, y2;        /* ruler position in real units of the graph */
113     long px, py;                /* ruler position in the viewport units */
114 } ruler = {
115     FALSE, 0, 0, 0, 0, 0, 0
116 };
117
118
119 /* the coordinates of the mouse cursor in gnuplot's internal coordinate system
120  */
121 static int mouse_x = -1, mouse_y = -1;
122
123
124 /* the "real" coordinates of the mouse cursor, i.e., in the user's coordinate
125  * system(s)
126  */
127 static double real_x, real_y, real_x2, real_y2;
128
129
130 /* mouse_polar_distance is set to TRUE if user wants to see the
131  * distance between the ruler and mouse pointer in polar coordinates
132  * too (otherwise, distance in cartesian coordinates only is shown) */
133 /* int mouse_polar_distance = FALSE; */
134 /* moved to the struct mouse_setting_t (joze) */
135
136
137 /* status of buttons; button i corresponds to bit (1<<i) of this variable
138  */
139 static int button = 0;
140
141
142 /* variables for setting the zoom region:
143  */
144 /* flag, TRUE while user is outlining the zoom region */
145 static TBOOLEAN setting_zoom_region = FALSE;
146 /* coordinates of the first corner of the zoom region, in the internal
147  * coordinate system */
148 static int setting_zoom_x, setting_zoom_y;
149
150
151 /* variables for changing the 3D view:
152 */
153 /* do we allow motion to result in a replot right now? */
154 TBOOLEAN allowmotion = TRUE;    /* used by pm.trm, too */
155 /* did we already postpone a replot because allowmotion was FALSE ? */
156 static TBOOLEAN needreplot = FALSE;
157 /* mouse position when dragging started */
158 static int start_x, start_y;
159 /* ButtonPress sets this to 0, ButtonMotion to 1 */
160 static int motion = 0;
161 /* values for rot_x and rot_z corresponding to zero position of mouse */
162 static float zero_rot_x, zero_rot_z;
163
164
165 /* bind related stuff */
166
167 typedef struct bind_t {
168     struct bind_t *prev;
169     int key;
170     char modifier;
171     char *command;
172     char *(*builtin) (struct gp_event_t * ge);
173     TBOOLEAN allwindows;
174     struct bind_t *next;
175 } bind_t;
176
177 static bind_t *bindings = (bind_t *) 0;
178 static const int NO_KEY = -1;
179 static TBOOLEAN trap_release = FALSE;
180
181 /* forward declarations */
182 static void alert __PROTO((void));
183 static void MousePosToGraphPosReal __PROTO((int xx, int yy, double *x, double *y, double *x2, double *y2));
184 static char *xy_format __PROTO((void));
185 static char *zoombox_format __PROTO((void));
186 static char *xy1_format __PROTO((char *leader));
187 static char *GetAnnotateString __PROTO((char *s, double x, double y, int mode, char *fmt));
188 static char *xDateTimeFormat __PROTO((double x, char *b, int mode));
189 #ifdef OLD_STATUS_LINE
190 static char *GetCoordinateString __PROTO((char *s, double x, double y));
191 #endif
192 static void GetRulerString __PROTO((char *p, double x, double y));
193 static void apply_zoom __PROTO((struct t_zoom * z));
194 static void do_zoom __PROTO((double xmin, double ymin, double x2min,
195                              double y2min, double xmax, double ymax, double x2max, double y2max));
196 static void ZoomNext __PROTO((void));
197 static void ZoomPrevious __PROTO((void));
198 static void ZoomUnzoom __PROTO((void));
199 static void incr_mousemode __PROTO((const int amount));
200 static void incr_clipboardmode __PROTO((const int amount));
201 static void UpdateStatuslineWithMouseSetting __PROTO((mouse_setting_t * ms));
202
203 static void event_keypress __PROTO((struct gp_event_t * ge, TBOOLEAN current));
204 static void ChangeView __PROTO((int x, int z));
205 static void event_buttonpress __PROTO((struct gp_event_t * ge));
206 static void event_buttonrelease __PROTO((struct gp_event_t * ge));
207 static void event_motion __PROTO((struct gp_event_t * ge));
208 static void event_modifier __PROTO((struct gp_event_t * ge));
209 static void do_save_3dplot __PROTO((struct surface_points *, int, int));
210 static void load_mouse_variables __PROTO((double, double, TBOOLEAN, int));
211
212 /* builtins */
213 static char *builtin_autoscale __PROTO((struct gp_event_t * ge));
214 static char *builtin_toggle_border __PROTO((struct gp_event_t * ge));
215 static char *builtin_replot __PROTO((struct gp_event_t * ge));
216 static char *builtin_toggle_grid __PROTO((struct gp_event_t * ge));
217 static char *builtin_help __PROTO((struct gp_event_t * ge));
218 static char *builtin_toggle_log __PROTO((struct gp_event_t * ge));
219 static char *builtin_nearest_log __PROTO((struct gp_event_t * ge));
220 static char *builtin_toggle_mouse __PROTO((struct gp_event_t * ge));
221 static char *builtin_toggle_ruler __PROTO((struct gp_event_t * ge));
222 static char *builtin_decrement_mousemode __PROTO((struct gp_event_t * ge));
223 static char *builtin_increment_mousemode __PROTO((struct gp_event_t * ge));
224 static char *builtin_decrement_clipboardmode __PROTO((struct gp_event_t * ge));
225 static char *builtin_increment_clipboardmode __PROTO((struct gp_event_t * ge));
226 static char *builtin_toggle_polardistance __PROTO((struct gp_event_t * ge));
227 static char *builtin_toggle_verbose __PROTO((struct gp_event_t * ge));
228 static char *builtin_toggle_ratio __PROTO((struct gp_event_t * ge));
229 static char *builtin_zoom_next __PROTO((struct gp_event_t * ge));
230 static char *builtin_zoom_previous __PROTO((struct gp_event_t * ge));
231 static char *builtin_unzoom __PROTO((struct gp_event_t * ge));
232 static char *builtin_rotate_right __PROTO((struct gp_event_t * ge));
233 static char *builtin_rotate_up __PROTO((struct gp_event_t * ge));
234 static char *builtin_rotate_left __PROTO((struct gp_event_t * ge));
235 static char *builtin_rotate_down __PROTO((struct gp_event_t * ge));
236 static char *builtin_cancel_zoom __PROTO((struct gp_event_t * ge));
237
238 /* prototypes for bind stuff
239  * which are used only here. */
240 static void bind_install_default_bindings __PROTO((void));
241 static void bind_clear __PROTO((bind_t * b));
242 static int lookup_key __PROTO((char *ptr, int *len));
243 static int bind_scan_lhs __PROTO((bind_t * out, const char *in));
244 static char *bind_fmt_lhs __PROTO((const bind_t * in));
245 static int bind_matches __PROTO((const bind_t * a, const bind_t * b));
246 static void bind_display_one __PROTO((bind_t * ptr));
247 static void bind_display __PROTO((char *lhs));
248 static void bind_all __PROTO((char *lhs));
249 static void bind_remove __PROTO((bind_t * b));
250 static void bind_append __PROTO((char *lhs, char *rhs, char *(*builtin) (struct gp_event_t * ge)));
251 static void recalc_ruler_pos __PROTO((void));
252 static void turn_ruler_off __PROTO((void));
253 static int nearest_label_tag __PROTO((int x, int y, struct termentry * t));
254 static void remove_label __PROTO((int x, int y));
255 static void put_label __PROTO((char *label, double x, double y));
256
257 /********* functions ********************************************/
258
259 /* produce a beep */
260 static void
261 alert()
262 {
263 # ifdef GNUPMDRV
264     DosBeep(444, 111);
265 # else
266 #  ifdef HAVE_LIBREADLINE
267     rl_ding();
268     fflush(rl_outstream);
269 #  else
270     fprintf(stderr, "\a");
271 #  endif
272 # endif
273 }
274
275 /* always include the prototype. The prototype might even not be
276  * declared if the system supports stpcpy(). E.g. on Linux I would
277  * have to define __USE_GNU before including string.h to get the
278  * prototype (joze) */
279 /* HBB 20001109: *BUT* if a prototype is there, this one may easily
280  * conflict with it... */
281 char *stpcpy __PROTO((char *s, const char *p));
282
283 # ifndef HAVE_STPCPY
284 /* handy function for composing strings; note: some platforms may
285  * already provide it, how should we handle that? autoconf? -- ptdb */
286 char *
287 stpcpy(char *s, const char *p)
288 {
289     strcpy(s, p);
290     return s + strlen(p);
291 }
292 # endif
293
294
295 /* main job of transformation, which is not device dependent
296 */
297 static void
298 MousePosToGraphPosReal(int xx, int yy, double *x, double *y, double *x2, double *y2)
299 {
300     if (!is_3d_plot) {
301 # if 0
302         printf("POS: plot_bounds.xleft=%i, plot_bounds.xright=%i, plot_bounds.ybot=%i, plot_bounds.ytop=%i\n", plot_bounds.xleft, plot_bounds.xright, plot_bounds.ybot, plot_bounds.ytop);
303 # endif
304         if (plot_bounds.xright == plot_bounds.xleft)
305             *x = *x2 = VERYLARGE;       /* protection */
306         else {
307             *x = AXIS_MAPBACK(FIRST_X_AXIS, xx);
308             *x2 = AXIS_MAPBACK(SECOND_X_AXIS, xx);
309         }
310         if (plot_bounds.ytop == plot_bounds.ybot)
311             *y = *y2 = VERYLARGE;       /* protection */
312         else {
313             *y = AXIS_MAPBACK(FIRST_Y_AXIS, yy);
314             *y2 = AXIS_MAPBACK(SECOND_Y_AXIS, yy);
315         }
316 #if 0
317         printf("POS: xx=%i, yy=%i  =>  x=%g  y=%g\n", xx, yy, *x, *y);
318 #endif
319     } else {
320         /* for 3D plots, we treat the mouse position as if it is
321          * in the bottom plane, i.e., the plane of the x and y axis */
322         /* note: at present, this projection is only correct if
323          * surface_rot_z is a multiple of 90 degrees! */
324         /* HBB 20010522: added protection against division by zero
325          * for cases like 'set view 90,0' */
326         xx -= axis3d_o_x;
327         yy -= axis3d_o_y;
328         if (abs(axis3d_x_dx) > abs(axis3d_x_dy)) {
329             *x = axis_array[FIRST_X_AXIS].min
330                 + ((double) xx) / axis3d_x_dx * (axis_array[FIRST_X_AXIS].max -
331                                                  axis_array[FIRST_X_AXIS].min);
332         } else if (axis3d_x_dy != 0) {
333             *x = axis_array[FIRST_X_AXIS].min
334                 + ((double) yy) / axis3d_x_dy * (axis_array[FIRST_X_AXIS].max -
335                                                  axis_array[FIRST_X_AXIS].min);
336         } else {
337             /* both diffs are zero (x axis points into the screen */
338             *x = VERYLARGE;
339         }
340
341         if (abs(axis3d_y_dx) > abs(axis3d_y_dy)) {
342             *y = axis_array[FIRST_Y_AXIS].min
343                 + ((double) xx) / axis3d_y_dx * (axis_array[FIRST_Y_AXIS].max -
344                                                  axis_array[FIRST_Y_AXIS].min);
345         } else if (axis3d_y_dy != 0) {
346             *y = axis_array[FIRST_Y_AXIS].min
347                 + ((double) yy) / axis3d_y_dy * (axis_array[FIRST_Y_AXIS].max -
348                                                  axis_array[FIRST_Y_AXIS].min);
349         } else {
350             /* both diffs are zero (y axis points into the screen */
351             *y = VERYLARGE;
352         }
353
354         *x2 = *y2 = VERYLARGE;  /* protection */
355     }
356     /*
357        Note: there is plot_bounds.xleft+0.5 in "#define map_x" in graphics.c, which
358        makes no major impact here. It seems that the mistake of the real
359        coordinate is at about 0.5%, which corresponds to the screen resolution.
360        It would be better to round the distance to this resolution, and thus
361        *x = xmin + rounded-to-screen-resolution (xdistance)
362      */
363
364     /* Now take into account possible log scales of x and y axes */
365     *x = AXIS_DE_LOG_VALUE(FIRST_X_AXIS, *x);
366     *y = AXIS_DE_LOG_VALUE(FIRST_Y_AXIS, *y);
367     if (!is_3d_plot) {
368         *x2 = AXIS_DE_LOG_VALUE(SECOND_X_AXIS, *x2);
369         *y2 = AXIS_DE_LOG_VALUE(SECOND_Y_AXIS, *y2);
370     }
371 }
372
373 static char *
374 xy_format()
375 {
376     static char format[0xff];
377     format[0] = NUL;
378     strcat(format, mouse_setting.fmt);
379     strcat(format, ", ");
380     strcat(format, mouse_setting.fmt);
381     return format;
382 }
383
384 static char *
385 zoombox_format()
386 {
387     static char format[0xff];
388     format[0] = NUL;
389     strcat(format, mouse_setting.fmt);
390     strcat(format, "\r");
391     strcat(format, mouse_setting.fmt);
392     return format;
393 }
394
395 static char *
396 xy1_format(char *leader)
397 {
398     static char format[0xff];
399     format[0] = NUL;
400     strcat(format, leader);
401     strcat(format, mouse_setting.fmt);
402     return format;
403 }
404
405 /* formats the information for an annotation (middle mouse button clicked)
406  */
407 static char *
408 GetAnnotateString(char *s, double x, double y, int mode, char *fmt)
409 {
410     if (mode == MOUSE_COORDINATES_XDATE || mode == MOUSE_COORDINATES_XTIME || mode == MOUSE_COORDINATES_XDATETIME || mode == MOUSE_COORDINATES_TIMEFMT) {       /* time is on the x axis */
411         char buf[0xff];
412         char format[0xff] = "[%s, ";
413         strcat(format, mouse_setting.fmt);
414         strcat(format, "]");
415         s += sprintf(s, format, xDateTimeFormat(x, buf, mode), y);
416     } else if (mode == MOUSE_COORDINATES_FRACTIONAL) {
417         double xrange = axis_array[FIRST_X_AXIS].max - axis_array[FIRST_X_AXIS].min;
418         double yrange = axis_array[FIRST_Y_AXIS].max - axis_array[FIRST_Y_AXIS].min;
419         /* calculate fractional coordinates.
420          * prevent division by zero */
421         if (xrange) {
422             char format[0xff] = "/";
423             strcat(format, mouse_setting.fmt);
424             s += sprintf(s, format, (x - axis_array[FIRST_X_AXIS].min) / xrange);
425         } else {
426             s += sprintf(s, "/(undefined)");
427         }
428         if (yrange) {
429             char format[0xff] = ", ";
430             strcat(format, mouse_setting.fmt);
431             strcat(format, "/");
432             s += sprintf(s, format, (y - axis_array[FIRST_Y_AXIS].min) / yrange);
433         } else {
434             s += sprintf(s, ", (undefined)/");
435         }
436     } else if (mode == MOUSE_COORDINATES_REAL1) {
437         s += sprintf(s, xy_format(), x, y);     /* w/o brackets */
438     } else if (mode == MOUSE_COORDINATES_ALT && fmt) {
439         s += sprintf(s, fmt, x, y);     /* user defined format */
440     } else {
441         s += sprintf(s, xy_format(), x, y);     /* usual x,y values */
442     }
443     return s;
444 }
445
446
447 /* Format x according to the date/time mouse mode. Uses and returns b as
448    a buffer
449  */
450 static char *
451 xDateTimeFormat(double x, char *b, int mode)
452 {
453 # ifndef SEC_OFFS_SYS
454 #  define SEC_OFFS_SYS 946684800
455 # endif
456     time_t xtime_position = SEC_OFFS_SYS + x;
457     struct tm *pxtime_position = gmtime(&xtime_position);
458     switch (mode) {
459     case MOUSE_COORDINATES_XDATE:
460         sprintf(b, "%d. %d. %04d", pxtime_position->tm_mday, (pxtime_position->tm_mon) + 1,
461 # if 1
462                 (pxtime_position->tm_year) + ((pxtime_position->tm_year <= 68) ? 2000 : 1900)
463 # else
464                 ((pxtime_position->tm_year) < 100) ? (pxtime_position->tm_year) : (pxtime_position->tm_year) - 100
465                 /*              (pxtime_position->tm_year)+1900 */
466 # endif
467             );
468         break;
469     case MOUSE_COORDINATES_XTIME:
470         sprintf(b, "%d:%02d", pxtime_position->tm_hour, pxtime_position->tm_min);
471         break;
472     case MOUSE_COORDINATES_XDATETIME:
473         sprintf(b, "%d. %d. %04d %d:%02d", pxtime_position->tm_mday, (pxtime_position->tm_mon) + 1,
474 # if 1
475                 (pxtime_position->tm_year) + ((pxtime_position->tm_year <= 68) ? 2000 : 1900),
476 # else
477                 ((pxtime_position->tm_year) < 100) ? (pxtime_position->tm_year) : (pxtime_position->tm_year) - 100,
478                 /*              (pxtime_position->tm_year)+1900, */
479 # endif
480                 pxtime_position->tm_hour, pxtime_position->tm_min);
481         break;
482     case MOUSE_COORDINATES_TIMEFMT:
483         /* FIXME HBB 20000507: timefmt is for *reading* timedata, not
484          * for writing them! */
485         gstrftime(b, 0xff, axis_array[FIRST_X_AXIS].timefmt, x);
486         break;
487     default:
488         sprintf(b, mouse_setting.fmt, x);
489     }
490     return b;
491 }
492
493
494
495 /* HBB 20000507: fixed a construction error. Was using the 'timefmt'
496  * string (which is for reading, not writing time data) to output the
497  * value. Code is now closer to what setup_tics does. */
498 #define MKSTR(sp,x,axis)                                        \
499 do {                                                            \
500     if (x >= VERYLARGE) break;                                  \
501     if (axis_array[axis].is_timedata) {                         \
502         char *format = copy_or_invent_formatstring(axis);       \
503         while (strchr(format,'\n'))                             \
504              *(strchr(format,'\n')) = ' ';                      \
505         sp+=gstrftime(sp, 40, format, x);                       \
506     } else                                                      \
507         sp+=sprintf(sp, mouse_setting.fmt ,x);                  \
508 } while (0)
509
510
511 /* formats the information for an annotation (middle mouse button clicked)
512  * returns pointer to the end of the string, for easy concatenation
513 */
514 # ifdef OLD_STATUS_LINE
515 static char *
516 GetCoordinateString(char *s, double x, double y)
517 {
518     char *sp;
519     s[0] = '[';
520     sp = s + 1;
521     MKSTR(sp, x, FIRST_X_AXIS);
522     *sp++ = ',';
523     *sp++ = ' ';
524     MKSTR(sp, y, FIRST_Y_AXIS);
525     *sp++ = ']';
526     *sp = 0;
527     return sp;
528 }
529 # endif
530
531
532 /* ratio for log, distance for linear */
533 # define DIST(x,rx,axis)                        \
534    (axis_array[axis].log)                       \
535     ? ( (rx==0) ? 99999 : x / rx )              \
536     : (x - rx)
537
538
539 /* formats the ruler information (position, distance,...) into string p
540         (it must be sufficiently long)
541    x, y is the current mouse position in real coords (for the calculation
542         of distance)
543 */
544 static void
545 GetRulerString(char *p, double x, double y)
546 {
547     double dx, dy;
548
549     char format[0xff] = "  ruler: [";
550     strcat(format, mouse_setting.fmt);
551     strcat(format, ", ");
552     strcat(format, mouse_setting.fmt);
553     strcat(format, "]  distance: ");
554     strcat(format, mouse_setting.fmt);
555     strcat(format, ", ");
556     strcat(format, mouse_setting.fmt);
557
558     dx = DIST(x, ruler.x, FIRST_X_AXIS);
559     dy = DIST(y, ruler.y, FIRST_Y_AXIS);
560     sprintf(p, format, ruler.x, ruler.y, dx, dy);
561
562     /* Previously, the following "if" let the polar coordinates to be shown only
563        for lin-lin plots:
564             if (mouse_setting.polardistance && !axis_array[FIRST_X_AXIS].log && !axis_array[FIRST_Y_AXIS].log) ...
565        Now, let us support also semilog and log-log plots.
566        Values of mouse_setting.polardistance are:
567             0 (no polar coordinates), 1 (polar coordinates), 2 (tangent instead of angle).
568     */
569     if (mouse_setting.polardistance) {
570         double rho, phi, rx, ry;
571         char ptmp[69];
572         x = AXIS_LOG_VALUE(FIRST_X_AXIS, x);
573         y = AXIS_LOG_VALUE(FIRST_Y_AXIS, y);
574         rx = AXIS_LOG_VALUE(FIRST_X_AXIS, ruler.x);
575         ry = AXIS_LOG_VALUE(FIRST_Y_AXIS, ruler.y);
576         format[0] = '\0';
577         strcat(format, " (");
578         strcat(format, mouse_setting.fmt);
579         rho = sqrt((x - rx) * (x - rx) + (y - ry) * (y - ry)); /* distance */
580         if (mouse_setting.polardistance == 1) { /* (distance, angle) */
581             phi = (180 / M_PI) * atan2(y - ry, x - rx);
582 # ifdef OS2
583             strcat(format, ";% #.4gø)");
584 # else
585             strcat(format, ", % #.4gdeg)");
586 # endif
587         } else { /* mouse_setting.polardistance==2: (distance, tangent) */
588             phi = x - rx;
589             phi = (phi == 0) ? ((y-ry>0) ? VERYLARGE : -VERYLARGE) : (y - ry)/phi;
590             sprintf(format+strlen(format), ", tangent=%s)", mouse_setting.fmt);
591         }
592         sprintf(ptmp, format, rho, phi);
593         strcat(p, ptmp);
594     }
595 }
596
597
598 static struct t_zoom *zoom_head = NULL, *zoom_now = NULL;
599 /* Applies the zoom rectangle of  z  by sending the appropriate command
600    to gnuplot
601 */
602
603 static void
604 apply_zoom(struct t_zoom *z)
605 {
606     char s[1024];               /* HBB 20011005: made larger */
607     /* HBB 20011004: new variable, fixing 'unzoom' back to autoscaled range */
608     static t_autoscale autoscale_copies[AXIS_ARRAY_SIZE];
609     int is_splot_map = (is_3d_plot && (splot_map == TRUE));
610     int flip = 0;
611
612     if (zoom_now != NULL) {     /* remember the current zoom */
613         zoom_now->xmin = axis_array[FIRST_X_AXIS].set_min;
614         zoom_now->xmax = axis_array[FIRST_X_AXIS].set_max;
615         zoom_now->x2min = axis_array[SECOND_X_AXIS].set_min;
616         zoom_now->x2max = axis_array[SECOND_X_AXIS].set_max;
617         zoom_now->was_splot_map = is_splot_map;
618         if (!is_splot_map) { /* 2D plot */
619             zoom_now->ymin = axis_array[FIRST_Y_AXIS].set_min;
620             zoom_now->ymax = axis_array[FIRST_Y_AXIS].set_max;
621             zoom_now->y2min = axis_array[SECOND_Y_AXIS].set_min;
622             zoom_now->y2max = axis_array[SECOND_Y_AXIS].set_max;
623         } else { /* the opposite, i.e. case 'set view map' */
624             zoom_now->ymin = axis_array[FIRST_Y_AXIS].set_max;
625             zoom_now->ymax = axis_array[FIRST_Y_AXIS].set_min;
626             zoom_now->y2min = axis_array[SECOND_Y_AXIS].set_max;
627             zoom_now->y2max = axis_array[SECOND_Y_AXIS].set_min;
628         }
629
630     }
631
632     /* HBB 20011004: implement save/restore of autoscaling if returning to
633      * original setting */
634     if (zoom_now == zoom_head) {
635         /* previous current zoom is the head --> we're leaving
636          * original non-moused status --> save autoscales */
637         AXIS_INDEX i;
638         for (i = 0; i < AXIS_ARRAY_SIZE; i++)
639             autoscale_copies[i] = axis_array[i].set_autoscale;
640     }
641
642     zoom_now = z;
643     if (zoom_now == NULL) {
644         alert();
645         return;
646     }
647
648     flip = (is_splot_map && zoom_now->was_splot_map);
649 #ifdef HAVE_LOCALE_H
650     if (strcmp(localeconv()->decimal_point,".")) {
651         char *save_locale = gp_strdup(setlocale(LC_NUMERIC,NULL));
652         setlocale(LC_NUMERIC,"C");
653         sprintf(s, "set xr[%.12g:%.12g]; set yr[%.12g:%.12g]",
654                zoom_now->xmin, zoom_now->xmax, 
655                (flip) ? zoom_now->ymax : zoom_now->ymin,
656                (flip) ? zoom_now->ymin : zoom_now->ymax);
657         setlocale(LC_NUMERIC,save_locale);
658         free(save_locale);
659     } else
660 #endif
661         sprintf(s, "set xr[%.12g:%.12g]; set yr[%.12g:%.12g]",
662                zoom_now->xmin, zoom_now->xmax, 
663                (flip) ? zoom_now->ymax : zoom_now->ymin,
664                (flip) ? zoom_now->ymin : zoom_now->ymax);
665
666     /* HBB 20011004: the final part of 'unzoom to autoscale mode'.
667      * Optionally appends 'set autoscale' commands to the string to be
668      * sent, to restore the saved settings. */
669 #define OUTPUT_AUTOSCALE(axis)                                               \
670     {                                        \
671         t_autoscale autoscale = autoscale_copies[axis];                      \
672         t_autoscale fix = autoscale & (AUTOSCALE_FIXMIN | AUTOSCALE_FIXMAX); \
673                                                                              \
674         autoscale &= AUTOSCALE_BOTH;                                         \
675                                                                              \
676         if (autoscale) {                                                     \
677             sprintf(s + strlen(s), "; set au %s%s",                          \
678                     axis_defaults[axis].name,                                \
679                     autoscale == AUTOSCALE_MIN ? "min"                       \
680                     : autoscale == AUTOSCALE_MAX ? "max"                     \
681                     : "");                                                   \
682             if (fix)                                                         \
683                 sprintf(s + strlen(s), "; set au %sfix%s",                   \
684                         axis_defaults[axis].name,                            \
685                         fix == AUTOSCALE_FIXMIN ? "min"                      \
686                         : fix == AUTOSCALE_FIXMAX ? "max"                    \
687                         : "");                                               \
688         }                                                                    \
689     }
690
691     if (zoom_now == zoom_head) {
692         /* new current zoom is the head --> returning to unzoomed
693          * settings --> restore autoscale */
694         OUTPUT_AUTOSCALE(FIRST_X_AXIS);
695         OUTPUT_AUTOSCALE(FIRST_Y_AXIS);
696     }
697
698     if (!is_3d_plot) {
699 #ifdef HAVE_LOCALE_H
700         if (strcmp(localeconv()->decimal_point,".")) {
701             char *save_locale = gp_strdup(setlocale(LC_NUMERIC,NULL));
702             setlocale(LC_NUMERIC,"C");
703             sprintf(s + strlen(s), "; set x2r[% #g:% #g]; set y2r[% #g:% #g]",
704                 zoom_now->x2min, zoom_now->x2max,
705                 zoom_now->y2min, zoom_now->y2max);
706             setlocale(LC_NUMERIC,save_locale);
707             free(save_locale);
708         } else
709 #endif
710             sprintf(s + strlen(s), "; set x2r[% #g:% #g]; set y2r[% #g:% #g]",
711                 zoom_now->x2min, zoom_now->x2max,
712                 zoom_now->y2min, zoom_now->y2max);
713         if (zoom_now == zoom_head) {
714             /* new current zoom is the head --> returning to unzoomed
715              * settings --> restore autoscale */
716             OUTPUT_AUTOSCALE(SECOND_X_AXIS);
717             OUTPUT_AUTOSCALE(SECOND_Y_AXIS);
718         }
719 #undef OUTPUT_AUTOSCALE
720     }
721     do_string_replot(s);
722 }
723
724
725 /* makes a zoom: update zoom history, call gnuplot to set ranges + replot
726 */
727
728 static void
729 do_zoom(double xmin, double ymin, double x2min, double y2min, double xmax, double ymax, double x2max, double y2max)
730 {
731     struct t_zoom *z;
732     if (zoom_head == NULL) {    /* queue not yet created, thus make its head */
733         zoom_head = gp_alloc(sizeof(struct t_zoom), "mouse zoom history head");
734         zoom_head->prev = NULL;
735         zoom_head->next = NULL;
736     }
737     if (zoom_now == NULL)
738         zoom_now = zoom_head;
739     if (zoom_now->next == NULL) {       /* allocate new item */
740         z = gp_alloc(sizeof(struct t_zoom), "mouse zoom history element");
741         z->prev = zoom_now;
742         z->next = NULL;
743         zoom_now->next = z;
744         z->prev = zoom_now;
745     } else                      /* overwrite next item */
746         z = zoom_now->next;
747     z->xmin = xmin;
748     z->ymin = ymin;
749     z->x2min = x2min;
750     z->y2min = y2min;
751     z->xmax = xmax;
752     z->ymax = ymax;
753     z->x2max = x2max;
754     z->y2max = y2max;
755     z->was_splot_map = (is_3d_plot && (splot_map == TRUE)); /* see is_splot_map in apply_zoom() */ 
756     apply_zoom(z);
757 }
758
759
760 static void
761 ZoomNext()
762 {
763     if (zoom_now == NULL || zoom_now->next == NULL)
764         alert();
765     else
766         apply_zoom(zoom_now->next);
767     if (display_ipc_commands()) {
768         fprintf(stderr, "next zoom.\n");
769     }
770 }
771
772 static void
773 ZoomPrevious()
774 {
775     if (zoom_now == NULL || zoom_now->prev == NULL)
776         alert();
777     else
778         apply_zoom(zoom_now->prev);
779     if (display_ipc_commands()) {
780         fprintf(stderr, "previous zoom.\n");
781     }
782 }
783
784 static void
785 ZoomUnzoom()
786 {
787     if (zoom_head == NULL || zoom_now == zoom_head)
788         alert();
789     else
790         apply_zoom(zoom_head);
791     if (display_ipc_commands()) {
792         fprintf(stderr, "unzoom.\n");
793     }
794 }
795
796 static void
797 incr_mousemode(const int amount)
798 {
799     long int old = mouse_mode;
800     mouse_mode += amount;
801     if (MOUSE_COORDINATES_ALT == mouse_mode && !mouse_alt_string) {
802         mouse_mode += amount;   /* stepping over */
803     }
804     if (mouse_mode > MOUSE_COORDINATES_ALT) {
805         mouse_mode = MOUSE_COORDINATES_REAL;
806     } else if (mouse_mode < MOUSE_COORDINATES_REAL) {
807         mouse_mode = MOUSE_COORDINATES_ALT;
808         if (MOUSE_COORDINATES_ALT == mouse_mode && !mouse_alt_string) {
809             mouse_mode--;       /* stepping over */
810         }
811     }
812     UpdateStatusline();
813     if (display_ipc_commands()) {
814         fprintf(stderr, "switched mouse format from %ld to %ld\n", old, mouse_mode);
815     }
816 }
817
818 static void
819 incr_clipboardmode(const int amount)
820 {
821     long int old = clipboard_mode;
822     clipboard_mode += amount;
823     if (MOUSE_COORDINATES_ALT == clipboard_mode && !clipboard_alt_string) {
824         clipboard_mode += amount;       /* stepping over */
825     }
826     if (clipboard_mode > MOUSE_COORDINATES_ALT) {
827         clipboard_mode = MOUSE_COORDINATES_REAL;
828     } else if (clipboard_mode < MOUSE_COORDINATES_REAL) {
829         clipboard_mode = MOUSE_COORDINATES_ALT;
830         if (MOUSE_COORDINATES_ALT == clipboard_mode && !clipboard_alt_string) {
831             clipboard_mode--;   /* stepping over */
832         }
833     }
834     if (display_ipc_commands()) {
835         fprintf(stderr, "switched clipboard format from %ld to %ld\n", old, clipboard_mode);
836     }
837 }
838
839 # define TICS_ON(ti) (((ti)&TICS_MASK)!=NO_TICS)
840
841 void
842 UpdateStatusline()
843 {
844     UpdateStatuslineWithMouseSetting(&mouse_setting);
845 }
846
847 static void
848 UpdateStatuslineWithMouseSetting(mouse_setting_t * ms)
849 {
850     char s0[256], *sp;
851     if (!term_initialised)
852         return;
853     if (!ms->on) {
854         s0[0] = 0;
855     } else if (!ALMOST2D) {
856         char format[0xff];
857         format[0] = '\0';
858         strcat(format, "view: ");
859         strcat(format, ms->fmt);
860         strcat(format, ", ");
861         strcat(format, ms->fmt);
862         strcat(format, "   scale: ");
863         strcat(format, ms->fmt);
864         strcat(format, ", ");
865         strcat(format, ms->fmt);
866         sprintf(s0, format, surface_rot_x, surface_rot_z, surface_scale, surface_zscale);
867     } else if (!TICS_ON(axis_array[SECOND_X_AXIS].ticmode) && !TICS_ON(axis_array[SECOND_Y_AXIS].ticmode)) {
868         /* only first X and Y axis are in use */
869 # ifdef OLD_STATUS_LINE
870         sp = GetCoordinateString(s0, real_x, real_y);
871 # else
872         sp = GetAnnotateString(s0, real_x, real_y, mouse_mode, mouse_alt_string);
873 # endif
874         if (ruler.on) {
875 # if 0
876             MousePosToGraphPosReal(ruler.px, ruler.py, &ruler.x, &ruler.y, &ruler.x2, &ruler.y2);
877 # endif
878             GetRulerString(sp, real_x, real_y);
879         }
880     } else {
881         /* X2 and/or Y2 are in use: use more verbose format */
882         sp = s0;
883         if (TICS_ON(axis_array[FIRST_X_AXIS].ticmode)) {
884             sp = stpcpy(sp, "x=");
885             MKSTR(sp, real_x, FIRST_X_AXIS);
886             *sp++ = ' ';
887         }
888         if (TICS_ON(axis_array[FIRST_Y_AXIS].ticmode)) {
889             sp = stpcpy(sp, "y=");
890             MKSTR(sp, real_y, FIRST_Y_AXIS);
891             *sp++ = ' ';
892         }
893         if (TICS_ON(axis_array[SECOND_X_AXIS].ticmode)) {
894             sp = stpcpy(sp, "x2=");
895             MKSTR(sp, real_x2, SECOND_X_AXIS);
896             *sp++ = ' ';
897         }
898         if (TICS_ON(axis_array[SECOND_Y_AXIS].ticmode)) {
899             sp = stpcpy(sp, "y2=");
900             MKSTR(sp, real_y2, SECOND_Y_AXIS);
901             *sp++ = ' ';
902         }
903         if (ruler.on) {
904             /* ruler on? then also print distances to ruler */
905 # if 0
906             MousePosToGraphPosReal(ruler.px, ruler.py, &ruler.x, &ruler.y, &ruler.x2, &ruler.y2);
907 # endif
908             if (TICS_ON(axis_array[FIRST_X_AXIS].ticmode))
909                 sp += sprintf(sp, xy1_format("dx="), DIST(real_x, ruler.x, FIRST_X_AXIS));
910             if (TICS_ON(axis_array[FIRST_Y_AXIS].ticmode))
911                 sp += sprintf(sp, xy1_format("dy="), DIST(real_y, ruler.y, FIRST_Y_AXIS));
912             if (TICS_ON(axis_array[SECOND_X_AXIS].ticmode))
913                 sp += sprintf(sp, xy1_format("dx2="), DIST(real_x2, ruler.x2, SECOND_X_AXIS));
914             if (TICS_ON(axis_array[SECOND_Y_AXIS].ticmode))
915                 sp += sprintf(sp, xy1_format("dy2="), DIST(real_y2, ruler.y2, SECOND_Y_AXIS));
916         }
917         *--sp = 0;              /* delete trailing space */
918     }
919     if (term->put_tmptext) {
920         (term->put_tmptext) (0, s0);
921     }
922 }
923 #undef MKSTR
924
925
926 void
927 recalc_statusline()
928 {
929     MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
930     UpdateStatusline();
931 }
932
933 /****************** handlers for user's actions ******************/
934
935 static char *
936 builtin_autoscale(struct gp_event_t *ge)
937 {
938     if (!ge) {
939         return "`builtin-autoscale` (set autoscale keepfix; replot)";
940     }
941     do_string_replot("set autoscale keepfix");
942     return (char *) 0;
943 }
944
945 static char *
946 builtin_toggle_border(struct gp_event_t *ge)
947 {
948     if (!ge) {
949         return "`builtin-toggle-border`";
950     }
951     if (is_3d_plot) {
952         if (draw_border == 4095)
953             do_string_replot("set border");
954         else
955             do_string_replot("set border 4095 lw 0.5");
956     } else {
957         if (draw_border == 15)
958             do_string_replot("set border");
959         else
960             do_string_replot("set border 15 lw 0.5");
961     }
962     return (char *) 0;
963 }
964
965 static char *
966 builtin_replot(struct gp_event_t *ge)
967 {
968     if (!ge) {
969         return "`builtin-replot`";
970     }
971     do_string_replot("");
972     return (char *) 0;
973 }
974
975 static char *
976 builtin_toggle_grid(struct gp_event_t *ge)
977 {
978     if (!ge) {
979         return "`builtin-toggle-grid`";
980     }
981     if (! some_grid_selected())
982         do_string_replot("set grid");
983     else
984         do_string_replot("unset grid");
985     return (char *) 0;
986 }
987
988 static char *
989 builtin_help(struct gp_event_t *ge)
990 {
991     if (!ge) {
992         return "`builtin-help`";
993     }
994     fprintf(stderr, "\n");
995     bind_display((char *) 0);   /* display all bindings */
996     restore_prompt();
997     return (char *) 0;
998 }
999
1000 static char *
1001 builtin_toggle_log(struct gp_event_t *ge)
1002 {
1003     if (!ge)
1004         return "`builtin-toggle-log` y logscale for plots, z and cb logscale for splots";
1005     if (is_3d_plot) {
1006         if (Z_AXIS.log || CB_AXIS.log)
1007             do_string_replot("unset log zcb");
1008         else
1009             do_string_replot("set log zcb");
1010     } else {
1011 #ifdef WITH_IMAGE
1012         /* set log cb or log y whether using "with (rgb)image" plot or not */
1013         if (is_cb_plot) {
1014             if (CB_AXIS.log)
1015                 do_string_replot("unset log cb");
1016             else
1017                 do_string_replot("set log cb");
1018         } else {
1019 #endif
1020         if (axis_array[FIRST_Y_AXIS].log)
1021             do_string_replot("unset log y");
1022         else
1023             do_string_replot("set log y");
1024 #ifdef WITH_IMAGE
1025         }
1026 #endif
1027     }
1028     return (char *) 0;
1029 }
1030
1031 static char *
1032 builtin_nearest_log(struct gp_event_t *ge)
1033 {
1034     if (!ge) {
1035         return "`builtin-nearest-log` toggle logscale of axis nearest cursor";
1036     }
1037     if (is_3d_plot) {
1038         /* 3D-plot: toggle lin/log z axis */
1039         if (Z_AXIS.log || CB_AXIS.log)
1040             do_string_replot("unset log zcb");
1041         else
1042             do_string_replot("set log zcb");
1043     } else {
1044         /* 2D-plot: figure out which axis/axes is/are
1045          * close to the mouse cursor, and toggle those lin/log */
1046         /* note: here it is assumed that the x axis is at
1047          * the bottom, x2 at top, y left and y2 right; it
1048          * would be better to derive that from the ..tics settings */
1049         TBOOLEAN change = FALSE;
1050         if (mouse_y < plot_bounds.ybot + (plot_bounds.ytop - plot_bounds.ybot) / 4 && mouse_x > plot_bounds.xleft && mouse_x < plot_bounds.xright) {
1051             do_string(axis_array[FIRST_X_AXIS].log ? "unset log x" : "set log x");
1052             change = TRUE;
1053         }
1054         if (mouse_y > plot_bounds.ytop - (plot_bounds.ytop - plot_bounds.ybot) / 4 && mouse_x > plot_bounds.xleft && mouse_x < plot_bounds.xright) {
1055             do_string(axis_array[SECOND_X_AXIS].log ? "unset log x2" : "set log x2");
1056             change = TRUE;
1057         }
1058         if (mouse_x < plot_bounds.xleft + (plot_bounds.xright - plot_bounds.xleft) / 4 && mouse_y > plot_bounds.ybot && mouse_y < plot_bounds.ytop) {
1059             do_string(axis_array[FIRST_Y_AXIS].log ? "unset log y" : "set log y");
1060             change = TRUE;
1061         }
1062         if (mouse_x > plot_bounds.xright - (plot_bounds.xright - plot_bounds.xleft) / 4 && mouse_y > plot_bounds.ybot && mouse_y < plot_bounds.ytop) {
1063             do_string(axis_array[SECOND_Y_AXIS].log ? "unset log y2" : "set log y2");
1064             change = TRUE;
1065         }
1066         if (change)
1067             do_string_replot("");
1068     }
1069     return (char *) 0;
1070 }
1071
1072 static char *
1073 builtin_toggle_mouse(struct gp_event_t *ge)
1074 {
1075     if (!ge) {
1076         return "`builtin-toggle-mouse`";
1077     }
1078     if (!mouse_setting.on) {
1079         mouse_setting.on = 1;
1080         if (display_ipc_commands()) {
1081             fprintf(stderr, "turning mouse on.\n");
1082         }
1083     } else {
1084         mouse_setting.on = 0;
1085         if (display_ipc_commands()) {
1086             fprintf(stderr, "turning mouse off.\n");
1087         }
1088 # if 0
1089         if (ruler.on) {
1090             ruler.on = FALSE;
1091             (*term->set_ruler) (-1, -1);
1092         }
1093 # endif
1094     }
1095 # ifdef OS2
1096     PM_update_menu_items();
1097 # endif
1098     UpdateStatusline();
1099     return (char *) 0;
1100 }
1101
1102 static char *
1103 builtin_toggle_ruler(struct gp_event_t *ge)
1104 {
1105     if (!ge) {
1106         return "`builtin-toggle-ruler`";
1107     }
1108     if (!term->set_ruler)
1109         return (char *) 0;
1110     if (ruler.on) {
1111         turn_ruler_off();
1112         if (display_ipc_commands())
1113             fprintf(stderr, "turning ruler off.\n");
1114     } else if (ALMOST2D) {
1115         /* only allow ruler, if the plot
1116          * is 2d or a 3d `map' */
1117         struct udvt_entry *u;
1118         ruler.on = TRUE;
1119         ruler.px = ge->mx;
1120         ruler.py = ge->my;
1121         MousePosToGraphPosReal(ruler.px, ruler.py, &ruler.x, &ruler.y, &ruler.x2, &ruler.y2);
1122         (*term->set_ruler) (ruler.px, ruler.py);
1123         if ((u = add_udv_by_name("MOUSE_RULER_X"))) {
1124             u->udv_undef = FALSE;
1125             Gcomplex(&u->udv_value,ruler.x,0);
1126         }
1127         if ((u = add_udv_by_name("MOUSE_RULER_Y"))) {
1128             u->udv_undef = FALSE;
1129             Gcomplex(&u->udv_value,ruler.y,0);
1130         }
1131         if (display_ipc_commands()) {
1132             fprintf(stderr, "turning ruler on.\n");
1133         }
1134     }
1135     UpdateStatusline();
1136     return (char *) 0;
1137 }
1138
1139 static char *
1140 builtin_decrement_mousemode(struct gp_event_t *ge)
1141 {
1142     if (!ge) {
1143         return "`builtin-decrement-mousemode`";
1144     }
1145     incr_mousemode(-1);
1146     return (char *) 0;
1147 }
1148
1149 static char *
1150 builtin_increment_mousemode(struct gp_event_t *ge)
1151 {
1152     if (!ge) {
1153         return "`builtin-increment-mousemode`";
1154     }
1155     incr_mousemode(1);
1156     return (char *) 0;
1157 }
1158
1159 static char *
1160 builtin_decrement_clipboardmode(struct gp_event_t *ge)
1161 {
1162     if (!ge) {
1163         return "`builtin-decrement-clipboardmode`";
1164     }
1165     incr_clipboardmode(-1);
1166     return (char *) 0;
1167 }
1168
1169 static char *
1170 builtin_increment_clipboardmode(struct gp_event_t *ge)
1171 {
1172     if (!ge) {
1173         return "`builtin-increment-clipboardmode`";
1174     }
1175     incr_clipboardmode(1);
1176     return (char *) 0;
1177 }
1178
1179 static char *
1180 builtin_toggle_polardistance(struct gp_event_t *ge)
1181 {
1182     if (!ge) {
1183         return "`builtin-toggle-polardistance`";
1184     }
1185     if (++mouse_setting.polardistance > 2) mouse_setting.polardistance = 0;
1186         /* values: 0 (no polar coordinates), 1 (polar coordinates), 2 (tangent instead of angle) */
1187     term->set_cursor((mouse_setting.polardistance ? -3:-4), ge->mx, ge->my); /* change cursor type */
1188 # ifdef OS2
1189     PM_update_menu_items();
1190 # endif
1191     UpdateStatusline();
1192     if (display_ipc_commands()) {
1193         fprintf(stderr, "distance to ruler will %s be shown in polar coordinates.\n", mouse_setting.polardistance ? "" : "not");
1194     }
1195     return (char *) 0;
1196 }
1197
1198 static char *
1199 builtin_toggle_verbose(struct gp_event_t *ge)
1200 {
1201     if (!ge) {
1202         return "`builtin-toggle-verbose`";
1203     }
1204     /* this is tricky as the command itself modifies
1205      * the state of display_ipc_commands() */
1206     if (display_ipc_commands()) {
1207         fprintf(stderr, "echoing of communication commands is turned off.\n");
1208     }
1209     toggle_display_of_ipc_commands();
1210     if (display_ipc_commands()) {
1211         fprintf(stderr, "communication commands will be echoed.\n");
1212     }
1213     return (char *) 0;
1214 }
1215
1216 static char *
1217 builtin_toggle_ratio(struct gp_event_t *ge)
1218 {
1219     if (!ge) {
1220         return "`builtin-toggle-ratio`";
1221     }
1222     if (aspect_ratio == 0)
1223         do_string_replot("set size ratio -1");
1224     else if (aspect_ratio == 1)
1225         do_string_replot("set size nosquare");
1226     else
1227         do_string_replot("set size square");
1228     return (char *) 0;
1229 }
1230
1231 static char *
1232 builtin_zoom_next(struct gp_event_t *ge)
1233 {
1234     if (!ge) {
1235         return "`builtin-zoom-next` go to next zoom in the zoom stack";
1236     }
1237     ZoomNext();
1238     return (char *) 0;
1239 }
1240
1241 static char *
1242 builtin_zoom_previous(struct gp_event_t *ge)
1243 {
1244     if (!ge) {
1245         return "`builtin-zoom-previous` go to previous zoom in the zoom stack";
1246     }
1247     ZoomPrevious();
1248     return (char *) 0;
1249 }
1250
1251 static char *
1252 builtin_unzoom(struct gp_event_t *ge)
1253 {
1254     if (!ge) {
1255         return "`builtin-unzoom`";
1256     }
1257     ZoomUnzoom();
1258     return (char *) 0;
1259 }
1260
1261 static char *
1262 builtin_rotate_right(struct gp_event_t *ge)
1263 {
1264     if (!ge) {
1265         return "`builtin-rotate-right` only for splots; <shift> increases amount";
1266     }
1267     if (is_3d_plot)
1268         ChangeView(0, -1);
1269     return (char *) 0;
1270 }
1271
1272 static char *
1273 builtin_rotate_left(struct gp_event_t *ge)
1274 {
1275     if (!ge) {
1276         return "`builtin-rotate-left` only for splots; <shift> increases amount";
1277     }
1278     if (is_3d_plot)
1279         ChangeView(0, 1);
1280     return (char *) 0;
1281 }
1282
1283 static char *
1284 builtin_rotate_up(struct gp_event_t *ge)
1285 {
1286     if (!ge) {
1287         return "`builtin-rotate-up` only for splots; <shift> increases amount";
1288     }
1289     if (is_3d_plot)
1290         ChangeView(1, 0);
1291     return (char *) 0;
1292 }
1293
1294 static char *
1295 builtin_rotate_down(struct gp_event_t *ge)
1296 {
1297     if (!ge) {
1298         return "`builtin-rotate-down` only for splots; <shift> increases amount";
1299     }
1300     if (is_3d_plot)
1301         ChangeView(-1, 0);
1302     return (char *) 0;
1303 }
1304
1305 static char *
1306 builtin_cancel_zoom(struct gp_event_t *ge)
1307 {
1308     if (!ge) {
1309         return "`builtin-cancel-zoom` cancel zoom region";
1310     }
1311     if (!setting_zoom_region)
1312         return (char *) 0;
1313     if (term->set_cursor)
1314         term->set_cursor(0, 0, 0);
1315     setting_zoom_region = FALSE;
1316     if (display_ipc_commands()) {
1317         fprintf(stderr, "zooming cancelled.\n");
1318     }
1319     return (char *) 0;
1320 }
1321
1322 static void
1323 event_keypress(struct gp_event_t *ge, TBOOLEAN current)
1324 {
1325     int x, y;
1326     int c, par2;
1327     bind_t *ptr;
1328     bind_t keypress;
1329
1330     c = ge->par1;
1331     par2 = ge->par2;
1332     x = ge->mx;
1333     y = ge->my;
1334
1335     if (!bindings) {
1336         bind_install_default_bindings();
1337     }
1338
1339     if (modifier_mask & Mod_Shift) {
1340         c = toupper(c);
1341     }
1342
1343     bind_clear(&keypress);
1344     keypress.key = c;
1345     keypress.modifier = modifier_mask;
1346
1347     /*
1348      * On 'pause mouse keypress' in active window export current keypress 
1349      * and mouse coords to user variables. A key with 'bind all' terminates 
1350      * a pause even from non-active windows.
1351      * Ignore NULL keypress.
1352      *
1353      * If we are paused for a keystroke, this takes precendence over normal
1354      * key bindings. Otherwise, for example typing 'm' would turn off mousing,
1355      * which is a bad thing if you are in the  middle of a mousing operation.
1356      */
1357
1358 #ifdef _Windows
1359     if (paused_for_mouse & PAUSE_KEYSTROKE)
1360         kill_pending_Pause_dialog();
1361 #endif
1362     
1363     if ((paused_for_mouse & PAUSE_KEYSTROKE) && (c > '\0') && current) {
1364         load_mouse_variables(x, y, FALSE, c);
1365         return;
1366     }
1367
1368     for (ptr = bindings; ptr; ptr = ptr->next) {
1369         if (bind_matches(&keypress, ptr)) {
1370             struct udvt_entry *keywin;
1371             if ((keywin = add_udv_by_name("MOUSE_KEY_WINDOW"))) {
1372                 keywin->udv_undef = FALSE;
1373                 Ginteger(&keywin->udv_value, ge->winid);
1374             }
1375             /* Always honor keys set with "bind all" */
1376             if (ptr->allwindows && ptr->command) {
1377                 if (current)
1378                     load_mouse_variables(x, y, FALSE, c);
1379                 else
1380                     /* FIXME - Better to clear MOUSE_[XY] than to set it wrongly. */
1381                     /*         This may be worth a separate subroutine.           */
1382                     load_mouse_variables(0, 0, FALSE, c);
1383                 do_string(ptr->command);
1384                 /* Treat as a current event after we return to x11.trm */
1385                 ge->type = GE_keypress;
1386                 break;
1387             /* But otherwise ignore inactive windows */
1388             } else if (!current) {
1389                 break;
1390             /* Let user defined bindings overwrite the builtin bindings */
1391             } else if ((par2 & 1) == 0 && ptr->command) {
1392                 do_string(ptr->command);
1393                 break;
1394             } else if (ptr->builtin) {
1395                 ptr->builtin(ge);
1396             } else {
1397                 fprintf(stderr, "%s:%d protocol error\n", __FILE__, __LINE__);
1398             }
1399         }
1400     }
1401
1402 }
1403
1404
1405 static void
1406 ChangeView(int x, int z)
1407 {
1408     if (modifier_mask & Mod_Shift) {
1409         x *= 10;
1410         z *= 10;
1411     }
1412
1413     if (x) {
1414         surface_rot_x += x;
1415         if (surface_rot_x < 0)
1416             surface_rot_x = 0;
1417         if (surface_rot_x > 180)
1418             surface_rot_x = 180;
1419     }
1420     if (z) {
1421         surface_rot_z += z;
1422         if (surface_rot_z < 0)
1423             surface_rot_z += 360;
1424         if (surface_rot_z > 360)
1425             surface_rot_z -= 360;
1426     }
1427
1428     if (display_ipc_commands()) {
1429         fprintf(stderr, "changing view to %f, %f.\n", surface_rot_x, surface_rot_z);
1430     }
1431
1432     do_save_3dplot(first_3dplot, plot3d_num, 0 /* not quick */ );
1433
1434     if (ALMOST2D) {
1435         /* 2D plot, or suitably aligned 3D plot: update statusline */
1436         if (!term->put_tmptext)
1437             return;
1438         recalc_statusline();
1439     }
1440 }
1441
1442
1443 static void
1444 event_buttonpress(struct gp_event_t *ge)
1445 {
1446     int b;
1447
1448     motion = 0;
1449
1450     b = ge->par1;
1451     mouse_x = ge->mx;
1452     mouse_y = ge->my;
1453
1454     button |= (1 << b);
1455
1456     FPRINTF((stderr, "(event_buttonpress) mouse_x = %d\tmouse_y = %d\n", mouse_x, mouse_y));
1457
1458     MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
1459
1460     if (ALMOST2D) {
1461         if (!setting_zoom_region) {
1462             if (1 == b) {
1463                 /* not bound in 2d graphs */
1464             } else if (2 == b) {
1465                 /* not bound in 2d graphs */
1466             } else if (3 == b && !replot_disabled && !(paused_for_mouse & PAUSE_BUTTON3)) {
1467                 /* start zoom; but ignore it when
1468                  *   - replot is disabled, e.g. with inline data, or
1469                  *   - during 'pause mouse'
1470                  * allow zooming during 'pause mouse key' */
1471                 setting_zoom_x = mouse_x;
1472                 setting_zoom_y = mouse_y;
1473                 setting_zoom_region = TRUE;
1474                 if (term->set_cursor) {
1475                     int mv_mouse_x, mv_mouse_y;
1476                     if (mouse_setting.annotate_zoom_box && term->put_tmptext) {
1477                         double real_x, real_y, real_x2, real_y2;
1478                         char s[64];
1479                         /* tell driver annotations */
1480                         MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
1481                         sprintf(s, zoombox_format(), real_x, real_y);
1482                         term->put_tmptext(1, s);
1483                         term->put_tmptext(2, s);
1484                     }
1485                     /* displace mouse in order not to start with an empty zoom box */
1486                     mv_mouse_x = term->xmax / 20;
1487                     mv_mouse_y = (term->xmax == term->ymax) ? mv_mouse_x : (int) ((mv_mouse_x * (double) term->ymax) / term->xmax);
1488                     mv_mouse_x += mouse_x;
1489                     mv_mouse_y += mouse_y;
1490
1491                     /* change cursor type */
1492                     term->set_cursor(3, 0, 0);
1493
1494                     /* warp pointer */
1495                     if (mouse_setting.warp_pointer)
1496                         term->set_cursor(-2, mv_mouse_x, mv_mouse_y);
1497
1498                     /* turn on the zoom box */
1499                     term->set_cursor(-1, setting_zoom_x, setting_zoom_y);
1500                 }
1501                 if (display_ipc_commands()) {
1502                     fprintf(stderr, "starting zoom region.\n");
1503                 }
1504             }
1505         } else {
1506
1507             /* complete zoom (any button
1508              * finishes zooming.) */
1509
1510             /* the following variables are used to check,
1511              * if the box is big enough to be considered
1512              * as zoom box. */
1513             int dist_x = setting_zoom_x - mouse_x;
1514             int dist_y = setting_zoom_y - mouse_y;
1515             int dist = sqrt((double)(dist_x * dist_x + dist_y * dist_y));
1516
1517             if (1 == b || 2 == b) {
1518                 /* zoom region is finished by the `wrong' button.
1519                  * `trap' the next button-release event so that
1520                  * it won't trigger the actions which are bound
1521                  * to these events.
1522                  */
1523                 trap_release = TRUE;
1524             }
1525
1526             if (term->set_cursor) {
1527                 term->set_cursor(0, 0, 0);
1528                 if (mouse_setting.annotate_zoom_box && term->put_tmptext) {
1529                     term->put_tmptext(1, "");
1530                     term->put_tmptext(2, "");
1531                 }
1532             }
1533
1534             if (dist > 10 /* more ore less arbitrary */ ) {
1535
1536                 double xmin, ymin, x2min, y2min;
1537                 double xmax, ymax, x2max, y2max;
1538
1539                 MousePosToGraphPosReal(setting_zoom_x, setting_zoom_y, &xmin, &ymin, &x2min, &y2min);
1540                 xmax = real_x;
1541                 x2max = real_x2;
1542                 ymax = real_y;
1543                 y2max = real_y2;
1544                 /* keep the axes (no)reversed as they are now */
1545 #define sgn(x) (x==0 ? 0 : (x>0 ? 1 : -1))
1546 #define rev(a1,a2,A) if (sgn(a2-a1) != sgn(axis_array[A].max-axis_array[A].min)) \
1547                             { double tmp = a1; a1 = a2; a2 = tmp; }
1548                 rev(xmin,  xmax,  FIRST_X_AXIS);
1549                 rev(ymin,  ymax,  FIRST_Y_AXIS);
1550                 rev(x2min, x2max, SECOND_X_AXIS);
1551                 rev(y2min, y2max, SECOND_Y_AXIS);
1552 #undef rev
1553 #undef sgn
1554                 do_zoom(xmin, ymin, x2min, y2min, xmax, ymax, x2max, y2max);
1555                 if (display_ipc_commands()) {
1556                     fprintf(stderr, "zoom region finished.\n");
1557                 }
1558             } else {
1559                 /* silently ignore a tiny zoom box. This might
1560                  * happen, if the user starts and finishes the
1561                  * zoom box at the same position. */
1562             }
1563             setting_zoom_region = FALSE;
1564         }
1565     } else {
1566         if (term->set_cursor) {
1567             if (button & (1 << 1))
1568                 term->set_cursor(1, 0, 0);
1569             else if (button & (1 << 2))
1570                 term->set_cursor(2, 0, 0);
1571         }
1572     }
1573     start_x = mouse_x;
1574     start_y = mouse_y;
1575     zero_rot_z = surface_rot_z + 360.0 * mouse_x / term->xmax;
1576     zero_rot_x = surface_rot_x - 180.0 * mouse_y / term->ymax;
1577 }
1578
1579
1580 static void
1581 event_buttonrelease(struct gp_event_t *ge)
1582 {
1583     int b, doubleclick;
1584
1585     b = ge->par1;
1586     mouse_x = ge->mx;
1587     mouse_y = ge->my;
1588     doubleclick = ge->par2;
1589
1590     button &= ~(1 << b);        /* remove button */
1591
1592     if (setting_zoom_region) {
1593         return;
1594     }
1595     if (TRUE == trap_release) {
1596         trap_release = FALSE;
1597         return;
1598     }
1599
1600     MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
1601
1602     FPRINTF((stderr, "MOUSE.C: doublclick=%i, set=%i, motion=%i, ALMOST2D=%i\n", (int) doubleclick, (int) mouse_setting.doubleclick,
1603              (int) motion, (int) ALMOST2D));
1604
1605     if (ALMOST2D) {
1606         char s0[256];
1607         if (b == 1 && term->set_clipboard && ((doubleclick <= mouse_setting.doubleclick)
1608                                               || !mouse_setting.doubleclick)) {
1609
1610             /* put coordinates to clipboard. For 3d plots this takes
1611              * only place, if the user didn't drag (rotate) the plot */
1612
1613             if (!is_3d_plot || !motion) {
1614                 GetAnnotateString(s0, real_x, real_y, clipboard_mode, clipboard_alt_string);
1615                 term->set_clipboard(s0);
1616                 if (display_ipc_commands()) {
1617                     fprintf(stderr, "put `%s' to clipboard.\n", s0);
1618                 }
1619             }
1620         }
1621         if (b == 2) {
1622
1623             /* draw temporary annotation or label. For 3d plots this takes
1624              * only place, if the user didn't drag (scale) the plot */
1625
1626             if (!is_3d_plot || !motion) {
1627
1628                 GetAnnotateString(s0, real_x, real_y, mouse_mode, mouse_alt_string);
1629                 if (mouse_setting.label) {
1630                     if (modifier_mask & Mod_Ctrl) {
1631                         remove_label(mouse_x, mouse_y);
1632                     } else {
1633                         put_label(s0, real_x, real_y);
1634                     }
1635                 } else {
1636                     int dx, dy;
1637                     int x = mouse_x;
1638                     int y = mouse_y;
1639                     dx = term->h_tic;
1640                     dy = term->v_tic;
1641                     (term->linewidth) (border_lp.l_width);
1642                     (term->linetype) (border_lp.l_type);
1643                     (term->move) (x - dx, y);
1644                     (term->vector) (x + dx, y);
1645                     (term->move) (x, y - dy);
1646                     (term->vector) (x, y + dy);
1647                     (term->justify_text) (LEFT);
1648                     (term->put_text) (x + dx / 2, y + dy / 2 + term->v_char / 3, s0);
1649                     (term->text) ();
1650                 }
1651             }
1652         }
1653     }
1654     if (is_3d_plot && (b == 1 || b == 2)) {
1655         if (!!(modifier_mask & Mod_Ctrl) && !needreplot) {
1656             /* redraw the 3d plot if its last redraw was 'quick'
1657              * (only axes) because modifier key was pressed */
1658             do_save_3dplot(first_3dplot, plot3d_num, 0);
1659         }
1660         if (term->set_cursor)
1661             term->set_cursor(0, 0, 0);
1662     }
1663
1664     /* Export current mouse coords to user-accessible variables also */
1665     load_mouse_variables(mouse_x, mouse_y, TRUE, b);
1666     UpdateStatusline();
1667
1668 #ifdef _Windows
1669     if (paused_for_mouse & PAUSE_CLICK) {
1670         /* remove pause message box after 'pause mouse' */
1671         paused_for_mouse = 0;
1672         kill_pending_Pause_dialog();
1673     }
1674 #endif
1675 }
1676
1677 static void
1678 event_motion(struct gp_event_t *ge)
1679 {
1680     motion = 1;
1681
1682     mouse_x = ge->mx;
1683     mouse_y = ge->my;
1684
1685     if (is_3d_plot
1686         && (splot_map == FALSE) /* Rotate the surface if it is 3D graph but not "set view map". */
1687         ) {
1688
1689         TBOOLEAN redraw = FALSE;
1690
1691         if (button & (1 << 1)) {
1692             /* dragging with button 1 -> rotate */
1693 #if 0                           /* HBB 20001109: what's rint()? */
1694             surface_rot_x = rint(zero_rot_x + 180.0 * mouse_y / term->ymax);
1695 #else
1696             surface_rot_x = floor(0.5 + zero_rot_x + 180.0 * mouse_y / term->ymax);
1697 #endif
1698             if (surface_rot_x < 0)
1699                 surface_rot_x = 0;
1700             if (surface_rot_x > 180)
1701                 surface_rot_x = 180;
1702 #if 0                           /* HBB 20001109: what's rint()? */
1703             surface_rot_z = rint(fmod(zero_rot_z - 360.0 * mouse_x / term->xmax, 360));
1704 #else
1705             surface_rot_z = floor(0.5 + fmod(zero_rot_z - 360.0 * mouse_x / term->xmax, 360));
1706 #endif
1707             if (surface_rot_z < 0)
1708                 surface_rot_z += 360;
1709             redraw = TRUE;
1710         } else if (button & (1 << 2)) {
1711             /* dragging with button 2 -> scale or changing ticslevel.
1712              * we compare the movement in x and y direction, and
1713              * change either scale or zscale */
1714             double relx, rely;
1715             relx = fabs(mouse_x - start_x) / term->h_tic;
1716             rely = fabs(mouse_y - start_y) / term->v_tic;
1717
1718 # if 0
1719             /* threshold: motion should be at least 3 pixels.
1720              * We've to experiment with this. */
1721             if (relx < 3 && rely < 3)
1722                 return;
1723 # endif
1724             if (modifier_mask & Mod_Shift) {
1725                 xyplane.ticslevel += (1 + fabs(xyplane.ticslevel))
1726                     * (mouse_y - start_y) * 2.0 / term->ymax;
1727             } else {
1728
1729                 if (relx > rely) {
1730                     surface_scale += (mouse_x - start_x) * 2.0 / term->xmax;
1731                     if (surface_scale < 0)
1732                         surface_scale = 0;
1733                 } else {
1734                     if (disable_mouse_z && (mouse_y-start_y > 0))
1735                         ;
1736                     else {
1737                         surface_zscale += (mouse_y - start_y) * 2.0 / term->ymax;
1738                         disable_mouse_z = FALSE;
1739                     }
1740                     if (surface_zscale < 0)
1741                         surface_zscale = 0;
1742                 }
1743             }
1744             /* reset the start values */
1745             start_x = mouse_x;
1746             start_y = mouse_y;
1747             redraw = TRUE;
1748         } /* if (mousebutton 2 is down) */
1749
1750         if (!ALMOST2D) {
1751             turn_ruler_off();
1752         }
1753
1754         if (redraw) {
1755             if (allowmotion) {
1756                 /* is processing of motions allowed right now?
1757                  * then replot while
1758                  * disabling further replots until it completes */
1759                 allowmotion = FALSE;
1760                 do_save_3dplot(first_3dplot, plot3d_num, !!(modifier_mask & Mod_Ctrl));
1761             } else {
1762                 /* postpone the replotting */
1763                 needreplot = TRUE;
1764             }
1765         }
1766     } /* if (3D plot) */
1767
1768
1769     if (ALMOST2D) {
1770         /* 2D plot, or suitably aligned 3D plot: update
1771          * statusline and possibly the zoombox annotation */
1772         if (!term->put_tmptext)
1773             return;
1774         MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
1775         UpdateStatusline();
1776
1777         if (setting_zoom_region && mouse_setting.annotate_zoom_box) {
1778             double real_x, real_y, real_x2, real_y2;
1779             char s[64];
1780             MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
1781             sprintf(s, zoombox_format(), real_x, real_y);
1782             term->put_tmptext(2, s);
1783         }
1784     }
1785 }
1786
1787
1788 static void
1789 event_modifier(struct gp_event_t *ge)
1790 {
1791     modifier_mask = ge->par1;
1792
1793     if (modifier_mask == 0 && is_3d_plot && (button & ((1 << 1) | (1 << 2))) && !needreplot) {
1794         /* redraw the 3d plot if modifier key released */
1795         do_save_3dplot(first_3dplot, plot3d_num, 0);
1796     }
1797 }
1798
1799
1800 void
1801 event_plotdone()
1802 {
1803     if (needreplot) {
1804         needreplot = FALSE;
1805         do_save_3dplot(first_3dplot, plot3d_num, !!(modifier_mask & Mod_Ctrl));
1806     } else {
1807         allowmotion = TRUE;
1808     }
1809 }
1810
1811
1812 void
1813 event_reset(struct gp_event_t *ge)
1814 {
1815     modifier_mask = 0;
1816     button = 0;
1817     builtin_cancel_zoom(ge);
1818     if (term && term->set_cursor) {
1819         term->set_cursor(0, 0, 0);
1820         if (mouse_setting.annotate_zoom_box && term->put_tmptext) {
1821             term->put_tmptext(1, "");
1822             term->put_tmptext(2, "");
1823         }
1824     }
1825
1826     if (paused_for_mouse) {
1827         paused_for_mouse = 0;
1828 #ifdef _Windows
1829         /* remove pause message box after 'pause mouse' */
1830         kill_pending_Pause_dialog();
1831 #endif
1832         /* This hack is necessary on some systems in order to prevent one  */
1833         /* character of input from being swallowed when the plot window is */
1834         /* closed. But which systems, exactly?                             */
1835         if (term && (!strncmp("x11",term->name,3) || !strncmp("wxt",term->name,3)))
1836             ungetc('\n',stdin);
1837     }
1838 }
1839
1840 void
1841 do_event(struct gp_event_t *ge)
1842 {
1843     if (!term)
1844         return;
1845
1846     if (multiplot && ge->type != GE_fontprops)
1847         /* only informational event processing for multiplot */
1848         return;
1849
1850     /* disable `replot` when some data were sent through stdin */
1851     replot_disabled = plotted_data_from_stdin;
1852
1853     if (ge->type) {
1854         FPRINTF((stderr, "(do_event) type       = %d\n", ge->type));
1855         FPRINTF((stderr, "           mx, my     = %d, %d\n", ge->mx, ge->my));
1856         FPRINTF((stderr, "           par1, par2 = %d, %d\n", ge->par1, ge->par2));
1857     }
1858
1859     switch (ge->type) {
1860     case GE_plotdone:
1861         event_plotdone();
1862         break;
1863     case GE_keypress:
1864         event_keypress(ge, TRUE);
1865         break;
1866     case GE_keypress_old:
1867         event_keypress(ge, FALSE);
1868         break;
1869     case GE_modifier:
1870         event_modifier(ge);
1871         break;
1872     case GE_motion:
1873         if (!mouse_setting.on)
1874             break;
1875         event_motion(ge);
1876         break;
1877     case GE_buttonpress:
1878         if (!mouse_setting.on)
1879             break;
1880         event_buttonpress(ge);
1881         break;
1882     case GE_buttonrelease:
1883         if (!mouse_setting.on)
1884             break;
1885         event_buttonrelease(ge);
1886         break;
1887     case GE_replot:
1888         /* used only by ggi.trm */
1889         do_string("replot");
1890         break;
1891     case GE_reset:
1892         event_reset(ge);
1893         break;
1894     case GE_fontprops:
1895         term->h_char = ge->par1;
1896         term->v_char = ge->par2;
1897         /* Update aspect ratio based on current window size */
1898         term->v_tic = term->h_tic * (double)ge->mx / (double)ge->my;
1899         /* EAM FIXME - We could also update term->xmax and term->ymax here, */
1900         /*             but the existing code doesn't expect it to change.   */
1901         FPRINTF((stderr, "mouse do_event: window size %d X %d, font hchar %d vchar %d\n",
1902                 ge->mx, ge->my, ge->par1,ge->par2));
1903         break;
1904     default:
1905         fprintf(stderr, "%s:%d protocol error\n", __FILE__, __LINE__);
1906         break;
1907     }
1908
1909     replot_disabled = FALSE;    /* enable replot again */
1910 }
1911
1912 static void
1913 do_save_3dplot(struct surface_points *plots, int pcount, int quick)
1914 {
1915 #define M_TEST_AXIS(A) \
1916      (A.log && ((!(A.set_autoscale & AUTOSCALE_MIN) && A.set_min <= 0) || \
1917                 (!(A.set_autoscale & AUTOSCALE_MAX) && A.set_max <= 0)))
1918
1919     if (!plots) {
1920         /* this might happen after the `reset' command for example
1921          * which was reported by Franz Bakan.  replotrequest()
1922          * should set up again everything. */
1923         replotrequest();
1924     } else {
1925         if (M_TEST_AXIS(X_AXIS) || M_TEST_AXIS(Y_AXIS) || M_TEST_AXIS(Z_AXIS)
1926             || M_TEST_AXIS(CB_AXIS)
1927             ) {
1928                 graph_error("axis ranges must be above 0 for log scale!");
1929                 return;
1930         }
1931         do_3dplot(plots, pcount, quick);
1932     }
1933
1934 #undef M_TEST_AXIS
1935 }
1936
1937
1938 /*
1939  * bind related functions
1940  */
1941
1942 static void
1943 bind_install_default_bindings()
1944 {
1945     bind_remove_all();
1946     bind_append("a", (char *) 0, builtin_autoscale);
1947     bind_append("b", (char *) 0, builtin_toggle_border);
1948     bind_append("e", (char *) 0, builtin_replot);
1949     bind_append("g", (char *) 0, builtin_toggle_grid);
1950     bind_append("h", (char *) 0, builtin_help);
1951     bind_append("l", (char *) 0, builtin_toggle_log);
1952     bind_append("L", (char *) 0, builtin_nearest_log);
1953     bind_append("m", (char *) 0, builtin_toggle_mouse);
1954     bind_append("r", (char *) 0, builtin_toggle_ruler);
1955     bind_append("1", (char *) 0, builtin_decrement_mousemode);
1956     bind_append("2", (char *) 0, builtin_increment_mousemode);
1957     bind_append("3", (char *) 0, builtin_decrement_clipboardmode);
1958     bind_append("4", (char *) 0, builtin_increment_clipboardmode);
1959     bind_append("5", (char *) 0, builtin_toggle_polardistance);
1960     bind_append("6", (char *) 0, builtin_toggle_verbose);
1961     bind_append("7", (char *) 0, builtin_toggle_ratio);
1962     bind_append("n", (char *) 0, builtin_zoom_next);
1963     bind_append("p", (char *) 0, builtin_zoom_previous);
1964     bind_append("u", (char *) 0, builtin_unzoom);
1965     bind_append("Right", (char *) 0, builtin_rotate_right);
1966     bind_append("Up", (char *) 0, builtin_rotate_up);
1967     bind_append("Left", (char *) 0, builtin_rotate_left);
1968     bind_append("Down", (char *) 0, builtin_rotate_down);
1969     bind_append("Escape", (char *) 0, builtin_cancel_zoom);
1970 }
1971
1972 static void
1973 bind_clear(bind_t * b)
1974 {
1975     b->key = NO_KEY;
1976     b->modifier = 0;
1977     b->command = (char *) 0;
1978     b->builtin = 0;
1979     b->prev = (struct bind_t *) 0;
1980     b->next = (struct bind_t *) 0;
1981 }
1982
1983 /* returns the enum which corresponds to the
1984  * string (ptr) or NO_KEY if ptr matches not
1985  * any of special_keys. */
1986 static int
1987 lookup_key(char *ptr, int *len)
1988 {
1989     char **keyptr;
1990     /* first, search in the table of "usual well-known" keys */
1991     int what = lookup_table_nth(usual_special_keys, ptr);
1992     if (what >= 0) {
1993         *len = strlen(usual_special_keys[what].key);
1994         return usual_special_keys[what].value;
1995     }
1996     /* second, search in the table of other keys */
1997     for (keyptr = special_keys; *keyptr; ++keyptr) {
1998         if (!strcmp(ptr, *keyptr)) {
1999             *len = strlen(ptr);
2000             return keyptr - special_keys + GP_FIRST_KEY;
2001         }
2002     }
2003     return NO_KEY;
2004 }
2005
2006 /* returns 1 on success, else 0. */
2007 static int
2008 bind_scan_lhs(bind_t * out, const char *in)
2009 {
2010     static const char DELIM = '-';
2011     int itmp = NO_KEY;
2012     char *ptr;
2013     int len;
2014     bind_clear(out);
2015     if (!in) {
2016         return 0;
2017     }
2018     for (ptr = (char *) in; ptr && *ptr; /* EMPTY */ ) {
2019         if (!strncasecmp(ptr, "alt-", 4)) {
2020             out->modifier |= Mod_Alt;
2021             ptr += 4;
2022         } else if (!strncasecmp(ptr, "ctrl-", 5)) {
2023             out->modifier |= Mod_Ctrl;
2024             ptr += 5;
2025         } else if (NO_KEY != (itmp = lookup_key(ptr, &len))) {
2026             out->key = itmp;
2027             ptr += len;
2028         } else if ((out->key = *ptr++) && *ptr && *ptr != DELIM) {
2029             fprintf(stderr, "bind: cannot parse %s\n", in);
2030             return 0;
2031         }
2032     }
2033     if (NO_KEY == out->key)
2034         return 0;               /* failed */
2035     else
2036         return 1;               /* success */
2037 }
2038
2039 /* note, that this returns a pointer
2040  * to the static char* `out' which is
2041  * modified on subsequent calls.
2042  */
2043 static char *
2044 bind_fmt_lhs(const bind_t * in)
2045 {
2046     static char out[0x40];
2047     out[0] = '\0';              /* empty string */
2048     if (!in)
2049         return out;
2050     if (in->modifier & Mod_Ctrl) {
2051         sprintf(out, "Ctrl-");
2052     }
2053     if (in->modifier & Mod_Alt) {
2054         sprintf(out, "%sAlt-", out);
2055     }
2056     if (in->key > GP_FIRST_KEY && in->key < GP_LAST_KEY) {
2057         sprintf(out, "%s%s", out, special_keys[in->key - GP_FIRST_KEY]);
2058     } else {
2059         int k = 0;
2060         for ( ; usual_special_keys[k].value > 0; k++) {
2061             if (usual_special_keys[k].value == in->key) {
2062                 sprintf(out, "%s%s", out, usual_special_keys[k].key);
2063                 k = -1;
2064                 break;
2065             }
2066         }
2067         if (k >= 0)
2068         sprintf(out, "%s%c", out, in->key);
2069     }
2070     return out;
2071 }
2072
2073 static int
2074 bind_matches(const bind_t * a, const bind_t * b)
2075 {
2076     /* discard Shift modifier */
2077     int a_mod = a->modifier & (Mod_Ctrl | Mod_Alt);
2078     int b_mod = b->modifier & (Mod_Ctrl | Mod_Alt);
2079
2080     if (a->key == b->key && a_mod == b_mod)
2081         return 1;
2082     else
2083         return 0;
2084 }
2085
2086 static void
2087 bind_display_one(bind_t * ptr)
2088 {
2089     fprintf(stderr, " %-12s ", bind_fmt_lhs(ptr));
2090     fprintf(stderr, "%c ", ptr->allwindows ? '*' : ' ');
2091     if (ptr->command) {
2092         fprintf(stderr, "`%s'\n", ptr->command);
2093     } else if (ptr->builtin) {
2094         fprintf(stderr, "%s\n", ptr->builtin(0));
2095     } else {
2096         fprintf(stderr, "`%s:%d oops.'\n", __FILE__, __LINE__);
2097     }
2098 }
2099
2100 static void
2101 bind_display(char *lhs)
2102 {
2103     bind_t *ptr;
2104     bind_t lhs_scanned;
2105
2106     if (!bindings) {
2107         bind_install_default_bindings();
2108     }
2109
2110     if (!lhs) {
2111         /* display all bindings */
2112         char fmt[] = " %-17s  %s\n";
2113         fprintf(stderr, "\n");
2114         fprintf(stderr, fmt, "2x<B1>",
2115                 "print coordinates to clipboard using `clipboardformat`\n                    (see keys '3', '4')");
2116         fprintf(stderr, fmt, "<B2>", "annotate the graph using `mouseformat` (see keys '1', '2')");
2117         fprintf(stderr, fmt, "", "or draw labels if `set mouse labels is on`");
2118         fprintf(stderr, fmt, "<Ctrl-B2>", "remove label close to pointer if `set mouse labels` is on");
2119         fprintf(stderr, fmt, "<B3>", "mark zoom region (only for 2d-plots and maps).");
2120         fprintf(stderr, fmt, "<B1-Motion>", "change view (rotation). Use <ctrl> to rotate the axes only.");
2121         fprintf(stderr, fmt, "<B2-Motion>", "change view (scaling). Use <ctrl> to scale the axes only.");
2122         fprintf(stderr, fmt, "<Shift-B2-Motion>", "vertical motion -- change xyplane");
2123         fprintf(stderr, "\n");
2124         fprintf(stderr, " %-12s   %s\n", "Space", "raise gnuplot console window");
2125         fprintf(stderr, " %-12s * %s\n", "q", "close this X11 plot window");
2126         fprintf(stderr, "\n");
2127         for (ptr = bindings; ptr; ptr = ptr->next) {
2128             bind_display_one(ptr);
2129         }
2130         fprintf(stderr, "\n");
2131         fprintf(stderr, "              * indicates this key is active from all plot windows\n");
2132         fprintf(stderr, "\n");
2133         return;
2134     }
2135
2136     if (!bind_scan_lhs(&lhs_scanned, lhs)) {
2137         return;
2138     }
2139     for (ptr = bindings; ptr; ptr = ptr->next) {
2140         if (bind_matches(&lhs_scanned, ptr)) {
2141             bind_display_one(ptr);
2142             break;              /* only one match */
2143         }
2144     }
2145 }
2146
2147 static void
2148 bind_remove(bind_t * b)
2149 {
2150     if (!b) {
2151         return;
2152     } else if (b->builtin) {
2153         /* don't remove builtins, just remove the overriding command */
2154         if (b->command) {
2155             free(b->command);
2156             b->command = (char *) 0;
2157         }
2158         return;
2159     }
2160     if (b->prev)
2161         b->prev->next = b->next;
2162     if (b->next)
2163         b->next->prev = b->prev;
2164     else
2165         bindings->prev = b->prev;
2166     if (b->command) {
2167         free(b->command);
2168         b->command = (char *) 0;
2169     }
2170     if (b == bindings) {
2171         bindings = b->next;
2172         if (bindings && bindings->prev) {
2173             bindings->prev->next = (bind_t *) 0;
2174         }
2175     }
2176     free(b);
2177 }
2178
2179 static void
2180 bind_append(char *lhs, char *rhs, char *(*builtin) (struct gp_event_t * ge))
2181 {
2182     bind_t *new = (bind_t *) gp_alloc(sizeof(bind_t), "bind_append->new");
2183
2184     if (!bind_scan_lhs(new, lhs)) {
2185         free(new);
2186         return;
2187     }
2188
2189     if (!bindings) {
2190         /* first binding */
2191         bindings = new;
2192     } else {
2193
2194         bind_t *ptr;
2195         for (ptr = bindings; ptr; ptr = ptr->next) {
2196             if (bind_matches(new, ptr)) {
2197                 /* overwriting existing binding */
2198                 if (!rhs) {
2199                     ptr->builtin = builtin;
2200                 } else if (*rhs) {
2201                     if (ptr->command) {
2202                         free(ptr->command);
2203                         ptr->command = (char *) 0;
2204                     }
2205                     ptr->command = rhs;
2206                 } else {        /* rhs is an empty string, so remove the binding */
2207                     bind_remove(ptr);
2208                 }
2209                 free(new);      /* don't need it any more */
2210                 return;
2211             }
2212         }
2213         /* if we're here, the binding does not exist yet */
2214         /* append binding ... */
2215         bindings->prev->next = new;
2216         new->prev = bindings->prev;
2217     }
2218
2219     bindings->prev = new;
2220     new->next = (struct bind_t *) 0;
2221     new->allwindows = FALSE;    /* Can be explicitly set later */
2222     if (!rhs) {
2223         new->builtin = builtin;
2224     } else if (*rhs) {
2225         new->command = rhs;     /* was allocated in command.c */
2226     } else {
2227         bind_remove(new);
2228     }
2229 }
2230
2231 void
2232 bind_process(char *lhs, char *rhs, TBOOLEAN allwindows)
2233 {
2234     if (!bindings) {
2235         bind_install_default_bindings();
2236     }
2237     if (!rhs) {
2238         bind_display(lhs);
2239     } else {
2240         bind_append(lhs, rhs, 0);
2241         if (allwindows)
2242             bind_all(lhs);
2243     }
2244     if (lhs)
2245         free(lhs);
2246 }
2247
2248 void
2249 bind_all(char *lhs)
2250 {
2251     bind_t *ptr;
2252     bind_t keypress;
2253
2254     if (!bind_scan_lhs(&keypress, lhs))
2255         return;
2256
2257     for (ptr = bindings; ptr; ptr = ptr->next) {
2258         if (bind_matches(&keypress, ptr))
2259             ptr->allwindows = TRUE;
2260     }
2261 }
2262
2263 void
2264 bind_remove_all()
2265 {
2266     bind_t *ptr;
2267     bind_t *safe;
2268     for (ptr = bindings; ptr; safe = ptr, ptr = ptr->next, free(safe)) {
2269         if (ptr->command) {
2270             free(ptr->command);
2271             ptr->command = (char *) 0;
2272         }
2273     }
2274     bindings = (bind_t *) 0;
2275 }
2276
2277 /* Ruler is on, thus recalc its (px,py) from (x,y) for the current zoom and
2278    log axes.
2279 */
2280 static void
2281 recalc_ruler_pos()
2282 {
2283     double P, dummy;
2284     if (is_3d_plot) {
2285         /* To be exact, it is 'set view map' splot. */
2286         unsigned int ppx, ppy;
2287         dummy = 1.0; /* dummy value, but not 0.0 for the fear of log z-axis */
2288         map3d_xy(ruler.x, ruler.y, dummy, &ppx, &ppy);
2289         ruler.px = ppx;
2290         ruler.py = ppy;
2291         return;
2292     }
2293     /* It is 2D plot. */
2294     if (axis_array[FIRST_X_AXIS].log && ruler.x < 0)
2295         ruler.px = -1;
2296     else {
2297         P = AXIS_LOG_VALUE(FIRST_X_AXIS, ruler.x);
2298         ruler.px = AXIS_MAP(FIRST_X_AXIS, P);
2299     }
2300     if (axis_array[FIRST_Y_AXIS].log && ruler.y < 0)
2301         ruler.py = -1;
2302     else {
2303         P = AXIS_LOG_VALUE(FIRST_Y_AXIS, ruler.y);
2304         ruler.py = AXIS_MAP(FIRST_Y_AXIS, P);
2305     }
2306     MousePosToGraphPosReal(ruler.px, ruler.py, &dummy, &dummy, &ruler.x2, &ruler.y2);
2307 }
2308
2309
2310 /* Recalculate and replot the ruler after a '(re)plot'. Called from term.c.
2311 */
2312 void
2313 update_ruler()
2314 {
2315     if (!term->set_ruler || !ruler.on)
2316         return;
2317     (*term->set_ruler) (-1, -1);
2318     recalc_ruler_pos();
2319     (*term->set_ruler) (ruler.px, ruler.py);
2320 }
2321
2322 /* Set ruler on/off, and set its position.
2323    Called from set.c for 'set mouse ruler ...' command.
2324 */
2325 void
2326 set_ruler(TBOOLEAN on, int mx, int my)
2327 {
2328     struct gp_event_t ge;
2329     if (ruler.on == FALSE && on == FALSE)
2330         return;
2331     if (ruler.on == TRUE && on == TRUE && (mx < 0 || my < 0))
2332         return;
2333     if (ruler.on == TRUE) /* ruler is on => switch it off */
2334         builtin_toggle_ruler(&ge);
2335     /* now the ruler is off */
2336     if (on == FALSE) /* want ruler off */
2337         return;
2338     if (mx>=0 && my>=0) { /* change ruler position */
2339         ge.mx = mx;
2340         ge.my = my;
2341     } else { /* don't change ruler position */
2342         ge.mx = ruler.px;
2343         ge.my = ruler.py;
2344     }
2345     builtin_toggle_ruler(&ge);
2346 }
2347
2348 /* for checking if we change from plot to splot (or vice versa) */
2349 int
2350 plot_mode(int set)
2351 {
2352     static int mode = MODE_PLOT;
2353     if (MODE_PLOT == set || MODE_SPLOT == set) {
2354         if (mode != set) {
2355             turn_ruler_off();
2356         }
2357         mode = set;
2358     }
2359     return mode;
2360 }
2361
2362 static void
2363 turn_ruler_off()
2364 {
2365     if (ruler.on) {
2366         struct udvt_entry *u;
2367         ruler.on = FALSE;
2368         if (term && term->set_ruler) {
2369             (*term->set_ruler) (-1, -1);
2370         }
2371         if ((u = add_udv_by_name("MOUSE_RULER_X")))
2372             u->udv_undef = TRUE;
2373         if ((u = add_udv_by_name("MOUSE_RULER_Y")))
2374             u->udv_undef = TRUE;
2375         if (display_ipc_commands()) {
2376             fprintf(stderr, "turning ruler off.\n");
2377         }
2378     }
2379 }
2380
2381 static int
2382 nearest_label_tag(int xref, int yref, struct termentry *t)
2383 {
2384     double min = -1;
2385     int min_tag = -1;
2386     double diff_squared;
2387     int x, y;
2388     struct text_label *this_label;
2389     int xd;
2390     int yd;
2391
2392     for (this_label = first_label; this_label != NULL; this_label = this_label->next) {
2393         if (is_3d_plot) {
2394             map3d_position(&this_label->place, &xd, &yd, "label");
2395             xd -= xref;
2396             yd -= yref;
2397         } else {
2398             map_position(&this_label->place, &x, &y, "label");
2399             xd = x - xref;
2400             yd = y - yref;
2401         }
2402         diff_squared = xd * xd + yd * yd;
2403         if (-1 == min || min > diff_squared) {
2404             /* now we check if we're within a certain
2405              * threshold around the label */
2406             double tic_diff_squared;
2407             int htic, vtic;
2408             get_offsets(this_label, t, &htic, &vtic);
2409             tic_diff_squared = htic * htic + vtic * vtic;
2410             if (diff_squared < tic_diff_squared) {
2411                 min = diff_squared;
2412                 min_tag = this_label->tag;
2413             }
2414         }
2415     }
2416
2417     return min_tag;
2418 }
2419
2420 static void
2421 remove_label(int x, int y)
2422 {
2423     int tag = nearest_label_tag(x, y, term);
2424     if (-1 != tag) {
2425         char cmd[0x40];
2426         sprintf(cmd, "unset label %d", tag);
2427         do_string_replot(cmd);
2428     }
2429 }
2430
2431 static void
2432 put_label(char *label, double x, double y)
2433 {
2434     char cmd[0xff];
2435     sprintf(cmd, "set label \"%s\" at %g,%g %s", label, x, y, mouse_setting.labelopts);
2436     do_string_replot(cmd);
2437 }
2438
2439 #ifdef OS2
2440 /* routine required by pm.trm: fill in information needed for (un)checking
2441    menu items in the Presentation Manager terminal
2442 */
2443 void 
2444 PM_set_gpPMmenu __PROTO((struct t_gpPMmenu * gpPMmenu))
2445 {
2446     gpPMmenu->use_mouse = mouse_setting.on;
2447     if (zoom_now == NULL)
2448         gpPMmenu->where_zoom_queue = 0;
2449     else {
2450         gpPMmenu->where_zoom_queue = (zoom_now == zoom_head) ? 0 : 1;
2451         if (zoom_now->prev != NULL)
2452             gpPMmenu->where_zoom_queue |= 2;
2453         if (zoom_now->next != NULL)
2454             gpPMmenu->where_zoom_queue |= 4;
2455     }
2456     gpPMmenu->polar_distance = mouse_setting.polardistance;
2457 }
2458 #endif
2459
2460 /* Save current mouse position to user-accessible variables.
2461  * Save the keypress or mouse button that triggered this in MOUSE_KEY,
2462  * and define MOUSE_BUTTON if it was a button click.
2463  */
2464 static void
2465 load_mouse_variables(double x, double y, TBOOLEAN button, int c)
2466 {
2467     struct udvt_entry *current;
2468
2469     MousePosToGraphPosReal(x, y, &real_x, &real_y, &real_x2, &real_y2);
2470
2471     if ((current = add_udv_by_name("MOUSE_BUTTON"))) {
2472         current->udv_undef = !button;
2473         Ginteger(&current->udv_value, button?c:-1);
2474     }
2475     if ((current = add_udv_by_name("MOUSE_KEY"))) {
2476         current->udv_undef = FALSE;
2477         Ginteger(&current->udv_value,c);
2478     }
2479 #ifdef GP_STRING_VARS
2480     if ((current = add_udv_by_name("MOUSE_CHAR"))) {
2481         char *keychar = gp_alloc(2,"key_char");
2482         keychar[0] = c;
2483         keychar[1] = '\0';
2484         if (!current->udv_undef)
2485             gpfree_string(&current->udv_value);
2486         current->udv_undef = FALSE;
2487         Gstring(&current->udv_value,keychar);
2488     }
2489 #endif
2490
2491     if ((current = add_udv_by_name("MOUSE_X"))) {
2492         current->udv_undef = FALSE;
2493         Gcomplex(&current->udv_value,real_x,0);
2494     }
2495     if ((current = add_udv_by_name("MOUSE_Y"))) {
2496         current->udv_undef = FALSE;
2497         Gcomplex(&current->udv_value,real_y,0);
2498     }
2499     if ((current = add_udv_by_name("MOUSE_X2"))) {
2500         current->udv_undef = FALSE;
2501         Gcomplex(&current->udv_value,real_x2,0);
2502     }
2503     if ((current = add_udv_by_name("MOUSE_Y2"))) {
2504         current->udv_undef = FALSE;
2505         Gcomplex(&current->udv_value,real_y2,0);
2506     }
2507     if ((current = add_udv_by_name("MOUSE_SHIFT"))) {
2508         current->udv_undef = FALSE;
2509         Ginteger(&current->udv_value, modifier_mask & Mod_Shift);
2510     }
2511     if ((current = add_udv_by_name("MOUSE_ALT"))) {
2512         current->udv_undef = FALSE;
2513         Ginteger(&current->udv_value, modifier_mask & Mod_Alt);
2514     }
2515     if ((current = add_udv_by_name("MOUSE_CTRL"))) {
2516         current->udv_undef = FALSE;
2517         Ginteger(&current->udv_value, modifier_mask & Mod_Ctrl);
2518     }
2519     return;
2520 }
2521
2522 #endif /* USE_MOUSE */