1 /* GNUPLOT - mouse.c */
2 
3 /* driver independent mouse part. */
4 
5 /*[
6  * Permission to use, copy, and distribute this software and its
7  * documentation for any purpose with or without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation.
11  *
12  * Permission to modify the software is granted, but not the right to
13  * distribute the complete modified source code.  Modifications are to
14  * be distributed as patches to the released version.  Permission to
15  * distribute binaries produced by compiling modified sources is granted,
16  * provided you
17  *   1. distribute the corresponding source modifications from the
18  *    released version in the form of a patch file along with the binaries,
19  *   2. add special version identification to distinguish your version
20  *    in addition to the base release version number,
21  *   3. provide your name and address as the primary contact for the
22  *    support of your modified version, and
23  *   4. retain our contact information in regard to use of the base
24  *    software.
25  * Permission to distribute the released version of the source code along
26  * with corresponding source modifications in the form of a patch file is
27  * granted with same provisions 2 through 4 for binary distributions.
28  *
29  * This software is provided "as is" without express or implied warranty
30  * to the extent permitted by applicable law.
31 ]*/
32 
33 /*
34  * AUTHORS
35  *
36  *   Original Software (October 1999 - January 2000):
37  *     Pieter-Tjerk de Boer <ptdeboer@cs.utwente.nl>
38  *     Petr Mikulik <mikulik@physics.muni.cz>
39  *     Johannes Zellner <johannes@zellner.org>
40  */
41 
42 #include "syscfg.h"
43 #include "stdfn.h"
44 #include "gp_types.h"
45 
46 #define _MOUSE_C		/* FIXME HBB 20010207: violates Codestyle */
47 #ifdef USE_MOUSE		/* comment out whole file, otherwise... */
48 
49 #include "mouse.h"
50 #include "pm3d.h"
51 #include "alloc.h"
52 #include "axis.h"
53 #include "command.h"
54 #include "datafile.h"
55 #include "gadgets.h"
56 #include "gp_time.h"
57 #include "graphics.h"
58 #include "graph3d.h"
59 #include "plot3d.h"
60 #include "readline.h"
61 #include "term_api.h"
62 #include "util3d.h"
63 #include "hidden3d.h"
64 #include "plot.h"	/* for interactive */
65 
66 #ifdef _Windows
67 # include "win/winmain.h"
68 #endif
69 
70 #ifdef OS2
71 #include <os2.h>
72 #include "os2/pm_msgs.h"
73 #endif
74 
75 /********************** variables ***********************************************************/
76 char mouse_fmt_default[] = "% #g";
77 
78 mouse_setting_t default_mouse_setting = DEFAULT_MOUSE_SETTING;
79 mouse_setting_t         mouse_setting = DEFAULT_MOUSE_SETTING;
80 
81 /* "usual well-known" keycodes, i.e. those not listed in special_keys in mouse.h
82 */
83 static const struct gen_table usual_special_keys[] =
84 {
85     { "BackSpace", GP_BackSpace},
86     { "Tab", GP_Tab},
87     { "KP_Enter", GP_KP_Enter},
88     { "Return", GP_Return},
89     { "Escape", GP_Escape},
90     { "Delete", GP_Delete},
91     { NULL, 0}
92 };
93 
94 /* the status of the shift, ctrl and alt keys
95 */
96 static int modifier_mask = 0;
97 
98 /* Structure for the ruler: on/off, position,...
99  */
100 static struct {
101     TBOOLEAN on;
102     double x, y, x2, y2;	/* ruler position in real units of the graph */
103     long px, py;		/* ruler position in the viewport units */
104 } ruler = {
105     FALSE, 0, 0, 0, 0, 0, 0
106 };
107 
108 
109 /* the coordinates of the mouse cursor in gnuplot's internal coordinate system
110  */
111 static int mouse_x = -1, mouse_y = -1;
112 
113 
114 /* the "real" coordinates of the mouse cursor, i.e., in the user's coordinate
115  * system(s)
116  */
117 static double real_x, real_y, real_x2, real_y2;
118 
119 
120 /* status of buttons; button i corresponds to bit (1<<i) of this variable
121  */
122 static int button = 0;
123 
124 
125 /* variables for setting the zoom region:
126  */
127 /* flag, TRUE while user is outlining the zoom region */
128 static TBOOLEAN setting_zoom_region = FALSE;
129 /* coordinates of the first corner of the zoom region, in the internal
130  * coordinate system */
131 static int setting_zoom_x, setting_zoom_y;
132 
133 
134 /* variables for changing the 3D view:
135 */
136 /* do we allow motion to result in a replot right now? */
137 TBOOLEAN allowmotion = TRUE;	/* used by pm.trm, too */
138 /* did we already postpone a replot because allowmotion was FALSE ? */
139 static TBOOLEAN needreplot = FALSE;
140 /* mouse position when dragging started */
141 static int start_x, start_y;
142 /* ButtonPress sets this to 0, ButtonMotion to 1 */
143 static int motion = 0;
144 /* values for rot_x and rot_z corresponding to zero position of mouse */
145 static float zero_rot_x, zero_rot_z;
146 
147 
148 /* bind related stuff */
149 
150 typedef struct bind_t {
151     struct bind_t *prev;
152     int key;
153     char modifier;
154     char *command;
155     char *(*builtin) (struct gp_event_t * ge);
156     TBOOLEAN allwindows;
157     struct bind_t *next;
158 } bind_t;
159 
160 static bind_t *bindings = (bind_t *) 0;
161 static const int NO_KEY = -1;
162 static TBOOLEAN trap_release = FALSE;
163 
164 /* forward declarations */
165 static void alert __PROTO((void));
166 static void MousePosToGraphPosReal __PROTO((int xx, int yy, double *x, double *y, double *x2, double *y2));
167 static char *xy_format __PROTO((void));
168 static char *zoombox_format __PROTO((void));
169 static char *GetAnnotateString __PROTO((char *s, double x, double y, int mode, char *fmt));
170 static char *xDateTimeFormat __PROTO((double x, char *b, int mode));
171 static void GetRulerString __PROTO((char *p, double x, double y));
172 static void apply_zoom __PROTO((struct t_zoom * z));
173 static void do_zoom __PROTO((double xmin, double ymin, double x2min,
174 			     double y2min, double xmax, double ymax, double x2max, double y2max));
175 static void ZoomNext __PROTO((void));
176 static void ZoomPrevious __PROTO((void));
177 static void ZoomUnzoom __PROTO((void));
178 static void incr_mousemode __PROTO((const int amount));
179 static void UpdateStatuslineWithMouseSetting __PROTO((mouse_setting_t * ms));
180 
181 static void event_keypress __PROTO((struct gp_event_t * ge, TBOOLEAN current));
182 static void ChangeView __PROTO((int x, int z));
183 static void ChangeAzimuth __PROTO((int x));
184 static void event_buttonpress __PROTO((struct gp_event_t * ge));
185 static void event_buttonrelease __PROTO((struct gp_event_t * ge));
186 static void event_motion __PROTO((struct gp_event_t * ge));
187 static void event_modifier __PROTO((struct gp_event_t * ge));
188 static void do_save_3dplot __PROTO((struct surface_points *, int, int));
189 static void load_mouse_variables __PROTO((double, double, TBOOLEAN, int));
190 
191 static void do_zoom_in_around_mouse __PROTO((void));
192 static void do_zoom_out_around_mouse __PROTO((void));
193 static void do_zoom_in_X __PROTO((void));
194 static void do_zoom_out_X __PROTO((void));
195 static void do_zoom_scroll_up __PROTO((void));
196 static void do_zoom_scroll_down __PROTO((void));
197 static void do_zoom_scroll_left __PROTO((void));
198 static void do_zoom_scroll_right __PROTO((void));
199 
200 /* builtins */
201 static char *builtin_autoscale __PROTO((struct gp_event_t * ge));
202 static char *builtin_toggle_border __PROTO((struct gp_event_t * ge));
203 static char *builtin_replot __PROTO((struct gp_event_t * ge));
204 static char *builtin_toggle_grid __PROTO((struct gp_event_t * ge));
205 static char *builtin_help __PROTO((struct gp_event_t * ge));
206 static char *builtin_set_plots_visible __PROTO((struct gp_event_t * ge));
207 static char *builtin_set_plots_invisible __PROTO((struct gp_event_t * ge));
208 static char *builtin_invert_plot_visibilities __PROTO((struct gp_event_t * ge));
209 static char *builtin_toggle_log __PROTO((struct gp_event_t * ge));
210 static char *builtin_nearest_log __PROTO((struct gp_event_t * ge));
211 static char *builtin_toggle_mouse __PROTO((struct gp_event_t * ge));
212 static char *builtin_toggle_ruler __PROTO((struct gp_event_t * ge));
213 static char *builtin_decrement_mousemode __PROTO((struct gp_event_t * ge));
214 static char *builtin_increment_mousemode __PROTO((struct gp_event_t * ge));
215 static char *builtin_toggle_polardistance __PROTO((struct gp_event_t * ge));
216 static char *builtin_toggle_verbose __PROTO((struct gp_event_t * ge));
217 static char *builtin_toggle_ratio __PROTO((struct gp_event_t * ge));
218 static char *builtin_zoom_next __PROTO((struct gp_event_t * ge));
219 static char *builtin_zoom_previous __PROTO((struct gp_event_t * ge));
220 static char *builtin_unzoom __PROTO((struct gp_event_t * ge));
221 static char *builtin_rotate_right __PROTO((struct gp_event_t * ge));
222 static char *builtin_rotate_up __PROTO((struct gp_event_t * ge));
223 static char *builtin_rotate_left __PROTO((struct gp_event_t * ge));
224 static char *builtin_rotate_down __PROTO((struct gp_event_t * ge));
225 static char *builtin_azimuth_left __PROTO((struct gp_event_t * ge));
226 static char *builtin_azimuth_right __PROTO((struct gp_event_t * ge));
227 static char *builtin_cancel_zoom __PROTO((struct gp_event_t * ge));
228 static char *builtin_zoom_in_around_mouse __PROTO((struct gp_event_t * ge));
229 static char *builtin_zoom_out_around_mouse __PROTO((struct gp_event_t * ge));
230 #if (0)	/* Not currently used */
231 static char *builtin_zoom_scroll_left __PROTO((struct gp_event_t * ge));
232 static char *builtin_zoom_scroll_right __PROTO((struct gp_event_t * ge));
233 static char *builtin_zoom_scroll_up __PROTO((struct gp_event_t * ge));
234 static char *builtin_zoom_scroll_down __PROTO((struct gp_event_t * ge));
235 static char *builtin_zoom_in_X __PROTO((struct gp_event_t * ge));
236 static char *builtin_zoom_out_X __PROTO((struct gp_event_t * ge));
237 #endif
238 
239 /* prototypes for bind stuff
240  * which are used only here. */
241 static void bind_install_default_bindings __PROTO((void));
242 static void bind_clear __PROTO((bind_t * b));
243 static int lookup_key __PROTO((char *ptr, int *len));
244 static int bind_scan_lhs __PROTO((bind_t * out, const char *in));
245 static char *bind_fmt_lhs __PROTO((const bind_t * in));
246 static int bind_matches __PROTO((const bind_t * a, const bind_t * b));
247 static void bind_display_one __PROTO((bind_t * ptr));
248 static void bind_display __PROTO((char *lhs));
249 static void bind_all __PROTO((char *lhs));
250 static void bind_remove __PROTO((bind_t * b));
251 static void bind_append __PROTO((char *lhs, char *rhs, char *(*builtin) (struct gp_event_t * ge)));
252 static void recalc_ruler_pos __PROTO((void));
253 static void turn_ruler_off __PROTO((void));
254 static int nearest_label_tag __PROTO((int x, int y));
255 static void remove_label __PROTO((int x, int y));
256 static void put_label __PROTO((char *label, double x, double y));
257 
258 /********* functions ********************************************/
259 
260 /* produce a beep */
261 static void
alert()262 alert()
263 {
264 # ifdef OS2
265     DosBeep(444, 111);
266 # else
267 #  ifdef HAVE_LIBREADLINE
268 #    if !defined(MISSING_RL_DING)
269         rl_ding();
270 #    endif
271     fflush(rl_outstream);
272 #  else
273     fprintf(stderr, "\a");
274 #  endif
275 # endif
276 }
277 
278 /* always include the prototype. The prototype might even not be
279  * declared if the system supports stpcpy(). E.g. on Linux I would
280  * have to define __USE_GNU before including string.h to get the
281  * prototype (joze) */
282 /* HBB 20001109: *BUT* if a prototype is there, this one may easily
283  * conflict with it... */
284 char *stpcpy __PROTO((char *s, const char *p));
285 
286 # ifndef HAVE_STPCPY
287 /* handy function for composing strings; note: some platforms may
288  * already provide it, how should we handle that? autoconf? -- ptdb */
289 char *
stpcpy(char * s,const char * p)290 stpcpy(char *s, const char *p)
291 {
292     strcpy(s, p);
293     return s + strlen(p);
294 }
295 # endif
296 
297 
298 /* main job of transformation, which is not device dependent
299 */
300 static void
MousePosToGraphPosReal(int xx,int yy,double * x,double * y,double * x2,double * y2)301 MousePosToGraphPosReal(int xx, int yy, double *x, double *y, double *x2, double *y2)
302 {
303     if (!is_3d_plot) {
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 	FPRINTF((stderr, "POS: xx=%i, yy=%i  =>  x=%g  y=%g\n", xx, yy, *x, *y));
317 
318     } else {
319 	/* for 3D plots, we treat the mouse position as if it is
320 	 * in the bottom plane, i.e., the plane of the x and y axis */
321 	/* note: at present, this projection is only correct if
322 	 * surface_rot_z is a multiple of 90 degrees! */
323 	/* HBB 20010522: added protection against division by zero
324 	 * for cases like 'set view 90,0' */
325 	xx -= axis3d_o_x;
326 	yy -= axis3d_o_y;
327 	if (abs(axis3d_x_dx) > abs(axis3d_x_dy)) {
328 	    *x = axis_array[FIRST_X_AXIS].min
329 		+ ((double) xx) / axis3d_x_dx * (axis_array[FIRST_X_AXIS].max -
330 						 axis_array[FIRST_X_AXIS].min);
331 	} else if (axis3d_x_dy != 0) {
332 	    *x = axis_array[FIRST_X_AXIS].min
333 		+ ((double) yy) / axis3d_x_dy * (axis_array[FIRST_X_AXIS].max -
334 						 axis_array[FIRST_X_AXIS].min);
335 	} else {
336 	    /* both diffs are zero (x axis points into the screen */
337 	    *x = VERYLARGE;
338 	}
339 
340 	if (abs(axis3d_y_dx) > abs(axis3d_y_dy)) {
341 	    *y = axis_array[FIRST_Y_AXIS].min
342 		+ ((double) xx) / axis3d_y_dx * (axis_array[FIRST_Y_AXIS].max -
343 						 axis_array[FIRST_Y_AXIS].min);
344 	} else if (axis3d_y_dy != 0) {
345 	    if (splot_map)
346 		*y = axis_array[FIRST_Y_AXIS].max
347 		    + ((double) yy) / axis3d_y_dy * (axis_array[FIRST_Y_AXIS].min -
348 						     axis_array[FIRST_Y_AXIS].max);
349 	    else
350 		*y = axis_array[FIRST_Y_AXIS].min
351 		    + ((double) yy) / axis3d_y_dy * (axis_array[FIRST_Y_AXIS].max -
352 						     axis_array[FIRST_Y_AXIS].min);
353 	} else {
354 	    /* both diffs are zero (y axis points into the screen */
355 	    *y = VERYLARGE;
356 	}
357 
358 	*x2 = *y2 = VERYLARGE;	/* protection */
359     }
360     /*
361        Note: there is plot_bounds.xleft+0.5 in "#define map_x" in graphics.c, which
362        makes no major impact here. It seems that the mistake of the real
363        coordinate is at about 0.5%, which corresponds to the screen resolution.
364        It would be better to round the distance to this resolution, and thus
365        *x = xmin + rounded-to-screen-resolution (xdistance)
366      */
367 
368     /* Now take into account possible log scales of x and y axes */
369     *x = AXIS_DE_LOG_VALUE(FIRST_X_AXIS, *x);
370     *y = AXIS_DE_LOG_VALUE(FIRST_Y_AXIS, *y);
371     if (!is_3d_plot) {
372 	*x2 = AXIS_DE_LOG_VALUE(SECOND_X_AXIS, *x2);
373 	*y2 = AXIS_DE_LOG_VALUE(SECOND_Y_AXIS, *y2);
374     }
375 
376     /* If x2 or y2 is linked to a primary axis via mapping function, apply it now */
377     if (!is_3d_plot) {
378 	AXIS *secondary = &axis_array[SECOND_X_AXIS];
379 	if (secondary->linked_to_primary && secondary->link_udf->at)
380 	    *x2 = eval_link_function(secondary, *x);
381 	secondary = &axis_array[SECOND_Y_AXIS];
382 	if (secondary->linked_to_primary && secondary->link_udf->at)
383 	    *y2 = eval_link_function(secondary, *y);
384     }
385 
386 #ifdef NONLINEAR_AXES
387     /* If x or y is linked to a (hidden) primary axis, it's a bit more complicated */
388     if (!is_3d_plot) {
389 	AXIS *secondary;
390 	secondary = &axis_array[FIRST_X_AXIS];
391 	if (secondary->linked_to_primary
392 	&&  secondary->linked_to_primary->index == -FIRST_X_AXIS) {
393 	    *x = axis_mapback(secondary->linked_to_primary, xx);
394 	    *x = eval_link_function(secondary, *x);
395 	}
396 	secondary = &axis_array[FIRST_Y_AXIS];
397 	if (secondary->linked_to_primary
398 	&&  secondary->linked_to_primary->index == -FIRST_Y_AXIS) {
399 	    *y = axis_mapback(secondary->linked_to_primary, yy);
400 	    *y = eval_link_function(secondary, *y);
401 	}
402 	secondary = &axis_array[SECOND_X_AXIS];
403 	if (secondary->linked_to_primary
404 	&&  secondary->linked_to_primary->index == -SECOND_X_AXIS) {
405 	    *x2 = axis_mapback(secondary->linked_to_primary, xx);
406 	    *x2 = eval_link_function(secondary, *x2);
407 	}
408 	secondary = &axis_array[SECOND_Y_AXIS];
409 	if (secondary->linked_to_primary
410 	&&  secondary->linked_to_primary->index == -SECOND_Y_AXIS) {
411 	    *y2 = axis_mapback(secondary->linked_to_primary, yy);
412 	    *y2 = eval_link_function(secondary, *y2);
413 	}
414     }
415 #endif
416 
417 }
418 
419 static char *
xy_format()420 xy_format()
421 {
422     static char format[64];
423     format[0] = NUL;
424     strncat(format, mouse_setting.fmt, 30);
425     strncat(format, ", ", 2);
426     strncat(format, mouse_setting.fmt, 30);
427     return format;
428 }
429 
430 static char *
zoombox_format()431 zoombox_format()
432 {
433     static char format[64];
434     format[0] = NUL;
435     strncat(format, mouse_setting.fmt, 30);
436     strncat(format, "\r", 2);
437     strncat(format, mouse_setting.fmt, 30);
438     return format;
439 }
440 
441 /* formats the information for an annotation (middle mouse button clicked)
442  */
443 static char *
GetAnnotateString(char * s,double x,double y,int mode,char * fmt)444 GetAnnotateString(char *s, double x, double y, int mode, char *fmt)
445 {
446     if (axis_array[FIRST_X_AXIS].datatype == DT_DMS
447     ||  axis_array[FIRST_Y_AXIS].datatype == DT_DMS) {
448 	static char dms_format[16];
449 	sprintf(dms_format, "%%D%s%%.2m'", degree_sign);
450 	if (axis_array[FIRST_X_AXIS].datatype == DT_DMS)
451 	    gstrdms(s, fmt ? fmt : dms_format, x);
452 	else
453 	    sprintf(s, mouse_setting.fmt, x);
454 	strcat(s,", ");
455 	s += strlen(s);
456 	if (axis_array[FIRST_Y_AXIS].datatype == DT_DMS)
457 	    gstrdms(s, fmt ? fmt : dms_format, y);
458 	else
459 	    sprintf(s, mouse_setting.fmt, y);
460 	s += strlen(s);
461     } else if (mode == MOUSE_COORDINATES_XDATE || mode == MOUSE_COORDINATES_XTIME || mode == MOUSE_COORDINATES_XDATETIME || mode == MOUSE_COORDINATES_TIMEFMT) {	/* time is on the x axis */
462 	char buf[0xff];
463 	char format[0xff] = "[%s, ";
464 	strcat(format, mouse_setting.fmt);
465 	strcat(format, "]");
466 	sprintf(s, format, xDateTimeFormat(x, buf, mode), y);
467     } else if (mode == MOUSE_COORDINATES_FRACTIONAL) {
468 	double xrange = axis_array[FIRST_X_AXIS].max - axis_array[FIRST_X_AXIS].min;
469 	double yrange = axis_array[FIRST_Y_AXIS].max - axis_array[FIRST_Y_AXIS].min;
470 	/* calculate fractional coordinates.
471 	 * prevent division by zero */
472 	if (xrange) {
473 	    char format[0xff] = "/";
474 	    strcat(format, mouse_setting.fmt);
475 	    sprintf(s, format, (x - axis_array[FIRST_X_AXIS].min) / xrange);
476 	} else {
477 	    sprintf(s, "/(undefined)");
478 	}
479 	s += strlen(s);
480 	if (yrange) {
481 	    char format[0xff] = ", ";
482 	    strcat(format, mouse_setting.fmt);
483 	    strcat(format, "/");
484 	    sprintf(s, format, (y - axis_array[FIRST_Y_AXIS].min) / yrange);
485 	} else {
486 	    sprintf(s, ", (undefined)/");
487 	}
488     } else if (mode == MOUSE_COORDINATES_REAL1) {
489 	sprintf(s, xy_format(), x, y);	/* w/o brackets */
490     } else if (mode == MOUSE_COORDINATES_ALT && (fmt || polar)) {
491 	if (polar) {
492 	    double r;
493 	    double phi = atan2(y,x);
494 	    double rmin = (R_AXIS.autoscale & AUTOSCALE_MIN) ? 0.0 : R_AXIS.set_min;
495 	    double theta = phi / DEG2RAD;
496 
497 	    /* Undo "set theta" */
498 	    theta = (theta - theta_origin) * theta_direction;
499 	    if (theta > 180.)
500 		theta = theta - 360.;
501 
502 	    if (nonlinear(&R_AXIS))
503 		r = eval_link_function(&R_AXIS, x/cos(phi) + R_AXIS.linked_to_primary->min);
504 	    else if (R_AXIS.log)
505 		r = AXIS_UNDO_LOG(POLAR_AXIS, x/cos(phi) + AXIS_DO_LOG(POLAR_AXIS, rmin));
506 	    else if (inverted_raxis)
507 		r = rmin - x/cos(phi);
508 	    else
509 		r = rmin + x/cos(phi);
510 
511 	    if (fmt)
512 		sprintf(s, fmt, theta, r);
513 	    else {
514 		sprintf(s, "theta: %.1f%s  r: %g", theta, degree_sign, r);
515 	    }
516 	} else {
517 	    sprintf(s, fmt, x, y);	/* user defined format */
518 	}
519     } else {
520 	sprintf(s, xy_format(), x, y);	/* usual x,y values */
521     }
522     return s + strlen(s);
523 }
524 
525 
526 /* Format x according to the date/time mouse mode. Uses and returns b as
527    a buffer
528  */
529 static char *
xDateTimeFormat(double x,char * b,int mode)530 xDateTimeFormat(double x, char *b, int mode)
531 {
532     struct tm tm;
533 
534     switch (mode) {
535     case MOUSE_COORDINATES_XDATE:
536 	ggmtime(&tm, x);
537 	sprintf(b, "%d. %d. %04d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year);
538 	break;
539     case MOUSE_COORDINATES_XTIME:
540 	ggmtime(&tm, x);
541 	sprintf(b, "%d:%02d", tm.tm_hour, tm.tm_min);
542 	break;
543     case MOUSE_COORDINATES_XDATETIME:
544 	ggmtime(&tm, x);
545 	sprintf(b, "%d. %d. %04d %d:%02d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year,
546 		tm.tm_hour, tm.tm_min);
547 	break;
548     case MOUSE_COORDINATES_TIMEFMT:
549 	/* FIXME HBB 20000507: timefmt is for *reading* timedata, not
550 	 * for writing them! */
551 	gstrftime(b, 0xff, timefmt, x);
552 	break;
553     default:
554 	sprintf(b, mouse_setting.fmt, x);
555     }
556     return b;
557 }
558 
559 
560 
561 /* HBB 20000507: fixed a construction error. Was using the 'timefmt'
562  * string (which is for reading, not writing time data) to output the
563  * value. Code is now closer to what setup_tics does. */
564 #define MKSTR(sp,x,axis)					\
565 do {								\
566     if (x >= VERYLARGE)	break;					\
567     if (axis_array[axis].datatype == DT_TIMEDATE) {		\
568 	char *format = copy_or_invent_formatstring(&axis_array[axis]);	\
569 	while (strchr(format,'\n'))				\
570 	     *(strchr(format,'\n')) = ' ';			\
571 	gstrftime(sp, 40, format, x);				\
572     } else {							\
573 	sprintf(sp, mouse_setting.fmt ,x);			\
574     }								\
575     sp += strlen(sp);						\
576 } while (0)
577 
578 
579 /* ratio for log, distance for linear */
580 # define DIST(x,rx,axis)			\
581    (axis_array[axis].log)			\
582     ? ( (rx==0) ? 99999 : x / rx )		\
583     : (x - rx)
584 
585 
586 /* formats the ruler information (position, distance,...) into string p
587 	(it must be sufficiently long)
588    x, y is the current mouse position in real coords (for the calculation
589 	of distance)
590 */
591 static void
GetRulerString(char * p,double x,double y)592 GetRulerString(char *p, double x, double y)
593 {
594     double dx, dy;
595 
596     char format[0xff] = "  ruler: [";
597     strcat(format, mouse_setting.fmt);
598     strcat(format, ", ");
599     strcat(format, mouse_setting.fmt);
600     strcat(format, "]  distance: ");
601     strcat(format, mouse_setting.fmt);
602     strcat(format, ", ");
603     strcat(format, mouse_setting.fmt);
604 
605     dx = DIST(x, ruler.x, FIRST_X_AXIS);
606     dy = DIST(y, ruler.y, FIRST_Y_AXIS);
607     sprintf(p, format, ruler.x, ruler.y, dx, dy);
608 
609     /* Previously, the following "if" let the polar coordinates to be shown only
610        for lin-lin plots:
611 	    if (mouse_setting.polardistance && !axis_array[FIRST_X_AXIS].log && !axis_array[FIRST_Y_AXIS].log) ...
612        Now, let us support also semilog and log-log plots.
613        Values of mouse_setting.polardistance are:
614 	    0 (no polar coordinates), 1 (polar coordinates), 2 (tangent instead of angle).
615     */
616     if (mouse_setting.polardistance) {
617 	double rho, phi, rx, ry;
618 	char ptmp[69];
619 	x = AXIS_LOG_VALUE(FIRST_X_AXIS, x);
620 	y = AXIS_LOG_VALUE(FIRST_Y_AXIS, y);
621 	rx = AXIS_LOG_VALUE(FIRST_X_AXIS, ruler.x);
622 	ry = AXIS_LOG_VALUE(FIRST_Y_AXIS, ruler.y);
623 	format[0] = '\0';
624 	strcat(format, " (");
625 	strcat(format, mouse_setting.fmt);
626 	rho = sqrt((x - rx) * (x - rx) + (y - ry) * (y - ry)); /* distance */
627 	if (mouse_setting.polardistance == 1) { /* (distance, angle) */
628 	    phi = (180 / M_PI) * atan2(y - ry, x - rx);
629 # ifdef OS2
630 	    strcat(format, ";% #.4g�)");
631 # else
632 	    strcat(format, ", % #.4gdeg)");
633 # endif
634 	} else { /* mouse_setting.polardistance==2: (distance, tangent) */
635 	    phi = x - rx;
636 	    phi = (phi == 0) ? ((y-ry>0) ? VERYLARGE : -VERYLARGE) : (y - ry)/phi;
637 	    sprintf(format+strlen(format), ", tangent=%s)", mouse_setting.fmt);
638 	}
639 	sprintf(ptmp, format, rho, phi);
640 	strcat(p, ptmp);
641     }
642 }
643 
644 
645 static struct t_zoom *zoom_head = NULL, *zoom_now = NULL;
646 static AXIS *axis_array_copy = NULL;
647 
648 /* Applies the zoom rectangle of  z  by sending the appropriate command
649    to gnuplot
650 */
651 
652 static void
apply_zoom(struct t_zoom * z)653 apply_zoom(struct t_zoom *z)
654 {
655     int is_splot_map = (is_3d_plot && (splot_map == TRUE));
656 
657     if (zoom_now != NULL) {	/* remember the current zoom */
658 	zoom_now->xmin = axis_array[FIRST_X_AXIS].set_min;
659 	zoom_now->xmax = axis_array[FIRST_X_AXIS].set_max;
660 	zoom_now->x2min = axis_array[SECOND_X_AXIS].set_min;
661 	zoom_now->x2max = axis_array[SECOND_X_AXIS].set_max;
662 	zoom_now->ymin = axis_array[FIRST_Y_AXIS].set_min;
663 	zoom_now->ymax = axis_array[FIRST_Y_AXIS].set_max;
664 	zoom_now->y2min = axis_array[SECOND_Y_AXIS].set_min;
665 	zoom_now->y2max = axis_array[SECOND_Y_AXIS].set_max;
666     }
667 
668     /* EAM DEBUG - The autoscale save/restore was too complicated, and
669      * broke refresh. Just save the complete axis state and have done with it.
670      */
671     if (zoom_now == zoom_head && z != zoom_head) {
672 	axis_array_copy = gp_realloc( axis_array_copy, sizeof(axis_array), "axis_array copy");
673 	memcpy(axis_array_copy, axis_array, sizeof(axis_array));
674     }
675 
676     /* If we are zooming, we don't want to autoscale the range.
677      * This wasn't necessary before we introduced "refresh".  Why?
678      */
679     if (zoom_now == zoom_head && z != zoom_head) {
680 	axis_array[FIRST_X_AXIS].autoscale = AUTOSCALE_NONE;
681 	axis_array[FIRST_Y_AXIS].autoscale = AUTOSCALE_NONE;
682 	axis_array[SECOND_X_AXIS].autoscale = AUTOSCALE_NONE;
683 	axis_array[SECOND_Y_AXIS].autoscale = AUTOSCALE_NONE;
684     }
685 
686     zoom_now = z;
687     if (zoom_now == NULL) {
688 	alert();
689 	return;
690     }
691 
692     /* Now we're committed. Notify the terminal the the next replot is a zoom */
693     (*term->layer)(TERM_LAYER_BEFORE_ZOOM);
694 
695     /* New range on primary axes */
696     set_explicit_range(&axis_array[FIRST_X_AXIS], zoom_now->xmin, zoom_now->xmax);
697     set_explicit_range(&axis_array[FIRST_Y_AXIS], zoom_now->ymin, zoom_now->ymax);
698 
699     /* EAM Apr 2013 - The tests on VERYLARGE protect against trying to   */
700     /* interpret the autoscaling initial state as an actual limit value. */
701     if (!is_3d_plot
702     && (zoom_now->x2min < VERYLARGE && zoom_now->x2max > -VERYLARGE)) {
703 	set_explicit_range(&axis_array[SECOND_X_AXIS], zoom_now->x2min, zoom_now->x2max);
704     }
705     if (!is_3d_plot
706     && (zoom_now->y2min < VERYLARGE && zoom_now->y2max > -VERYLARGE)) {
707 	set_explicit_range(&axis_array[SECOND_Y_AXIS], zoom_now->y2min, zoom_now->y2max);
708     }
709 
710     /* EAM Jun 2007 - The autoscale save/restore was too complicated, and broke
711      * refresh. Just save/restore the complete axis state and have done with it.
712      * Well, not _quite_ the complete state.  The labels are maintained dynamically.
713      * Apr 2015 - The same is now true (dynamic storage) for ticfmt, formatstring.
714      */
715     if (zoom_now == zoom_head) {
716 	int i;
717 	for (i=0; i<AXIS_ARRAY_SIZE; i++) {
718 	    axis_array_copy[i].label = axis_array[i].label;
719 	    axis_array_copy[i].ticdef.def.user = axis_array[i].ticdef.def.user;
720 	    axis_array_copy[i].ticdef.font = axis_array[i].ticdef.font;
721 	    axis_array_copy[i].ticfmt = axis_array[i].ticfmt;
722 	    axis_array_copy[i].formatstring = axis_array[i].formatstring;
723 	}
724 	memcpy(axis_array, axis_array_copy, sizeof(axis_array));
725 
726 	/* The shadowed primary axis, if any, is not restored by the memcpy.	*/
727 	/* We choose to recalculate the limits, but alternatively we could find	*/
728 	/* some place to save/restore the unzoomed limits.			*/
729 	if (nonlinear(&axis_array[FIRST_X_AXIS]))
730 	    clone_linked_axes(&axis_array[FIRST_X_AXIS], axis_array[FIRST_X_AXIS].linked_to_primary);
731 	if (nonlinear(&axis_array[FIRST_Y_AXIS]))
732 	    clone_linked_axes(&axis_array[FIRST_Y_AXIS], axis_array[FIRST_Y_AXIS].linked_to_primary);
733 
734 	/* Falling through to do_string_replot() does not work! */
735 	if (volatile_data) {
736 	    if (refresh_ok == E_REFRESH_OK_2D) {
737 		refresh_request();
738 		return;
739 	    }
740 	    if (is_splot_map && (refresh_ok == E_REFRESH_OK_3D)) {
741 		refresh_request();
742 		return;
743 	    }
744 	}
745 
746     } else {
747 	inside_zoom = TRUE;
748     }
749 
750     do_string_replot("");
751     inside_zoom = FALSE;
752 }
753 
754 
755 /* makes a zoom: update zoom history, call gnuplot to set ranges + replot
756 */
757 
758 static void
do_zoom(double xmin,double ymin,double x2min,double y2min,double xmax,double ymax,double x2max,double y2max)759 do_zoom(double xmin, double ymin, double x2min, double y2min, double xmax, double ymax, double x2max, double y2max)
760 {
761     struct t_zoom *z;
762     if (zoom_head == NULL) {	/* queue not yet created, thus make its head */
763 	zoom_head = gp_alloc(sizeof(struct t_zoom), "mouse zoom history head");
764 	zoom_head->prev = NULL;
765 	zoom_head->next = NULL;
766     }
767     if (zoom_now == NULL)
768 	zoom_now = zoom_head;
769     if (zoom_now->next == NULL) {	/* allocate new item */
770 	z = gp_alloc(sizeof(struct t_zoom), "mouse zoom history element");
771 	z->prev = zoom_now;
772 	z->next = NULL;
773 	zoom_now->next = z;
774 	z->prev = zoom_now;
775     } else			/* overwrite next item */
776 	z = zoom_now->next;
777 
778 #define SET_AXIS(axis, name, minmax, condition)                         \
779     z->name ## minmax = (axis_array[axis].minmax condition) ? name ## minmax : axis_array[axis].minmax
780 
781     SET_AXIS(FIRST_X_AXIS,  x,  min, < VERYLARGE);
782     SET_AXIS(FIRST_Y_AXIS,  y,  min, < VERYLARGE);
783     SET_AXIS(SECOND_X_AXIS, x2, min, < VERYLARGE);
784     SET_AXIS(SECOND_Y_AXIS, y2, min, < VERYLARGE);
785 
786     SET_AXIS(FIRST_X_AXIS,  x,  max, > -VERYLARGE);
787     SET_AXIS(FIRST_Y_AXIS,  y,  max, > -VERYLARGE);
788     SET_AXIS(SECOND_X_AXIS, x2, max, > -VERYLARGE);
789     SET_AXIS(SECOND_Y_AXIS, y2, max, > -VERYLARGE);
790 
791 #undef SET_AXIS
792 
793     apply_zoom(z);
794 }
795 
796 
797 static void
ZoomNext()798 ZoomNext()
799 {
800     if (zoom_now == NULL || zoom_now->next == NULL)
801 	alert();
802     else
803 	apply_zoom(zoom_now->next);
804     if (display_ipc_commands()) {
805 	fprintf(stderr, "next zoom.\n");
806     }
807 }
808 
809 static void
ZoomPrevious()810 ZoomPrevious()
811 {
812     if (zoom_now == NULL || zoom_now->prev == NULL)
813 	alert();
814     else
815 	apply_zoom(zoom_now->prev);
816     if (display_ipc_commands()) {
817 	fprintf(stderr, "previous zoom.\n");
818     }
819 }
820 
821 static void
ZoomUnzoom()822 ZoomUnzoom()
823 {
824     if (zoom_head == NULL || zoom_now == zoom_head)
825 	alert();
826     else
827 	apply_zoom(zoom_head);
828     if (display_ipc_commands()) {
829 	fprintf(stderr, "unzoom.\n");
830     }
831 }
832 
833 static void
incr_mousemode(const int amount)834 incr_mousemode(const int amount)
835 {
836     long int old = mouse_mode;
837     mouse_mode += amount;
838     if (MOUSE_COORDINATES_ALT == mouse_mode && !(mouse_alt_string || polar))
839 	mouse_mode += amount;	/* stepping over */
840     if (mouse_mode > MOUSE_COORDINATES_ALT) {
841 	mouse_mode = MOUSE_COORDINATES_REAL1;
842     } else if (mouse_mode <= MOUSE_COORDINATES_REAL) {
843 	mouse_mode = MOUSE_COORDINATES_ALT;
844 	if (!(mouse_alt_string || polar))
845 	    mouse_mode--;	/* stepping over */
846     }
847     UpdateStatusline();
848     if (display_ipc_commands())
849 	fprintf(stderr, "switched mouse format from %ld to %ld\n", old, mouse_mode);
850 }
851 
852 # define TICS_ON(ti) (((ti)&TICS_MASK)!=NO_TICS)
853 
854 void
UpdateStatusline()855 UpdateStatusline()
856 {
857     UpdateStatuslineWithMouseSetting(&mouse_setting);
858 }
859 
860 static void
UpdateStatuslineWithMouseSetting(mouse_setting_t * ms)861 UpdateStatuslineWithMouseSetting(mouse_setting_t * ms)
862 {
863     char s0[256], *sp;
864 
865 /* This suppresses mouse coordinate update after a ^C, but I think
866  * that the relevant terminals do their own checks anyhow so we
867  * we can just let the ones that care silently skip the update
868  * while the ones that don't care keep on updating as usual.
869  */
870 #if (0)
871     if (!term_initialised)
872 	return;
873 #endif
874 
875     if (!ms->on) {
876 	s0[0] = 0;
877     } else if (!ALMOST2D) {
878 	char format[0xff];
879 	format[0] = '\0';
880 	strcat(format, "view: ");
881 	strcat(format, ms->fmt);
882 	strcat(format, ", ");
883 	strcat(format, ms->fmt);
884 	strcat(format, "   scale: ");
885 	strcat(format, ms->fmt);
886 	strcat(format, ", ");
887 	strcat(format, ms->fmt);
888 	sprintf(s0, format, surface_rot_x, surface_rot_z, surface_scale, surface_zscale);
889     } else if (!TICS_ON(axis_array[SECOND_X_AXIS].ticmode) && !TICS_ON(axis_array[SECOND_Y_AXIS].ticmode)) {
890 	/* only first X and Y axis are in use */
891 	sp = GetAnnotateString(s0, real_x, real_y, mouse_mode, mouse_alt_string);
892 	if (ruler.on) {
893 	    GetRulerString(sp, real_x, real_y);
894 	}
895     } else {
896 	/* X2 and/or Y2 are in use: use more verbose format */
897 	sp = s0;
898 	if (TICS_ON(axis_array[FIRST_X_AXIS].ticmode)) {
899 	    sp = stpcpy(sp, "x=");
900 	    MKSTR(sp, real_x, FIRST_X_AXIS);
901 	    *sp++ = ' ';
902 	}
903 	if (TICS_ON(axis_array[FIRST_Y_AXIS].ticmode)) {
904 	    sp = stpcpy(sp, "y=");
905 	    MKSTR(sp, real_y, FIRST_Y_AXIS);
906 	    *sp++ = ' ';
907 	}
908 	if (TICS_ON(axis_array[SECOND_X_AXIS].ticmode)) {
909 	    sp = stpcpy(sp, "x2=");
910 	    MKSTR(sp, real_x2, SECOND_X_AXIS);
911 	    *sp++ = ' ';
912 	}
913 	if (TICS_ON(axis_array[SECOND_Y_AXIS].ticmode)) {
914 	    sp = stpcpy(sp, "y2=");
915 	    MKSTR(sp, real_y2, SECOND_Y_AXIS);
916 	    *sp++ = ' ';
917 	}
918 	if (ruler.on) {
919 	    /* ruler on? then also print distances to ruler */
920 	    if (TICS_ON(axis_array[FIRST_X_AXIS].ticmode)) {
921 		stpcpy(sp,"dx=");
922 		sprintf(sp+3, mouse_setting.fmt, DIST(real_x, ruler.x, FIRST_X_AXIS));
923 		sp += strlen(sp);
924 	    }
925 	    if (TICS_ON(axis_array[FIRST_Y_AXIS].ticmode)) {
926 		stpcpy(sp,"dy=");
927 		sprintf(sp+3, mouse_setting.fmt, DIST(real_y, ruler.y, FIRST_Y_AXIS));
928 		sp += strlen(sp);
929 	    }
930 	    if (TICS_ON(axis_array[SECOND_X_AXIS].ticmode)) {
931 		stpcpy(sp,"dx2=");
932 		sprintf(sp+4, mouse_setting.fmt, DIST(real_x2, ruler.x2, SECOND_X_AXIS));
933 		sp += strlen(sp);
934 	    }
935 	    if (TICS_ON(axis_array[SECOND_Y_AXIS].ticmode)) {
936 		stpcpy(sp,"dy2=");
937 		sprintf(sp+4, mouse_setting.fmt, DIST(real_y2, ruler.y2, SECOND_Y_AXIS));
938 		sp += strlen(sp);
939 	    }
940 	}
941 	*--sp = 0;		/* delete trailing space */
942     }
943     if (term->put_tmptext) {
944 	(term->put_tmptext) (0, s0);
945     }
946 }
947 #undef MKSTR
948 
949 
950 void
recalc_statusline()951 recalc_statusline()
952 {
953     MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
954     UpdateStatusline();
955 }
956 
957 /****************** handlers for user's actions ******************/
958 
959 static char *
builtin_autoscale(struct gp_event_t * ge)960 builtin_autoscale(struct gp_event_t *ge)
961 {
962     if (!ge) {
963 	return "`builtin-autoscale` (set autoscale keepfix; replot)";
964     }
965     do_string_replot("set autoscale keepfix");
966     return (char *) 0;
967 }
968 
969 static char *
builtin_toggle_border(struct gp_event_t * ge)970 builtin_toggle_border(struct gp_event_t *ge)
971 {
972     if (!ge)
973 	return "`builtin-toggle-border`";
974 
975     /* EAM July 2009  Cycle through border settings
976      * - no border
977      * - last border requested by the user
978      * - default border
979      * - (3D only) full border
980      */
981     if (draw_border == 0 && draw_border != user_border)
982 	draw_border = user_border;
983     else if (draw_border == user_border && draw_border != 31)
984 	draw_border = 31;
985     else if (is_3d_plot && draw_border == 31)
986 	draw_border = 4095;
987     else
988 	draw_border = 0;
989 
990     do_string_replot("");
991     return (char *) 0;
992 }
993 
994 static char *
builtin_replot(struct gp_event_t * ge)995 builtin_replot(struct gp_event_t *ge)
996 {
997     if (!ge) {
998 	return "`builtin-replot`";
999     }
1000     do_string_replot("");
1001     return (char *) 0;
1002 }
1003 
1004 static char *
builtin_toggle_grid(struct gp_event_t * ge)1005 builtin_toggle_grid(struct gp_event_t *ge)
1006 {
1007     if (!ge) {
1008 	return "`builtin-toggle-grid`";
1009     }
1010     if (! some_grid_selected())
1011 	do_string_replot("set grid");
1012     else
1013 	do_string_replot("unset grid");
1014     return (char *) 0;
1015 }
1016 
1017 static char *
builtin_help(struct gp_event_t * ge)1018 builtin_help(struct gp_event_t *ge)
1019 {
1020     if (!ge) {
1021 	return "`builtin-help`";
1022     }
1023     fprintf(stderr, "\n");
1024     bind_display((char *) 0);	/* display all bindings */
1025     restore_prompt();
1026     return (char *) 0;
1027 }
1028 
1029 static char *
builtin_set_plots_visible(struct gp_event_t * ge)1030 builtin_set_plots_visible(struct gp_event_t *ge)
1031 {
1032     if (!ge) {
1033 	return "`builtin-set-plots-visible`";
1034     }
1035     if (term->modify_plots)
1036 	term->modify_plots(MODPLOTS_SET_VISIBLE, -1);
1037     return (char *) 0;
1038 }
1039 
1040 static char *
builtin_set_plots_invisible(struct gp_event_t * ge)1041 builtin_set_plots_invisible(struct gp_event_t *ge)
1042 {
1043     if (!ge) {
1044 	return "`builtin-set-plots-invisible`";
1045     }
1046     if (term->modify_plots)
1047 	term->modify_plots(MODPLOTS_SET_INVISIBLE, -1);
1048     return (char *) 0;
1049 }
1050 
1051 static char *
builtin_invert_plot_visibilities(struct gp_event_t * ge)1052 builtin_invert_plot_visibilities(struct gp_event_t *ge)
1053 {
1054     if (!ge) {
1055 	return "`builtin-invert-plot-visibilities`";
1056     }
1057     if (term->modify_plots)
1058 	term->modify_plots(MODPLOTS_INVERT_VISIBILITIES, -1);
1059     return (char *) 0;
1060 }
1061 
1062 static char *
builtin_toggle_log(struct gp_event_t * ge)1063 builtin_toggle_log(struct gp_event_t *ge)
1064 {
1065     if (!ge)
1066 	return "`builtin-toggle-log` y logscale for plots, z and cb for splots";
1067 
1068     if (volatile_data)
1069 	int_warn(NO_CARET, "Cannot toggle log scale for volatile data");
1070     else if ((color_box.bounds.xleft < mouse_x && mouse_x < color_box.bounds.xright)
1071 	 &&  (color_box.bounds.ybot  < mouse_y && mouse_y < color_box.bounds.ytop))
1072 	do_string_replot( CB_AXIS.log ? "unset log cb" : "set log cb");
1073     else if (is_3d_plot && !splot_map)
1074 	do_string_replot( Z_AXIS.log ? "unset log z" : "set log z");
1075     else
1076 	do_string_replot( axis_array[FIRST_Y_AXIS].log ? "unset log y" : "set log y");
1077 
1078     return (char *) 0;
1079 }
1080 
1081 static char *
builtin_nearest_log(struct gp_event_t * ge)1082 builtin_nearest_log(struct gp_event_t *ge)
1083 {
1084     if (!ge)
1085 	return "`builtin-nearest-log` toggle logscale of axis nearest cursor";
1086 
1087     if ((color_box.bounds.xleft < mouse_x && mouse_x < color_box.bounds.xright)
1088     &&  (color_box.bounds.ybot  < mouse_y && mouse_y < color_box.bounds.ytop)) {
1089 	do_string_replot( CB_AXIS.log ? "unset log cb" : "set log cb");
1090     } else if (is_3d_plot && !splot_map) {
1091 	do_string_replot( Z_AXIS.log ? "unset log z" : "set log z");
1092     } else {
1093 	/* 2D-plot: figure out which axis/axes is/are
1094 	 * close to the mouse cursor, and toggle those lin/log */
1095 	/* note: here it is assumed that the x axis is at
1096 	 * the bottom, x2 at top, y left and y2 right; it
1097 	 * would be better to derive that from the ..tics settings */
1098 	TBOOLEAN change_x1 = FALSE;
1099 	TBOOLEAN change_y1 = FALSE;
1100 	TBOOLEAN change_x2 = FALSE;
1101 	TBOOLEAN change_y2 = FALSE;
1102 	if (mouse_y < plot_bounds.ybot + (plot_bounds.ytop - plot_bounds.ybot) / 4
1103 	&&  mouse_x > plot_bounds.xleft && mouse_x < plot_bounds.xright)
1104 	    change_x1 = TRUE;
1105 	if (mouse_x < plot_bounds.xleft + (plot_bounds.xright - plot_bounds.xleft) / 4
1106 	&&  mouse_y > plot_bounds.ybot && mouse_y < plot_bounds.ytop)
1107 	    change_y1 = TRUE;
1108 	if (mouse_y > plot_bounds.ytop - (plot_bounds.ytop - plot_bounds.ybot) / 4
1109 	&&  mouse_x > plot_bounds.xleft && mouse_x < plot_bounds.xright)
1110 	    change_x2 = TRUE;
1111 	if (mouse_x > plot_bounds.xright - (plot_bounds.xright - plot_bounds.xleft) / 4
1112 	&&  mouse_y > plot_bounds.ybot && mouse_y < plot_bounds.ytop)
1113 	    change_y2 = TRUE;
1114 
1115 	if (change_x1)
1116 	    do_string(axis_array[FIRST_X_AXIS].log ? "unset log x" : "set log x");
1117 	if (change_y1)
1118 	    do_string(axis_array[FIRST_Y_AXIS].log ? "unset log y" : "set log y");
1119 	if (change_x2 && !splot_map)
1120 	    do_string(axis_array[SECOND_X_AXIS].log ? "unset log x2" : "set log x2");
1121 	if (change_y2 && !splot_map)
1122 	    do_string(axis_array[SECOND_Y_AXIS].log ? "unset log y2" : "set log y2");
1123 	if (!change_x1 && !change_y1 && splot_map)
1124 	    do_string_replot( Z_AXIS.log ? "unset log z" : "set log z");
1125 
1126 	if (change_x1 || change_y1 || change_x2 || change_y2)
1127 	    do_string_replot("");
1128     }
1129 
1130     return (char *) 0;
1131 }
1132 
1133 static char *
builtin_toggle_mouse(struct gp_event_t * ge)1134 builtin_toggle_mouse(struct gp_event_t *ge)
1135 {
1136     if (!ge) {
1137 	return "`builtin-toggle-mouse`";
1138     }
1139     if (!mouse_setting.on) {
1140 	mouse_setting.on = 1;
1141 	if (display_ipc_commands()) {
1142 	    fprintf(stderr, "turning mouse on.\n");
1143 	}
1144     } else {
1145 	mouse_setting.on = 0;
1146 	if (display_ipc_commands()) {
1147 	    fprintf(stderr, "turning mouse off.\n");
1148 	}
1149     }
1150     if (term->set_cursor)
1151 	term->set_cursor(0, 0, 0);
1152 # ifdef OS2
1153     PM_update_menu_items();
1154 # endif
1155     UpdateStatusline();
1156     return (char *) 0;
1157 }
1158 
1159 static char *
builtin_toggle_ruler(struct gp_event_t * ge)1160 builtin_toggle_ruler(struct gp_event_t *ge)
1161 {
1162     if (!ge) {
1163 	return "`builtin-toggle-ruler`";
1164     }
1165     if (!term->set_ruler)
1166 	return (char *) 0;
1167     if (ruler.on) {
1168 	turn_ruler_off();
1169 	if (display_ipc_commands())
1170 	    fprintf(stderr, "turning ruler off.\n");
1171     } else if (ALMOST2D) {
1172 	/* only allow ruler, if the plot
1173 	 * is 2d or a 3d `map' */
1174 	struct udvt_entry *u;
1175 	ruler.on = TRUE;
1176 	ruler.px = ge->mx;
1177 	ruler.py = ge->my;
1178 	MousePosToGraphPosReal(ruler.px, ruler.py, &ruler.x, &ruler.y, &ruler.x2, &ruler.y2);
1179 	(*term->set_ruler) (ruler.px, ruler.py);
1180 	if ((u = add_udv_by_name("MOUSE_RULER_X"))) {
1181 	    Gcomplex(&u->udv_value,ruler.x,0);
1182 	}
1183 	if ((u = add_udv_by_name("MOUSE_RULER_Y"))) {
1184 	    Gcomplex(&u->udv_value,ruler.y,0);
1185 	}
1186 	if (display_ipc_commands()) {
1187 	    fprintf(stderr, "turning ruler on.\n");
1188 	}
1189     }
1190     UpdateStatusline();
1191     return (char *) 0;
1192 }
1193 
1194 static char *
builtin_decrement_mousemode(struct gp_event_t * ge)1195 builtin_decrement_mousemode(struct gp_event_t *ge)
1196 {
1197     if (!ge) {
1198 	return "`builtin-previous-mouse-format`";
1199     }
1200     incr_mousemode(-1);
1201     return (char *) 0;
1202 }
1203 
1204 static char *
builtin_increment_mousemode(struct gp_event_t * ge)1205 builtin_increment_mousemode(struct gp_event_t *ge)
1206 {
1207     if (!ge) {
1208 	return "`builtin-next-mouse-format`";
1209     }
1210     incr_mousemode(1);
1211     return (char *) 0;
1212 }
1213 
1214 static char *
builtin_toggle_polardistance(struct gp_event_t * ge)1215 builtin_toggle_polardistance(struct gp_event_t *ge)
1216 {
1217     if (!ge) {
1218 	return "`builtin-toggle-polardistance`";
1219     }
1220     if (++mouse_setting.polardistance > 2) mouse_setting.polardistance = 0;
1221 	/* values: 0 (no polar coordinates), 1 (polar coordinates), 2 (tangent instead of angle) */
1222     term->set_cursor((mouse_setting.polardistance ? -3:-4), ge->mx, ge->my); /* change cursor type */
1223 # ifdef OS2
1224     PM_update_menu_items();
1225 # endif
1226     UpdateStatusline();
1227     if (display_ipc_commands()) {
1228 	fprintf(stderr, "distance to ruler will %s be shown in polar coordinates.\n", mouse_setting.polardistance ? "" : "not");
1229     }
1230     return (char *) 0;
1231 }
1232 
1233 static char *
builtin_toggle_verbose(struct gp_event_t * ge)1234 builtin_toggle_verbose(struct gp_event_t *ge)
1235 {
1236     if (!ge) {
1237 	return "`builtin-toggle-verbose`";
1238     }
1239     /* this is tricky as the command itself modifies
1240      * the state of display_ipc_commands() */
1241     if (display_ipc_commands()) {
1242 	fprintf(stderr, "echoing of communication commands is turned off.\n");
1243     }
1244     toggle_display_of_ipc_commands();
1245     if (display_ipc_commands()) {
1246 	fprintf(stderr, "communication commands will be echoed.\n");
1247     }
1248     return (char *) 0;
1249 }
1250 
1251 static char *
builtin_toggle_ratio(struct gp_event_t * ge)1252 builtin_toggle_ratio(struct gp_event_t *ge)
1253 {
1254     if (!ge) {
1255 	return "`builtin-toggle-ratio`";
1256     }
1257     if (aspect_ratio == 0)
1258 	do_string_replot("set size ratio -1");
1259     else if (aspect_ratio == 1)
1260 	do_string_replot("set size nosquare");
1261     else
1262 	do_string_replot("set size square");
1263     return (char *) 0;
1264 }
1265 
1266 static char *
builtin_zoom_next(struct gp_event_t * ge)1267 builtin_zoom_next(struct gp_event_t *ge)
1268 {
1269     if (!ge) {
1270 	return "`builtin-zoom-next` go to next zoom in the zoom stack";
1271     }
1272     ZoomNext();
1273     return (char *) 0;
1274 }
1275 
1276 static char *
builtin_zoom_previous(struct gp_event_t * ge)1277 builtin_zoom_previous(struct gp_event_t *ge)
1278 {
1279     if (!ge) {
1280 	return "`builtin-zoom-previous` go to previous zoom in the zoom stack";
1281     }
1282     ZoomPrevious();
1283     return (char *) 0;
1284 }
1285 
1286 static char *
builtin_unzoom(struct gp_event_t * ge)1287 builtin_unzoom(struct gp_event_t *ge)
1288 {
1289     if (!ge) {
1290 	return "`builtin-unzoom`";
1291     }
1292     ZoomUnzoom();
1293     return (char *) 0;
1294 }
1295 
1296 static char *
builtin_rotate_right(struct gp_event_t * ge)1297 builtin_rotate_right(struct gp_event_t *ge)
1298 {
1299     if (!ge)
1300 	return "`scroll right in 2d, rotate right in 3d`; <Shift> faster";
1301     if (is_3d_plot)
1302 	ChangeView(0, -1);
1303     else {
1304 	int k = (modifier_mask & Mod_Shift) ? 3 : 1;
1305 	while (k-- > 0)
1306 	    do_zoom_scroll_right();
1307     }
1308     return (char *) 0;
1309 }
1310 
1311 static char *
builtin_rotate_left(struct gp_event_t * ge)1312 builtin_rotate_left(struct gp_event_t *ge)
1313 {
1314     if (!ge)
1315 	return "`scroll left in 2d, rotate left in 3d`; <Shift> faster";
1316     if (is_3d_plot)
1317 	ChangeView(0, 1);
1318     else {
1319 	int k = (modifier_mask & Mod_Shift) ? 3 : 1;
1320 	while (k-- > 0)
1321 	    do_zoom_scroll_left();
1322     }
1323     return (char *) 0;
1324 }
1325 
1326 static char *
builtin_rotate_up(struct gp_event_t * ge)1327 builtin_rotate_up(struct gp_event_t *ge)
1328 {
1329     if (!ge)
1330 	return "`scroll up in 2d, rotate up in 3d`; <Shift> faster";
1331     if (is_3d_plot)
1332 	ChangeView(1, 0);
1333     else {
1334 	int k = (modifier_mask & Mod_Shift) ? 3 : 1;
1335 	while (k-- > 0)
1336 	    do_zoom_scroll_up();
1337     }
1338     return (char *) 0;
1339 }
1340 
1341 static char *
builtin_rotate_down(struct gp_event_t * ge)1342 builtin_rotate_down(struct gp_event_t *ge)
1343 {
1344     if (!ge)
1345 	return "`scroll down in 2d, rotate down in 3d`; <Shift> faster";
1346     if (is_3d_plot)
1347 	ChangeView(-1, 0);
1348     else {
1349 	int k = (modifier_mask & Mod_Shift) ? 3 : 1;
1350 	while (k-- > 0)
1351 	    do_zoom_scroll_down();
1352     }
1353     return (char *) 0;
1354 }
1355 
1356 static char *
builtin_azimuth_left(struct gp_event_t * ge)1357 builtin_azimuth_left(struct gp_event_t *ge)
1358 {
1359     if (!ge)
1360 	return "`rotate azimuth left in 3d`; <ctrl> faster";
1361     if (is_3d_plot)
1362 	ChangeAzimuth(-1);
1363     return (char *) 0;
1364 }
1365 
1366 static char *
builtin_azimuth_right(struct gp_event_t * ge)1367 builtin_azimuth_right(struct gp_event_t *ge)
1368 {
1369     if (!ge)
1370 	return "`rotate azimuth right in 3d`; <ctrl> faster";
1371     if (is_3d_plot)
1372 	ChangeAzimuth(1);
1373     return (char *) 0;
1374 }
1375 
1376 static char *
builtin_cancel_zoom(struct gp_event_t * ge)1377 builtin_cancel_zoom(struct gp_event_t *ge)
1378 {
1379     if (!ge) {
1380 	return "`builtin-cancel-zoom` cancel zoom region";
1381     }
1382     if (!setting_zoom_region)
1383 	return (char *) 0;
1384     if (term->set_cursor)
1385 	term->set_cursor(0, 0, 0);
1386     setting_zoom_region = FALSE;
1387     if (display_ipc_commands()) {
1388 	fprintf(stderr, "zooming cancelled.\n");
1389     }
1390     return (char *) 0;
1391 }
1392 
1393 static void
event_keypress(struct gp_event_t * ge,TBOOLEAN current)1394 event_keypress(struct gp_event_t *ge, TBOOLEAN current)
1395 {
1396     int x, y;
1397     int c, par2;
1398     bind_t *ptr;
1399     bind_t keypress;
1400 
1401     c = ge->par1;
1402     par2 = ge->par2;
1403     x = ge->mx;
1404     y = ge->my;
1405 
1406     if (!bindings) {
1407 	bind_install_default_bindings();
1408     }
1409 
1410     if ((modifier_mask & Mod_Shift) && ((c & 0xff) == 0)) {
1411 	c = toupper(c);
1412     }
1413 
1414     bind_clear(&keypress);
1415     keypress.key = c;
1416     keypress.modifier = modifier_mask;
1417 
1418     /*
1419      * On 'pause mouse keypress' in active window export current keypress
1420      * and mouse coords to user variables. A key with 'bind all' terminates
1421      * a pause even from non-active windows.
1422      * Ignore NULL keypress.
1423      *
1424      * If we are paused for a keystroke, this takes precendence over normal
1425      * key bindings. Otherwise, for example typing 'm' would turn off mousing,
1426      * which is a bad thing if you are in the  middle of a mousing operation.
1427      */
1428 
1429     if ((paused_for_mouse & PAUSE_KEYSTROKE) && (c > '\0') && current) {
1430 	load_mouse_variables(x, y, FALSE, c);
1431 	return;
1432     }
1433 
1434     for (ptr = bindings; ptr; ptr = ptr->next) {
1435 	if (bind_matches(&keypress, ptr)) {
1436 	    struct udvt_entry *keywin;
1437 	    if ((keywin = add_udv_by_name("MOUSE_KEY_WINDOW"))) {
1438 		Ginteger(&keywin->udv_value, ge->winid);
1439 	    }
1440 	    /* Always honor keys set with "bind all" */
1441 	    if (ptr->allwindows && ptr->command) {
1442 		if (current)
1443 		    load_mouse_variables(x, y, FALSE, c);
1444 		else
1445 		    /* FIXME - Better to clear MOUSE_[XY] than to set it wrongly. */
1446 		    /*         This may be worth a separate subroutine.           */
1447 		    load_mouse_variables(0, 0, FALSE, c);
1448 		do_string(ptr->command);
1449 		/* Treat as a current event after we return to x11.trm */
1450 		ge->type = GE_keypress;
1451 		break;
1452 	    /* But otherwise ignore inactive windows */
1453 	    } else if (!current) {
1454 		break;
1455 	    /* Let user defined bindings overwrite the builtin bindings */
1456 	    } else if ((par2 & 1) == 0 && ptr->command) {
1457 		load_mouse_variables(x, y, FALSE, c);
1458 		do_string(ptr->command);
1459 		break;
1460 	    } else if (ptr->builtin) {
1461 		load_mouse_variables(x, y, FALSE, c);
1462 		ptr->builtin(ge);
1463 	    } else {
1464 		fprintf(stderr, "%s:%d protocol error\n", __FILE__, __LINE__);
1465 	    }
1466 	}
1467     }
1468 
1469 }
1470 
1471 
1472 static void
ChangeView(int x,int z)1473 ChangeView(int x, int z)
1474 {
1475     if (modifier_mask & Mod_Shift) {
1476 	x *= 10;
1477 	z *= 10;
1478     }
1479 
1480     if (x) {
1481 	surface_rot_x += x;
1482 	if (surface_rot_x < 0)
1483 	    surface_rot_x += 360;
1484 	if (surface_rot_x > 360)
1485 	    surface_rot_x -= 360;
1486     }
1487     if (z) {
1488 	surface_rot_z += z;
1489 	if (surface_rot_z < 0)
1490 	    surface_rot_z += 360;
1491 	if (surface_rot_z > 360)
1492 	    surface_rot_z -= 360;
1493     }
1494 
1495     if (x || z) {
1496 	fill_gpval_float("GPVAL_VIEW_ROT_X", surface_rot_x);
1497 	fill_gpval_float("GPVAL_VIEW_ROT_Z", surface_rot_z);
1498     }
1499 
1500     if (display_ipc_commands()) {
1501 	fprintf(stderr, "changing view to %f, %f.\n", surface_rot_x, surface_rot_z);
1502     }
1503 
1504     do_save_3dplot(first_3dplot, plot3d_num, 0 /* not quick */ );
1505 
1506     if (ALMOST2D) {
1507 	/* 2D plot, or suitably aligned 3D plot: update statusline */
1508 	if (!term->put_tmptext)
1509 	    return;
1510 	recalc_statusline();
1511     }
1512 }
1513 
1514 static void
ChangeAzimuth(int x)1515 ChangeAzimuth(int x)
1516 {
1517     /* Can't use Mod_Shift because keyboards differ on the */
1518     /* shift status of the < and > keys. */
1519     if (modifier_mask & Mod_Ctrl)
1520 	x *= 10;
1521 
1522     if (x) {
1523 	azimuth += x;
1524 	if (azimuth < 0)
1525 	    azimuth += 360;
1526 	if (azimuth > 360)
1527 	    azimuth -= 360;
1528 
1529 	fill_gpval_float("GPVAL_VIEW_AZIMUTH", azimuth);
1530     }
1531 
1532     if (display_ipc_commands())
1533 	fprintf(stderr, "changing azimuth to %f.\n", azimuth);
1534 
1535     do_save_3dplot(first_3dplot, plot3d_num, 0 /* not quick */ );
1536 }
1537 
1538 
is_mouse_outside_plot(void)1539 int is_mouse_outside_plot(void)
1540 {
1541     // Here I look at both min/max each time because reversed ranges can make
1542     // min > max
1543 #define CHECK_AXIS_OUTSIDE(real, axis)                                  \
1544     ( axis_array[axis].min <  VERYLARGE &&                              \
1545       axis_array[axis].max > -VERYLARGE &&                              \
1546       ( (real < AXIS_DE_LOG_VALUE(axis, axis_array[axis].min) &&        \
1547          real < AXIS_DE_LOG_VALUE(axis, axis_array[axis].max)) ||       \
1548         (real > AXIS_DE_LOG_VALUE(axis, axis_array[axis].min) &&        \
1549          real > AXIS_DE_LOG_VALUE(axis, axis_array[axis].max))))
1550 
1551     return
1552         CHECK_AXIS_OUTSIDE(real_x,  FIRST_X_AXIS)  ||
1553         CHECK_AXIS_OUTSIDE(real_y,  FIRST_Y_AXIS)  ||
1554         CHECK_AXIS_OUTSIDE(real_x2, SECOND_X_AXIS) ||
1555         CHECK_AXIS_OUTSIDE(real_y2, SECOND_Y_AXIS);
1556 
1557 #undef CHECK_AXIS_OUTSIDE
1558 }
1559 
1560 /* Return a new upper or lower axis limit that is a linear
1561  * combination of the current limits.
1562  */
1563 static double
rescale(int AXIS,double w1,double w2)1564 rescale(int AXIS, double w1, double w2)
1565 {
1566     double newlimit;
1567     struct axis *axis = &axis_array[AXIS];
1568     double axmin = axis->min;
1569     double axmax = axis->max;
1570 
1571     if (nonlinear(axis)) {
1572 	axmin = eval_link_function(axis->linked_to_primary, axmin);
1573 	axmax = eval_link_function(axis->linked_to_primary, axmax);
1574     }
1575 
1576     newlimit = w1*axmin + w2*axmax;
1577 
1578     if (nonlinear(axis))
1579 	newlimit = eval_link_function(axis->linked_to_primary->linked_to_secondary, newlimit);
1580     else
1581 	newlimit = AXIS_DE_LOG_VALUE(AXIS,newlimit);
1582 
1583     return newlimit;
1584 }
1585 
1586 /* Rescale axes and do zoom. */
1587 static void
zoom_rescale_xyx2y2(double a0,double a1,double a2,double a3,double a4,double a5,double a6,double a7,double a8,double a9,double a10,double a11,double a12,double a13,double a14,double a15,char msg[])1588 zoom_rescale_xyx2y2(double a0,double a1,double a2,double a3,double a4,double a5,double a6,
1589 	double a7,double a8,double a9,double a10,double a11,double a12,double a13,double a14,double a15,
1590 	char msg[])
1591 {
1592     double xmin  = rescale(FIRST_X_AXIS,   a0, a1);
1593     double ymin  = rescale(FIRST_Y_AXIS,   a2, a3);
1594     double x2min = rescale(SECOND_X_AXIS,  a4, a5);
1595     double y2min = rescale(SECOND_Y_AXIS,  a6, a7);
1596 
1597     double xmax  = rescale(FIRST_X_AXIS,   a8, a9);
1598     double ymax  = rescale(FIRST_Y_AXIS,  a10, a11);
1599     double x2max = rescale(SECOND_X_AXIS, a12, a13);
1600     double y2max = rescale(SECOND_Y_AXIS, a14, a15);
1601     do_zoom(xmin, ymin, x2min, y2min, xmax, ymax, x2max, y2max);
1602 
1603     if (msg[0] && display_ipc_commands()) {
1604 	fputs(msg, stderr); fputs("\n", stderr);
1605     }
1606 }
1607 
1608 
1609 /* Scroll left. */
1610 static void
do_zoom_scroll_left()1611 do_zoom_scroll_left()
1612 {
1613     zoom_rescale_xyx2y2(1.1, -0.1,
1614                         1,   0,
1615                         1.1, -0.1,
1616                         1,   0,
1617                         0.1, 0.9,
1618                         0,   1,
1619                         0.1, 0.9,
1620                         0,   1,
1621                         "scroll left.\n");
1622 }
1623 
1624 /* Scroll right. */
1625 static void
do_zoom_scroll_right()1626 do_zoom_scroll_right()
1627 {
1628     zoom_rescale_xyx2y2(0.9,  0.1,
1629                         1,    0,
1630                         0.9,  0.1,
1631                         1,    0,
1632                         -0.1, 1.1,
1633                         0,    1,
1634                         -0.1, 1.1,
1635                         0,    1,
1636                         "scroll right");
1637 }
1638 
1639 /* Scroll up. */
1640 static void
do_zoom_scroll_up()1641 do_zoom_scroll_up()
1642 {
1643     zoom_rescale_xyx2y2(1,    0,
1644                         0.9,  0.1,
1645                         1,    0,
1646                         0.9,  0.1,
1647                         0,    1,
1648                         -0.1, 1.1,
1649                         0,    1,
1650                         -0.1, 1.1,
1651                         "scroll up");
1652 }
1653 
1654 /* Scroll down. */
1655 static void
do_zoom_scroll_down()1656 do_zoom_scroll_down()
1657 {
1658     zoom_rescale_xyx2y2(1,   0,
1659                         1.1, -0.1,
1660                         1,   0,
1661                         1.1, -0.1,
1662                         0,   1,
1663                         0.1, 0.9,
1664                         0,   1,
1665                         0.1, 0.9,
1666                         "scroll down");
1667 }
1668 
1669 
1670 /* Return new lower and upper axis limits from expanding current limits
1671  * relative to current mouse position.
1672  */
1673 static void
rescale_around_mouse(double * newmin,double * newmax,int AXIS,double mouse_pos,double scale)1674 rescale_around_mouse(double *newmin, double *newmax, int AXIS, double mouse_pos, double scale)
1675 {
1676     struct axis *axis = &axis_array[AXIS];
1677     struct axis *primary = axis->linked_to_primary;
1678     double axmin = axis->min;
1679     double axmax = axis->max;
1680 
1681     if (nonlinear(axis)) {
1682 	axmin = eval_link_function(primary, axmin);
1683 	axmax = eval_link_function(primary, axmax);
1684 	mouse_pos = eval_link_function(primary, mouse_pos);
1685     } else {
1686 	mouse_pos = AXIS_LOG_VALUE(AXIS, mouse_pos);
1687     }
1688 
1689   *newmin = mouse_pos + (axmin - mouse_pos) * scale;
1690   *newmax = mouse_pos + (axmax - mouse_pos) * scale;
1691 
1692     if (nonlinear(axis)) {
1693 	*newmin = eval_link_function(primary->linked_to_secondary, *newmin);
1694 	*newmax = eval_link_function(primary->linked_to_secondary, *newmax);
1695     } else {
1696       *newmin = AXIS_DE_LOG_VALUE(AXIS,*newmin);
1697       *newmax = AXIS_DE_LOG_VALUE(AXIS,*newmax);
1698     }
1699 }
1700 
1701 
1702 /* Zoom in/out within x-axis. */
1703 static void
zoom_in_X(int zoom_key)1704 zoom_in_X(int zoom_key)
1705 {
1706     // I don't check for "outside" here. will do that later
1707     if (is_mouse_outside_plot()) {
1708         /* zoom in (X axis only) */
1709         double w1 = (zoom_key=='+') ? 23./25. : 23./21.;
1710         double w2 = (zoom_key=='+') ?  2./25. : -2./21.;
1711         zoom_rescale_xyx2y2(w1,w2, 1,0, w1,w2, 1,0,  w2,w1, 0,1, w2,w1, 0,1,
1712                             (zoom_key=='+' ? "zoom in X" : "zoom out X"));
1713     } else {
1714         double xmin, ymin, x2min, y2min, xmax, ymax, x2max, y2max;
1715 	double scale = (zoom_key=='+') ? 0.75 : 1.25;
1716 	rescale_around_mouse(&xmin,  &xmax,  FIRST_X_AXIS,  real_x,  scale);
1717 	rescale_around_mouse(&x2min, &x2max, SECOND_X_AXIS, real_x2, scale);
1718 
1719         ymin  = rescale(FIRST_Y_AXIS,  1,0);
1720         y2min = rescale(SECOND_Y_AXIS, 1,0);
1721         ymax  = rescale(FIRST_Y_AXIS,  0,1);
1722         y2max = rescale(SECOND_Y_AXIS, 0,1);
1723         do_zoom(xmin, ymin, x2min, y2min, xmax, ymax, x2max, y2max);
1724     }
1725 }
1726 
1727 static void
do_zoom_in_X()1728 do_zoom_in_X()
1729 {
1730     zoom_in_X('+');
1731 }
1732 
1733 static void
do_zoom_out_X()1734 do_zoom_out_X()
1735 {
1736     zoom_in_X('-');
1737 }
1738 
1739 
1740 /* Zoom around mouse cursor unless the cursor is outside the graph boundary,
1741    when it scales around the graph center.
1742    Syntax: zoom_key == '+' ... zoom in, zoom_key == '-' ... zoom out
1743 */
1744 static void
zoom_around_mouse(int zoom_key)1745 zoom_around_mouse(int zoom_key)
1746 {
1747     double xmin, ymin, x2min, y2min, xmax, ymax, x2max, y2max;
1748     if (is_mouse_outside_plot()) {
1749 	/* zoom in (factor of approximately 2^(.25), so four steps gives 2x larger) */
1750 	double w1 = (zoom_key=='+') ? 23./25. : 23./21.;
1751 	double w2 = (zoom_key=='+') ?  2./25. : -2./21.;
1752 	xmin  = rescale(FIRST_X_AXIS,  w1, w2);
1753 	ymin  = rescale(FIRST_Y_AXIS,  w1, w2);
1754 	x2min = rescale(SECOND_X_AXIS, w1, w2);
1755 	y2min = rescale(SECOND_Y_AXIS, w1, w2);
1756 
1757 	xmax  = rescale(FIRST_X_AXIS,  w2, w1);
1758 	ymax  = rescale(FIRST_Y_AXIS,  w2, w1);
1759 	x2max = rescale(SECOND_X_AXIS, w2, w1);
1760       	y2max = rescale(SECOND_Y_AXIS, w2, w1);
1761     } else {
1762 	int zsign = (zoom_key=='+') ? -1 : 1;
1763 	double xscale = pow(1.25, zsign * mouse_setting.xmzoom_factor);
1764 	double yscale = pow(1.25, zsign * mouse_setting.ymzoom_factor);
1765 	/* {x,y}zoom_factor = 0: not zoom, = 1: 0.8/1.25 zoom */
1766 	rescale_around_mouse(&xmin,  &xmax,  FIRST_X_AXIS,  real_x,  xscale);
1767 	rescale_around_mouse(&ymin,  &ymax,  FIRST_Y_AXIS,  real_y,  yscale);
1768 	rescale_around_mouse(&x2min, &x2max, SECOND_X_AXIS, real_x2, xscale);
1769 	rescale_around_mouse(&y2min, &y2max, SECOND_Y_AXIS, real_y2, yscale);
1770     }
1771     do_zoom(xmin, ymin, x2min, y2min, xmax, ymax, x2max, y2max);
1772     if (display_ipc_commands())
1773 	fprintf(stderr, "zoom %s.\n", (zoom_key=='+' ? "in" : "out"));
1774 }
1775 
1776 static void
do_zoom_in_around_mouse()1777 do_zoom_in_around_mouse()
1778 {
1779     zoom_around_mouse('+');
1780 }
1781 
1782 static void
do_zoom_out_around_mouse()1783 do_zoom_out_around_mouse()
1784 {
1785     zoom_around_mouse('-');
1786 }
1787 
1788 static char *
builtin_zoom_in_around_mouse(struct gp_event_t * ge)1789 builtin_zoom_in_around_mouse(struct gp_event_t *ge)
1790 {
1791     if (!ge)
1792 	return "`builtin-zoom-in` zoom in";
1793     do_zoom_in_around_mouse();
1794     return (char *) 0;
1795 }
1796 
1797 static char *
builtin_zoom_out_around_mouse(struct gp_event_t * ge)1798 builtin_zoom_out_around_mouse(struct gp_event_t *ge)
1799 {
1800     if (!ge)
1801 	return "`builtin-zoom-out` zoom out";
1802     do_zoom_out_around_mouse();
1803     return (char *) 0;
1804 }
1805 
1806 
1807 #if (0) /* Not currently used */
1808 static char *
builtin_zoom_scroll_left(struct gp_event_t * ge)1809 builtin_zoom_scroll_left(struct gp_event_t *ge)
1810 {
1811     if (!ge)
1812 	return "`builtin-zoom-scroll-left` scroll left";
1813     do_zoom_scroll_left();
1814     return (char *) 0;
1815 }
1816 
1817 static char *
builtin_zoom_scroll_right(struct gp_event_t * ge)1818 builtin_zoom_scroll_right(struct gp_event_t *ge)
1819 {
1820     if (!ge)
1821 	return "`builtin-zoom-scroll-right` scroll right";
1822     do_zoom_scroll_right();
1823     return (char *) 0;
1824 }
1825 
1826 static char *
builtin_zoom_scroll_up(struct gp_event_t * ge)1827 builtin_zoom_scroll_up(struct gp_event_t *ge)
1828 {
1829     if (!ge)
1830 	return "`builtin-zoom-scroll-up` scroll up";
1831     do_zoom_scroll_up();
1832     return (char *) 0;
1833 }
1834 
1835 static char *
builtin_zoom_scroll_down(struct gp_event_t * ge)1836 builtin_zoom_scroll_down(struct gp_event_t *ge)
1837 {
1838     if (!ge)
1839 	return "`builtin-zoom-scroll-down` scroll down";
1840     do_zoom_scroll_down();
1841     return (char *) 0;
1842 }
1843 
1844 static char *
builtin_zoom_in_X(struct gp_event_t * ge)1845 builtin_zoom_in_X(struct gp_event_t *ge)
1846 {
1847     if (!ge)
1848 	return "`builtin-zoom-in-X` zoom in X axis";
1849     do_zoom_in_X();
1850     return (char *) 0;
1851 }
1852 
1853 static char *
builtin_zoom_out_X(struct gp_event_t * ge)1854 builtin_zoom_out_X(struct gp_event_t *ge)
1855 {
1856     if (!ge)
1857 	return "`builtin-zoom-out-X` zoom out X axis";
1858     do_zoom_out_X();
1859     return (char *) 0;
1860 }
1861 #endif /* Not currently used */
1862 
1863 static void
event_buttonpress(struct gp_event_t * ge)1864 event_buttonpress(struct gp_event_t *ge)
1865 {
1866     int b;
1867 
1868     motion = 0;
1869 
1870     b = ge->par1;
1871     mouse_x = ge->mx;
1872     mouse_y = ge->my;
1873 
1874     button |= (1 << b);
1875 
1876     FPRINTF((stderr, "(event_buttonpress) mouse_x = %d\tmouse_y = %d\n", mouse_x, mouse_y));
1877 
1878     MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
1879 
1880 
1881     if ((b == 4 || b == 6) && /* 4 - wheel up, 6 - wheel left */
1882 	(!replot_disabled || (E_REFRESH_NOT_OK != refresh_ok))	/* Use refresh if available */
1883 	&& !(paused_for_mouse & PAUSE_BUTTON3)) {
1884 
1885 	/* Ctrl+Shift+wheel up or Squeeze (not implemented) */
1886 	if ((modifier_mask & Mod_Ctrl) && (modifier_mask & Mod_Shift))
1887 	    do_zoom_in_X();
1888 
1889 	/* Ctrl+wheel up or Ctrl+stroke */
1890 	else if ((modifier_mask & Mod_Ctrl))
1891 	    do_zoom_in_around_mouse();
1892 
1893 	/* Horizontal stroke (button 6) or Shift+wheel up */
1894 	else if (b == 6 || (modifier_mask & Mod_Shift))
1895 	    do_zoom_scroll_left();
1896 
1897 	/* Wheel up (no modifier keys) */
1898 	else
1899 	    do_zoom_scroll_up();
1900 
1901     } else if (((b == 5) || (b == 7)) && /* 5 - wheel down, 7 - wheel right */
1902 	       (!replot_disabled || (E_REFRESH_NOT_OK != refresh_ok))	/* Use refresh if available */
1903 	       && !(paused_for_mouse & PAUSE_BUTTON3)) {
1904 
1905 	/* Ctrl+Shift+wheel down or Unsqueeze (not implemented) */
1906 	if ((modifier_mask & Mod_Ctrl) && (modifier_mask & Mod_Shift))
1907 	    do_zoom_out_X();
1908 
1909 	/* Ctrl+wheel down or Ctrl+stroke */
1910 	else if ((modifier_mask & Mod_Ctrl))
1911 	    do_zoom_out_around_mouse();
1912 
1913 	/* Horizontal stroke (button 7) or Shift+wheel down */
1914 	else if (b == 7 || (modifier_mask & Mod_Shift))
1915 	    do_zoom_scroll_right();
1916 
1917 	/* Wheel down (no modifier keys) */
1918 	else
1919 	    do_zoom_scroll_down();
1920 
1921     } else if (ALMOST2D) {
1922         if (!setting_zoom_region) {
1923 	    if (1 == b) {
1924 		/* "pause button1" or "pause any" takes precedence over key bindings */
1925 		if (paused_for_mouse & PAUSE_BUTTON1) {
1926 		    load_mouse_variables(mouse_x, mouse_y, TRUE, b);
1927 		    trap_release = TRUE;	/* Don't trigger on release also */
1928 		    return;
1929 		}
1930 	    } else if (2 == b) {
1931 		/* not bound in 2d graphs */
1932 	    } else if (3 == b &&
1933 	    	(!replot_disabled || (E_REFRESH_NOT_OK != refresh_ok))	/* Use refresh if available */
1934 		&& !(paused_for_mouse & PAUSE_BUTTON3)) {
1935 		/* start zoom; but ignore it when
1936 		 *   - replot is disabled, e.g. with inline data, or
1937 		 *   - during 'pause mouse'
1938 		 * allow zooming during 'pause mouse key' */
1939 		setting_zoom_x = mouse_x;
1940 		setting_zoom_y = mouse_y;
1941 		setting_zoom_region = TRUE;
1942 		if (term->set_cursor) {
1943 		    int mv_mouse_x, mv_mouse_y;
1944 		    if (mouse_setting.annotate_zoom_box && term->put_tmptext) {
1945 			double real_x, real_y, real_x2, real_y2;
1946 			char s[64];
1947 			/* tell driver annotations */
1948 			MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
1949 			sprintf(s, zoombox_format(), real_x, real_y);
1950 			term->put_tmptext(1, s);
1951 			term->put_tmptext(2, s);
1952 		    }
1953 		    /* displace mouse in order not to start with an empty zoom box */
1954 		    mv_mouse_x = term->xmax / 20;
1955 		    mv_mouse_y = (term->xmax == term->ymax) ? mv_mouse_x : (int) ((mv_mouse_x * (double) term->ymax) / term->xmax);
1956 		    mv_mouse_x += mouse_x;
1957 		    mv_mouse_y += mouse_y;
1958 
1959 		    /* change cursor type */
1960 		    term->set_cursor(3, 0, 0);
1961 
1962 		    /* warp pointer */
1963 		    if (mouse_setting.warp_pointer)
1964 			term->set_cursor(-2, mv_mouse_x, mv_mouse_y);
1965 
1966 		    /* turn on the zoom box */
1967 		    term->set_cursor(-1, setting_zoom_x, setting_zoom_y);
1968 		}
1969 		if (display_ipc_commands()) {
1970 		    fprintf(stderr, "starting zoom region.\n");
1971 		}
1972 	    }
1973 	} else {
1974 
1975 	    /* complete zoom (any button
1976 	     * finishes zooming.) */
1977 
1978 	    /* the following variables are used to check,
1979 	     * if the box is big enough to be considered
1980 	     * as zoom box. */
1981 	    int dist_x = setting_zoom_x - mouse_x;
1982 	    int dist_y = setting_zoom_y - mouse_y;
1983 	    int dist = sqrt((double)(dist_x * dist_x + dist_y * dist_y));
1984 
1985 	    if (1 == b || 2 == b) {
1986 		/* zoom region is finished by the `wrong' button.
1987 		 * `trap' the next button-release event so that
1988 		 * it won't trigger the actions which are bound
1989 		 * to these events.
1990 		 */
1991 		trap_release = TRUE;
1992 	    }
1993 
1994 	    if (term->set_cursor) {
1995 		term->set_cursor(0, 0, 0);
1996 		if (mouse_setting.annotate_zoom_box && term->put_tmptext) {
1997 		    term->put_tmptext(1, "");
1998 		    term->put_tmptext(2, "");
1999 		}
2000 	    }
2001 
2002 	    if (dist > 10 /* more ore less arbitrary */ ) {
2003 
2004 		double xmin, ymin, x2min, y2min;
2005 		double xmax, ymax, x2max, y2max;
2006 
2007 		MousePosToGraphPosReal(setting_zoom_x, setting_zoom_y, &xmin, &ymin, &x2min, &y2min);
2008 		xmax = real_x;
2009 		x2max = real_x2;
2010 		ymax = real_y;
2011 		y2max = real_y2;
2012 		/* keep the axes (no)reversed as they are now */
2013 #define rev(a1,a2,A) if (sgn(a2-a1) != sgn(axis_array[A].max-axis_array[A].min)) \
2014 			    { double tmp = a1; a1 = a2; a2 = tmp; }
2015 		rev(xmin,  xmax,  FIRST_X_AXIS);
2016 		rev(ymin,  ymax,  FIRST_Y_AXIS);
2017 		rev(x2min, x2max, SECOND_X_AXIS);
2018 		rev(y2min, y2max, SECOND_Y_AXIS);
2019 #undef rev
2020 		do_zoom(xmin, ymin, x2min, y2min, xmax, ymax, x2max, y2max);
2021 		if (display_ipc_commands()) {
2022 		    fprintf(stderr, "zoom region finished.\n");
2023 		}
2024 	    } else {
2025 		/* silently ignore a tiny zoom box. This might
2026 		 * happen, if the user starts and finishes the
2027 		 * zoom box at the same position. */
2028 	    }
2029 	    setting_zoom_region = FALSE;
2030 	}
2031     } else {
2032 	if (term->set_cursor) {
2033 	    if (button & (1 << 1) || button & (1 << 3))
2034 		term->set_cursor(1, 0, 0);
2035 	    else if (button & (1 << 2))
2036 		term->set_cursor(2, 0, 0);
2037 	}
2038     }
2039     start_x = mouse_x;
2040     start_y = mouse_y;
2041     zero_rot_z = surface_rot_z + 360.0 * mouse_x / term->xmax;
2042     /* zero_rot_x = surface_rot_x - 180.0 * mouse_y / term->ymax; */
2043     zero_rot_x = surface_rot_x - 360.0 * mouse_y / term->ymax;
2044 }
2045 
2046 
2047 static void
event_buttonrelease(struct gp_event_t * ge)2048 event_buttonrelease(struct gp_event_t *ge)
2049 {
2050     int b, doubleclick;
2051 
2052     b = ge->par1;
2053     mouse_x = ge->mx;
2054     mouse_y = ge->my;
2055     doubleclick = ge->par2;
2056 
2057     button &= ~(1 << b);	/* remove button */
2058 
2059     if (setting_zoom_region) {
2060 	return;
2061     }
2062     if (TRUE == trap_release) {
2063 	trap_release = FALSE;
2064 	return;
2065     }
2066 
2067     MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
2068 
2069     FPRINTF((stderr, "MOUSE.C: doublclick=%i, set=%i, motion=%i, ALMOST2D=%i\n", (int) doubleclick, (int) mouse_setting.doubleclick,
2070 	     (int) motion, (int) ALMOST2D));
2071 
2072     if (ALMOST2D) {
2073 	char s0[256];
2074 	if (b == 1 && term->set_clipboard && ((doubleclick <= mouse_setting.doubleclick)
2075 					      || !mouse_setting.doubleclick)) {
2076 
2077 	    /* put coordinates to clipboard. For 3d plots this takes
2078 	     * only place, if the user didn't drag (rotate) the plot */
2079 
2080 	    if (!is_3d_plot || !motion) {
2081 		GetAnnotateString(s0, real_x, real_y, mouse_mode, mouse_alt_string);
2082 		term->set_clipboard(s0);
2083 		if (display_ipc_commands()) {
2084 		    fprintf(stderr, "put `%s' to clipboard.\n", s0);
2085 		}
2086 	    }
2087 	}
2088 	if (b == 2) {
2089 
2090 	    /* draw temporary annotation or label. For 3d plots this is
2091 	     * only done if the user didn't drag (scale) the plot */
2092 
2093 	    if (!is_3d_plot || !motion) {
2094 
2095 		GetAnnotateString(s0, real_x, real_y, mouse_mode, mouse_alt_string);
2096 		if (mouse_setting.label) {
2097 		    if (modifier_mask & Mod_Ctrl) {
2098 			remove_label(mouse_x, mouse_y);
2099 		    } else {
2100 			put_label(s0, real_x, real_y);
2101 		    }
2102 		} else {
2103 		    int dx, dy;
2104 		    int x = mouse_x;
2105 		    int y = mouse_y;
2106 		    dx = term->h_tic;
2107 		    dy = term->v_tic;
2108 		    (term->linewidth) (border_lp.l_width);
2109 		    (term->linetype) (border_lp.l_type);
2110 		    (term->move) (x - dx, y);
2111 		    (term->vector) (x + dx, y);
2112 		    (term->move) (x, y - dy);
2113 		    (term->vector) (x, y + dy);
2114 		    (term->justify_text) (LEFT);
2115 		    (term->put_text) (x + dx / 2, y + dy / 2 + term->v_char / 3, s0);
2116 		    (term->text) ();
2117 		}
2118 	    }
2119 	}
2120     }
2121     if (is_3d_plot && (b == 1 || b == 2 || b == 3)) {
2122 	if (!!(modifier_mask & Mod_Ctrl) && !needreplot) {
2123 	    /* redraw the 3d plot if its last redraw was 'quick'
2124 	     * (only axes) because modifier key was pressed */
2125 	    do_save_3dplot(first_3dplot, plot3d_num, 0);
2126 	}
2127 	if (term->set_cursor)
2128 	    term->set_cursor((button & (1 << 1)) ? 1 : (button & (1 << 2)) ? 2 : 0,
2129 				0, 0);
2130     }
2131 
2132     /* Export current mouse coords to user-accessible variables also */
2133     load_mouse_variables(mouse_x, mouse_y, TRUE, b);
2134     UpdateStatusline();
2135 
2136     /* In 2D mouse button 1 is available for "bind" commands */
2137     if (!is_3d_plot && (b == 1)) {
2138 	int save = ge->par1;
2139 	ge->par1 = GP_Button1;
2140 	ge->par2 = 0;
2141 	event_keypress(ge, TRUE);
2142 	ge->par1 = save;	/* needed for "pause mouse" */
2143     }
2144 }
2145 
2146 
2147 static void
event_motion(struct gp_event_t * ge)2148 event_motion(struct gp_event_t *ge)
2149 {
2150     motion = 1;
2151 
2152     mouse_x = ge->mx;
2153     mouse_y = ge->my;
2154 
2155     if (is_3d_plot
2156 	&& (splot_map == FALSE) /* Rotate the surface if it is 3D graph but not "set view map". */
2157 	) {
2158 
2159 	TBOOLEAN redraw = FALSE;
2160 
2161 	if (button & (1 << 1)) {
2162 	    /* dragging with button 1 -> rotate */
2163 	  /*surface_rot_x = floor(0.5 + zero_rot_x + 180.0 * mouse_y / term->ymax);*/
2164 	    surface_rot_x = floor(0.5 + fmod(zero_rot_x + 360.0 * mouse_y / term->ymax, 360));
2165 	    if (surface_rot_x < 0)
2166 		surface_rot_x += 360;
2167 	    if (surface_rot_x > 360)
2168 		surface_rot_x -= 360;
2169 	    surface_rot_z = floor(0.5 + fmod(zero_rot_z - 360.0 * mouse_x / term->xmax, 360));
2170 	    if (surface_rot_z < 0)
2171 		surface_rot_z += 360;
2172 	    redraw = TRUE;
2173 	} else if (button & (1 << 2)) {
2174 	    /* dragging with button 2 -> scale or changing ticslevel.
2175 	     * we compare the movement in x and y direction, and
2176 	     * change either scale or zscale */
2177 	    double relx, rely;
2178 	    relx = (double)abs(mouse_x - start_x) / (double)term->h_tic;
2179 	    rely = (double)abs(mouse_y - start_y) / (double)term->v_tic;
2180 
2181 	    if (modifier_mask & Mod_Shift) {
2182 		xyplane.z += (1 + fabs(xyplane.z))
2183 		    * (mouse_y - start_y) * 2.0 / term->ymax;
2184 	    } else {
2185 
2186 		if (relx > rely) {
2187 		    surface_lscale += (mouse_x - start_x) * 2.0 / term->xmax;
2188 		    surface_scale = exp(surface_lscale);
2189 		    if (surface_scale < 0)
2190 			surface_scale = 0;
2191 		} else {
2192 		    if (disable_mouse_z && (mouse_y-start_y > 0))
2193 			;
2194 		    else {
2195 			surface_zscale += (mouse_y - start_y) * 2.0 / term->ymax;
2196 			disable_mouse_z = FALSE;
2197 		    }
2198 		    if (surface_zscale < 0)
2199 			surface_zscale = 0;
2200 		}
2201 	    }
2202 	    /* reset the start values */
2203 	    start_x = mouse_x;
2204 	    start_y = mouse_y;
2205 	    redraw = TRUE;
2206 	} else if (button & (1 << 3)) {
2207 	    /* dragging with button 3 -> change azimuth */
2208 	    azimuth += (mouse_x - start_x) * 90.0 / term->xmax;
2209 	    start_x = mouse_x;
2210 	    redraw = TRUE;
2211 	}
2212 
2213 	if (!ALMOST2D) {
2214 	    turn_ruler_off();
2215 	}
2216 
2217 	if (redraw) {
2218 	    if (allowmotion) {
2219 		/* is processing of motions allowed right now?
2220 		 * then replot while
2221 		 * disabling further replots until it completes */
2222 		allowmotion = FALSE;
2223 		do_save_3dplot(first_3dplot, plot3d_num, !!(modifier_mask & Mod_Ctrl));
2224 		fill_gpval_float("GPVAL_VIEW_ROT_X", surface_rot_x);
2225 		fill_gpval_float("GPVAL_VIEW_ROT_Z", surface_rot_z);
2226 		fill_gpval_float("GPVAL_VIEW_SCALE", surface_scale);
2227 		fill_gpval_float("GPVAL_VIEW_ZSCALE", surface_zscale);
2228 		fill_gpval_float("GPVAL_VIEW_AZIMUTH", azimuth);
2229 	    } else {
2230 		/* postpone the replotting */
2231 		needreplot = TRUE;
2232 	    }
2233 	}
2234     } /* if (3D plot) */
2235 
2236 
2237     if (ALMOST2D) {
2238 	/* 2D plot, or suitably aligned 3D plot: update
2239 	 * statusline and possibly the zoombox annotation */
2240 	if (!term->put_tmptext)
2241 	    return;
2242 	MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
2243 	UpdateStatusline();
2244 
2245 	if (setting_zoom_region && mouse_setting.annotate_zoom_box) {
2246 	    double real_x, real_y, real_x2, real_y2;
2247 	    char s[64];
2248 	    MousePosToGraphPosReal(mouse_x, mouse_y, &real_x, &real_y, &real_x2, &real_y2);
2249 	    sprintf(s, zoombox_format(), real_x, real_y);
2250 	    term->put_tmptext(2, s);
2251 	}
2252     }
2253 }
2254 
2255 
2256 static void
event_modifier(struct gp_event_t * ge)2257 event_modifier(struct gp_event_t *ge)
2258 {
2259     modifier_mask = ge->par1;
2260 
2261     if (modifier_mask == 0 && is_3d_plot && (button & ((1 << 1) | (1 << 2))) && !needreplot) {
2262 	/* redraw the 3d plot if modifier key released */
2263 	do_save_3dplot(first_3dplot, plot3d_num, 0);
2264     }
2265 }
2266 
2267 
2268 void
event_plotdone()2269 event_plotdone()
2270 {
2271     if (needreplot) {
2272 	needreplot = FALSE;
2273 	do_save_3dplot(first_3dplot, plot3d_num, !!(modifier_mask & Mod_Ctrl));
2274     } else {
2275 	allowmotion = TRUE;
2276     }
2277 }
2278 
2279 
2280 void
event_reset(struct gp_event_t * ge)2281 event_reset(struct gp_event_t *ge)
2282 {
2283     modifier_mask = 0;
2284     button = 0;
2285     builtin_cancel_zoom(ge);
2286     if (term && term_initialised && term->set_cursor) {
2287 	term->set_cursor(0, 0, 0);
2288 	if (mouse_setting.annotate_zoom_box && term->put_tmptext) {
2289 	    term->put_tmptext(1, "");
2290 	    term->put_tmptext(2, "");
2291 	}
2292     }
2293 
2294     /* This hack is necessary on some systems in order to prevent one  */
2295     /* character of input from being swallowed when the plot window is */
2296     /* closed. But which systems, exactly, and in what circumstances?  */
2297     if (paused_for_mouse || !interactive) {
2298 	if (term && (!strncmp("x11",term->name,3)
2299 		 || !strncmp("wxt",term->name,3)
2300 		 || !strncmp("qt",term->name,2)))
2301 	    ungetc('\n',stdin);
2302     }
2303 
2304     if (paused_for_mouse) {
2305 	paused_for_mouse = 0;
2306 #ifdef WIN32
2307 	/* close pause message box */
2308 	kill_pending_Pause_dialog();
2309 #endif
2310     }
2311 
2312     /* Dummy up a keystroke event so that we can conveniently check for a  */
2313     /* binding to "Close". We only get these for the current window. */
2314     if (ge != (void *)1) {
2315 	ge->par1 = GP_Cancel;	/* Dummy keystroke */
2316 	ge->par2 = 0;		/* Not used; could pass window id here? */
2317 	event_keypress(ge, TRUE);
2318     }
2319 }
2320 
2321 
2322 void
do_event(struct gp_event_t * ge)2323 do_event(struct gp_event_t *ge)
2324 {
2325     if (!term)
2326 	return;
2327 
2328     /* disable `replot` when some data were sent through stdin */
2329     replot_disabled = plotted_data_from_stdin;
2330 
2331     if (ge->type) {
2332 	FPRINTF((stderr, "(do_event) type       = %d\n", ge->type));
2333 	FPRINTF((stderr, "           mx, my     = %d, %d\n", ge->mx, ge->my));
2334 	FPRINTF((stderr, "           par1, par2 = %d, %d\n", ge->par1, ge->par2));
2335     }
2336 
2337     switch (ge->type) {
2338     case GE_plotdone:
2339 	event_plotdone();
2340 	if (ge->winid) {
2341 	    current_x11_windowid = ge->winid;
2342 	    update_gpval_variables(6); /* fill GPVAL_TERM_WINDOWID */
2343 	}
2344 	break;
2345     case GE_keypress:
2346 	event_keypress(ge, TRUE);
2347 	break;
2348     case GE_keypress_old:
2349 	event_keypress(ge, FALSE);
2350 	break;
2351     case GE_modifier:
2352 	event_modifier(ge);
2353 	break;
2354     case GE_motion:
2355 	if (!mouse_setting.on)
2356 	    break;
2357 	event_motion(ge);
2358 	break;
2359     case GE_buttonpress:
2360 	if (!mouse_setting.on)
2361 	    break;
2362 	event_buttonpress(ge);
2363 	break;
2364     case GE_buttonrelease:
2365 	if (!mouse_setting.on)
2366 	    break;
2367 	event_buttonrelease(ge);
2368 	break;
2369     case GE_replot:
2370 	/* auto-generated replot (e.g. from replot-on-resize) */
2371 	/* FIXME: more terminals should use this! */
2372 	if (replot_line == NULL || replot_line[0] == '\0')
2373 	    break;
2374 	if (!strncmp(replot_line,"test",4))
2375 	    break;
2376 	if (multiplot)
2377 	    break;
2378 	do_string_replot("");
2379 	break;
2380     case GE_reset:
2381 	event_reset(ge);
2382 	break;
2383     case GE_fontprops:
2384 #ifdef X11
2385 	/* EAM FIXME:  Despite the name, only X11 uses this to pass font info.	*/
2386 	/* Everyone else passes just the plot height and width.			*/
2387 	if (!strcmp(term->name,"x11")) {
2388 	    /* These are declared in ../term/x11.trm */
2389 	    extern int          X11_hchar_saved, X11_vchar_saved;
2390 	    extern double       X11_ymax_saved;
2391 
2392 	    /* Cached sizing values for the x11 terminal. Each time an X11 window is
2393 	       resized, these are updated with the new sizes. When a replot happens some
2394 	       time later, these saved values are used. The normal mechanism for doing this
2395 	       is sending a QG from inboard to outboard driver, then the outboard driver
2396 	       responds with the sizing info in a GE_fontprops event. The problem is that
2397 	       other than during plot initialization the communication is asynchronous.
2398 	    */
2399 	    X11_hchar_saved = ge->par1;
2400 	    X11_vchar_saved = ge->par2;
2401 	    X11_ymax_saved = (double)term->xmax * (double)ge->my / fabs((double)ge->mx);
2402 
2403 	    /* If mx < 0, we simply save the values for future use, and move on */
2404 	    if (ge->mx < 0) {
2405 		break;
2406 	    } else {
2407 	    /* Otherwise we apply the changes right now */
2408 		term->h_char = X11_hchar_saved;
2409 		term->v_char = X11_vchar_saved;
2410 		/* factor of 2.5 must match the use in x11.trm */
2411 		term->h_tic = term->v_tic = X11_vchar_saved / 2.5;
2412 		term->ymax  = X11_ymax_saved;
2413 	    }
2414 	} else
2415 	    /* Fall through to cover non-x11 case */
2416 #endif
2417 	/* Other terminals update aspect ratio based on current window size */
2418 	    term->v_tic = term->h_tic * (double)ge->mx / (double)ge->my;
2419 
2420 	FPRINTF((stderr, "mouse do_event: window size %d X %d, font hchar %d vchar %d\n",
2421 		ge->mx, ge->my, ge->par1,ge->par2));
2422 	break;
2423     case GE_buttonpress_old:
2424     case GE_buttonrelease_old:
2425 	/* ignore */
2426 	break;
2427     case GE_raise:
2428 	/* FIXME: No generic routine implemented! */
2429 	/* Individual terminal types must handle it themselves if they care */
2430 	break;
2431     default:
2432 	fprintf(stderr, "%s:%d unrecognized event type %d\n", __FILE__, __LINE__, ge->type);
2433 	break;
2434     }
2435 
2436     replot_disabled = FALSE;	/* enable replot again */
2437 }
2438 
2439 static void
do_save_3dplot(struct surface_points * plots,int pcount,int quick)2440 do_save_3dplot(struct surface_points *plots, int pcount, int quick)
2441 {
2442 #if (0)
2443 #define M_TEST_AXIS(A) \
2444      (A.log && ((!(A.set_autoscale & AUTOSCALE_MIN) && A.set_min <= 0) || \
2445 		(!(A.set_autoscale & AUTOSCALE_MAX) && A.set_max <= 0)))
2446 #endif
2447 
2448     if (!plots || (E_REFRESH_NOT_OK == refresh_ok)) {
2449 	/* !plots might happen after the `reset' command for example
2450 	 * (reported by Franz Bakan).
2451 	 * !refresh_ok can happen for example if log scaling is reset (EAM).
2452 	 * replotrequest() should set up everything again in either case.
2453 	 */
2454 	replotrequest();
2455     } else {
2456 #if (0)	/* Dead code.  This error is now trapped elsewhere */
2457 	if (M_TEST_AXIS(X_AXIS) || M_TEST_AXIS(Y_AXIS) || M_TEST_AXIS(Z_AXIS)
2458 	    || M_TEST_AXIS(CB_AXIS)
2459 	    ) {
2460 		int_error(NO_CARET, "axis ranges must be above 0 for log scale!");
2461 		return;
2462 	}
2463 #endif
2464 	do_3dplot(plots, pcount, quick);
2465     }
2466 
2467 #undef M_TEST_AXIS
2468 }
2469 
2470 
2471 /*
2472  * bind related functions
2473  */
2474 
2475 static void
bind_install_default_bindings()2476 bind_install_default_bindings()
2477 {
2478     bind_remove_all();
2479     bind_append("a", (char *) 0, builtin_autoscale);
2480     bind_append("b", (char *) 0, builtin_toggle_border);
2481     bind_append("e", (char *) 0, builtin_replot);
2482     bind_append("g", (char *) 0, builtin_toggle_grid);
2483     bind_append("h", (char *) 0, builtin_help);
2484     bind_append("i", (char *) 0, builtin_invert_plot_visibilities);
2485     bind_append("l", (char *) 0, builtin_toggle_log);
2486     bind_append("L", (char *) 0, builtin_nearest_log);
2487     bind_append("m", (char *) 0, builtin_toggle_mouse);
2488     bind_append("r", (char *) 0, builtin_toggle_ruler);
2489     bind_append("V", (char *) 0, builtin_set_plots_invisible);
2490     bind_append("v", (char *) 0, builtin_set_plots_visible);
2491     bind_append("1", (char *) 0, builtin_decrement_mousemode);
2492     bind_append("2", (char *) 0, builtin_increment_mousemode);
2493     bind_append("5", (char *) 0, builtin_toggle_polardistance);
2494     bind_append("6", (char *) 0, builtin_toggle_verbose);
2495     bind_append("7", (char *) 0, builtin_toggle_ratio);
2496     bind_append("n", (char *) 0, builtin_zoom_next);
2497     bind_append("p", (char *) 0, builtin_zoom_previous);
2498     bind_append("u", (char *) 0, builtin_unzoom);
2499     bind_append("+", (char *) 0, builtin_zoom_in_around_mouse);
2500     bind_append("=", (char *) 0, builtin_zoom_in_around_mouse); /* same key as + but no need for Shift */
2501     bind_append("-", (char *) 0, builtin_zoom_out_around_mouse);
2502     bind_append("Right", (char *) 0, builtin_rotate_right);
2503     bind_append("Up", (char *) 0, builtin_rotate_up);
2504     bind_append("Left", (char *) 0, builtin_rotate_left);
2505     bind_append("Down", (char *) 0, builtin_rotate_down);
2506     bind_append("Opt-<", (char *) 0, builtin_azimuth_left);
2507     bind_append("Opt->", (char *) 0, builtin_azimuth_right);
2508     bind_append("Escape", (char *) 0, builtin_cancel_zoom);
2509 }
2510 
2511 static void
bind_clear(bind_t * b)2512 bind_clear(bind_t * b)
2513 {
2514     b->key = NO_KEY;
2515     b->modifier = 0;
2516     b->command = (char *) 0;
2517     b->builtin = 0;
2518     b->prev = (struct bind_t *) 0;
2519     b->next = (struct bind_t *) 0;
2520 }
2521 
2522 /* returns the enum which corresponds to the
2523  * string (ptr) or NO_KEY if ptr matches not
2524  * any of special_keys. */
2525 static int
lookup_key(char * ptr,int * len)2526 lookup_key(char *ptr, int *len)
2527 {
2528     char **keyptr;
2529     /* first, search in the table of "usual well-known" keys */
2530     int what = lookup_table_nth(usual_special_keys, ptr);
2531     if (what >= 0) {
2532 	*len = strlen(usual_special_keys[what].key);
2533 	return usual_special_keys[what].value;
2534     }
2535     /* second, search in the table of other keys */
2536     for (keyptr = special_keys; *keyptr; ++keyptr) {
2537 	if (!strcmp(ptr, *keyptr)) {
2538 	    *len = strlen(ptr);
2539 	    return keyptr - special_keys + GP_FIRST_KEY;
2540 	}
2541     }
2542     return NO_KEY;
2543 }
2544 
2545 /* returns 1 on success, else 0. */
2546 static int
bind_scan_lhs(bind_t * out,const char * in)2547 bind_scan_lhs(bind_t * out, const char *in)
2548 {
2549     static const char DELIM = '-';
2550     int itmp = NO_KEY;
2551     char *ptr;
2552     int len;
2553     bind_clear(out);
2554     if (!in) {
2555 	return 0;
2556     }
2557     for (ptr = (char *) in; ptr && *ptr; /* EMPTY */ ) {
2558 	if (!strncasecmp(ptr, "alt-", 4)) {
2559 	    out->modifier |= Mod_Alt;
2560 	    ptr += 4;
2561 	} else if (!strncasecmp(ptr, "ctrl-", 5)) {
2562 	    out->modifier |= Mod_Ctrl;
2563 	    ptr += 5;
2564 	} else if (!strncasecmp(ptr, "shift-", 6)) {
2565 	    out->modifier |= Mod_Shift;
2566 	    ptr += 6;
2567 	} else if (!strncasecmp(ptr, "opt-", 4)) {
2568 	    out->modifier |= Mod_Opt;
2569 	    ptr += 4;
2570 	} else if (NO_KEY != (itmp = lookup_key(ptr, &len))) {
2571 	    out->key = itmp;
2572 	    ptr += len;
2573 	} else if ((out->key = *ptr++) && *ptr && *ptr != DELIM) {
2574 	    fprintf(stderr, "bind: cannot parse %s\n", in);
2575 	    return 0;
2576 	}
2577     }
2578     if (NO_KEY == out->key)
2579 	return 0;		/* failed */
2580     else
2581 	return 1;		/* success */
2582 }
2583 
2584 /* note, that this returns a pointer
2585  * to the static char* `out' which is
2586  * modified on subsequent calls.
2587  */
2588 static char *
bind_fmt_lhs(const bind_t * in)2589 bind_fmt_lhs(const bind_t * in)
2590 {
2591     static char out[0x40];
2592     out[0] = '\0';		/* empty string */
2593     if (!in)
2594 	return out;
2595     if (in->modifier & Mod_Ctrl) {
2596 	sprintf(out, "Ctrl-");
2597     }
2598     if (in->modifier & Mod_Alt) {
2599 	strcat(out, "Alt-");
2600     }
2601     if (in->modifier & Mod_Shift) {
2602 	strcat(out, "Shift-");
2603     }
2604     if (in->key > GP_FIRST_KEY && in->key < GP_LAST_KEY) {
2605 	strcat(out,special_keys[in->key - GP_FIRST_KEY]);
2606     } else {
2607 	int k = 0;
2608 	for ( ; usual_special_keys[k].value > 0; k++) {
2609 	    if (usual_special_keys[k].value == in->key) {
2610 		strcat(out, usual_special_keys[k].key);
2611 		k = -1;
2612 		break;
2613 	    }
2614 	}
2615 	if (k >= 0) {
2616 	    char foo[2] = {'\0','\0'};
2617 	    foo[0] = in->key;
2618 	    strcat(out,foo);
2619 	}
2620     }
2621     return out;
2622 }
2623 
2624 static int
bind_matches(const bind_t * a,const bind_t * b)2625 bind_matches(const bind_t * a, const bind_t * b)
2626 {
2627     int a_mod = a->modifier;
2628     int b_mod = b->modifier;
2629 
2630     /* discard Shift modifier (except for mouse button) */
2631     if (a->key != GP_Button1) {
2632 	a_mod &= (Mod_Ctrl | Mod_Alt);
2633 	b_mod &= (Mod_Ctrl | Mod_Alt);
2634     }
2635 
2636     if (a->key == b->key && a_mod == b_mod)
2637 	return 1;
2638     else if (a->key == b->key && (b->modifier & Mod_Opt))
2639 	/* Mod_Opt means both Alt and Ctrl are optional */
2640 	return 2;
2641     else
2642 	return 0;
2643 }
2644 
2645 static void
bind_display_one(bind_t * ptr)2646 bind_display_one(bind_t * ptr)
2647 {
2648     fprintf(stderr, " %-13s ", bind_fmt_lhs(ptr));
2649     fprintf(stderr, "%c ", ptr->allwindows ? '*' : ' ');
2650     if (ptr->command) {
2651 	fprintf(stderr, "`%s`\n", ptr->command);
2652     } else if (ptr->builtin) {
2653 	fprintf(stderr, "%s\n", ptr->builtin(0));
2654     } else {
2655 	fprintf(stderr, "`%s:%d oops.'\n", __FILE__, __LINE__);
2656     }
2657 }
2658 
2659 static void
bind_display(char * lhs)2660 bind_display(char *lhs)
2661 {
2662     bind_t *ptr;
2663     bind_t lhs_scanned;
2664 
2665     if (!bindings) {
2666 	bind_install_default_bindings();
2667     }
2668 
2669     if (!lhs) {
2670 	/* display all bindings */
2671 	char fmt[] = " %-17s  %s\n";
2672 	fprintf(stderr, "\n");
2673 	/* mouse buttons */
2674 	fprintf(stderr, fmt, "<B1> doubleclick", "send mouse coordinates to clipboard (pm win wxt x11)");
2675 	fprintf(stderr, fmt, "<B2>", "annotate the graph using `mouseformat` (see keys '1', '2')");
2676 	fprintf(stderr, fmt, "", "or draw labels if `set mouse labels is on`");
2677 	fprintf(stderr, fmt, "<Ctrl-B2>", "remove label close to pointer if `set mouse labels` is on");
2678 	fprintf(stderr, fmt, "<B3>", "mark zoom region (only for 2d-plots and maps)");
2679 	fprintf(stderr, fmt, "<B1-Motion>", "change view (rotation); use <Ctrl> to rotate the axes only");
2680 	fprintf(stderr, fmt, "<B2-Motion>", "change view (scaling); use <Ctrl> to scale the axes only");
2681 	fprintf(stderr, fmt, "<Shift-B2-Motion>", "vertical motion -- change xyplane");
2682 	fprintf(stderr, fmt, "<B3-Motion>", "change view (azimuth)");
2683 
2684 	/* mouse wheel */
2685 	fprintf(stderr, fmt, "<wheel-up>", "  scroll up (in +Y direction)");
2686 	fprintf(stderr, fmt, "<wheel-down>", "  scroll down");
2687 	fprintf(stderr, fmt, "<shift-wheel-up>", "  scroll left (in -X direction)");
2688 	fprintf(stderr, fmt, "<shift-wheel-down>", " scroll right");
2689 	fprintf(stderr, fmt, "<Control-WheelUp>", "  zoom in on mouse position");
2690 	fprintf(stderr, fmt, "<Control-WheelDown>", "zoom out on mouse position");
2691 	fprintf(stderr, fmt, "<Shift-Control-WheelUp>", "  pinch on x");
2692 	fprintf(stderr, fmt, "<Shift-Control-WheelDown>", "expand on x");
2693 
2694 	fprintf(stderr, "\n");
2695 	/* keystrokes */
2696 #if (0)	/* Not implemented in the core code! */
2697 #ifndef DISABLE_SPACE_RAISES_CONSOLE
2698 	fprintf(stderr, " %-12s   %s\n", "Space", "raise gnuplot console window");
2699 #endif
2700 #endif
2701 	fprintf(stderr, " %-12s * %s\n", "q", "close this plot window");
2702 	fprintf(stderr, "\n");
2703 	for (ptr = bindings; ptr; ptr = ptr->next) {
2704 	    bind_display_one(ptr);
2705 	}
2706 	fprintf(stderr, "\n");
2707 	fprintf(stderr, "              * indicates this key is active from all plot windows\n");
2708 	fprintf(stderr, "\n");
2709 	return;
2710     }
2711 
2712     if (!bind_scan_lhs(&lhs_scanned, lhs)) {
2713 	return;
2714     }
2715     for (ptr = bindings; ptr; ptr = ptr->next) {
2716 	if (bind_matches(&lhs_scanned, ptr)) {
2717 	    bind_display_one(ptr);
2718 	    break;		/* only one match */
2719 	}
2720     }
2721 }
2722 
2723 static void
bind_remove(bind_t * b)2724 bind_remove(bind_t * b)
2725 {
2726     if (!b) {
2727 	return;
2728     } else if (b->builtin) {
2729 	/* don't remove builtins, just remove the overriding command */
2730 	if (b->command) {
2731 	    free(b->command);
2732 	    b->command = (char *) 0;
2733 	}
2734 	return;
2735     }
2736     if (b->prev)
2737 	b->prev->next = b->next;
2738     if (b->next)
2739 	b->next->prev = b->prev;
2740     else
2741 	bindings->prev = b->prev;
2742     if (b->command) {
2743 	free(b->command);
2744 	b->command = (char *) 0;
2745     }
2746     if (b == bindings) {
2747 	bindings = b->next;
2748 	if (bindings && bindings->prev) {
2749 	    bindings->prev->next = (bind_t *) 0;
2750 	}
2751     }
2752     free(b);
2753 }
2754 
2755 static void
bind_append(char * lhs,char * rhs,char * (* builtin)(struct gp_event_t * ge))2756 bind_append(char *lhs, char *rhs, char *(*builtin) (struct gp_event_t * ge))
2757 {
2758     bind_t *new = (bind_t *) gp_alloc(sizeof(bind_t), "bind_append->new");
2759 
2760     if (!bind_scan_lhs(new, lhs)) {
2761 	free(new);
2762 	return;
2763     }
2764 
2765     if (!bindings) {
2766 	/* first binding */
2767 	bindings = new;
2768     } else {
2769 
2770 	bind_t *ptr;
2771 	for (ptr = bindings; ptr; ptr = ptr->next) {
2772 	    if (bind_matches(new, ptr)) {
2773 		/* overwriting existing binding */
2774 		if (!rhs) {
2775 		    ptr->builtin = builtin;
2776 		} else if (*rhs) {
2777 		    if (ptr->command) {
2778 			free(ptr->command);
2779 			ptr->command = (char *) 0;
2780 		    }
2781 		    ptr->command = rhs;
2782 		} else {	/* rhs is an empty string, so remove the binding */
2783 		    bind_remove(ptr);
2784 		}
2785 		free(new);	/* don't need it any more */
2786 		return;
2787 	    }
2788 	}
2789 	/* if we're here, the binding does not exist yet */
2790 	/* append binding ... */
2791 	bindings->prev->next = new;
2792 	new->prev = bindings->prev;
2793     }
2794 
2795     bindings->prev = new;
2796     new->next = (struct bind_t *) 0;
2797     new->allwindows = FALSE;	/* Can be explicitly set later */
2798     if (!rhs) {
2799 	new->builtin = builtin;
2800     } else if (*rhs) {
2801 	new->command = rhs;	/* was allocated in command.c */
2802     } else {
2803 	bind_remove(new);
2804     }
2805 }
2806 
2807 void
bind_process(char * lhs,char * rhs,TBOOLEAN allwindows)2808 bind_process(char *lhs, char *rhs, TBOOLEAN allwindows)
2809 {
2810     if (!bindings) {
2811 	bind_install_default_bindings();
2812     }
2813     if (!rhs) {
2814 	bind_display(lhs);
2815     } else {
2816 	bind_append(lhs, rhs, 0);
2817 	if (allwindows)
2818 	    bind_all(lhs);
2819     }
2820     if (lhs)
2821 	free(lhs);
2822 }
2823 
2824 void
bind_all(char * lhs)2825 bind_all(char *lhs)
2826 {
2827     bind_t *ptr;
2828     bind_t keypress;
2829 
2830     if (!bind_scan_lhs(&keypress, lhs))
2831 	return;
2832 
2833     for (ptr = bindings; ptr; ptr = ptr->next) {
2834 	if (bind_matches(&keypress, ptr))
2835 	    ptr->allwindows = TRUE;
2836     }
2837 }
2838 
2839 void
bind_remove_all()2840 bind_remove_all()
2841 {
2842     bind_t *ptr;
2843     bind_t *safe;
2844     for (ptr = bindings; ptr; safe = ptr, ptr = ptr->next, free(safe)) {
2845 	if (ptr->command) {
2846 	    free(ptr->command);
2847 	    ptr->command = (char *) 0;
2848 	}
2849     }
2850     bindings = (bind_t *) 0;
2851 }
2852 
2853 /* Ruler is on, thus recalc its (px,py) from (x,y) for the current zoom and
2854    log axes.
2855 */
2856 static void
recalc_ruler_pos()2857 recalc_ruler_pos()
2858 {
2859     double P, dummy;
2860     if (is_3d_plot) {
2861 	/* To be exact, it is 'set view map' splot. */
2862 	int ppx, ppy;
2863 	dummy = 1.0; /* dummy value, but not 0.0 for the fear of log z-axis */
2864 	map3d_xy(ruler.x, ruler.y, dummy, &ppx, &ppy);
2865 	ruler.px = ppx;
2866 	ruler.py = ppy;
2867 	return;
2868     }
2869     /* It is 2D plot. */
2870     if (axis_array[FIRST_X_AXIS].log && ruler.x < 0)
2871 	ruler.px = -1;
2872     else {
2873 	P = AXIS_LOG_VALUE(FIRST_X_AXIS, ruler.x);
2874 	ruler.px = AXIS_MAP(FIRST_X_AXIS, P);
2875     }
2876     if (axis_array[FIRST_Y_AXIS].log && ruler.y < 0)
2877 	ruler.py = -1;
2878     else {
2879 	P = AXIS_LOG_VALUE(FIRST_Y_AXIS, ruler.y);
2880 	ruler.py = AXIS_MAP(FIRST_Y_AXIS, P);
2881     }
2882     MousePosToGraphPosReal(ruler.px, ruler.py, &dummy, &dummy, &ruler.x2, &ruler.y2);
2883 }
2884 
2885 
2886 /* Recalculate and replot the ruler after a '(re)plot'. Called from term.c.
2887 */
2888 void
update_ruler()2889 update_ruler()
2890 {
2891     if (!term->set_ruler || !ruler.on)
2892 	return;
2893     (*term->set_ruler) (-1, -1);
2894     recalc_ruler_pos();
2895     (*term->set_ruler) (ruler.px, ruler.py);
2896 }
2897 
2898 /* Set ruler on/off, and set its position.
2899    Called from set.c for 'set mouse ruler ...' command.
2900 */
2901 void
set_ruler(TBOOLEAN on,int mx,int my)2902 set_ruler(TBOOLEAN on, int mx, int my)
2903 {
2904     struct gp_event_t ge;
2905     if (ruler.on == FALSE && on == FALSE)
2906 	return;
2907     if (ruler.on == TRUE && on == TRUE && (mx < 0 || my < 0))
2908 	return;
2909     if (ruler.on == TRUE) /* ruler is on => switch it off */
2910 	builtin_toggle_ruler(&ge);
2911     /* now the ruler is off */
2912     if (on == FALSE) /* want ruler off */
2913 	return;
2914     if (mx>=0 && my>=0) { /* change ruler position */
2915 	ge.mx = mx;
2916 	ge.my = my;
2917     } else { /* don't change ruler position */
2918 	ge.mx = ruler.px;
2919 	ge.my = ruler.py;
2920     }
2921     builtin_toggle_ruler(&ge);
2922 }
2923 
2924 /* for checking if we change from plot to splot (or vice versa) */
2925 int
plot_mode(int set)2926 plot_mode(int set)
2927 {
2928     static int mode = MODE_PLOT;
2929     if (MODE_PLOT == set || MODE_SPLOT == set) {
2930 	if (mode != set) {
2931 	    turn_ruler_off();
2932 	}
2933 	mode = set;
2934     }
2935     return mode;
2936 }
2937 
2938 static void
turn_ruler_off()2939 turn_ruler_off()
2940 {
2941     if (ruler.on) {
2942 	struct udvt_entry *u;
2943 	ruler.on = FALSE;
2944 	if (term && term->set_ruler) {
2945 	    (*term->set_ruler) (-1, -1);
2946 	}
2947 	if ((u = add_udv_by_name("MOUSE_RULER_X")))
2948 	    u->udv_value.type = NOTDEFINED;
2949 	if ((u = add_udv_by_name("MOUSE_RULER_Y")))
2950 	    u->udv_value.type = NOTDEFINED;
2951 	if (display_ipc_commands()) {
2952 	    fprintf(stderr, "turning ruler off.\n");
2953 	}
2954     }
2955 }
2956 
2957 static int
nearest_label_tag(int xref,int yref)2958 nearest_label_tag(int xref, int yref)
2959 {
2960     double min = -1;
2961     int min_tag = -1;
2962     double diff_squared;
2963     int x, y;
2964     struct text_label *this_label;
2965     int xd;
2966     int yd;
2967 
2968     for (this_label = first_label; this_label != NULL; this_label = this_label->next) {
2969 	if (is_3d_plot) {
2970 	    map3d_position(&this_label->place, &xd, &yd, "label");
2971 	    xd -= xref;
2972 	    yd -= yref;
2973 	} else {
2974 	    map_position(&this_label->place, &x, &y, "label");
2975 	    xd = x - xref;
2976 	    yd = y - yref;
2977 	}
2978 	diff_squared = xd * xd + yd * yd;
2979 	if (-1 == min || min > diff_squared) {
2980 	    /* now we check if we're within a certain
2981 	     * threshold around the label */
2982 	    double tic_diff_squared;
2983 	    int htic, vtic;
2984 	    get_offsets(this_label, &htic, &vtic);
2985 	    tic_diff_squared = htic * htic + vtic * vtic;
2986 	    if (diff_squared < tic_diff_squared) {
2987 		min = diff_squared;
2988 		min_tag = this_label->tag;
2989 	    }
2990 	}
2991     }
2992 
2993     return min_tag;
2994 }
2995 
2996 static void
remove_label(int x,int y)2997 remove_label(int x, int y)
2998 {
2999     int tag = nearest_label_tag(x, y);
3000     if (-1 != tag) {
3001 	char cmd[0x40];
3002 	sprintf(cmd, "unset label %d", tag);
3003 	do_string_replot(cmd);
3004     }
3005 }
3006 
3007 static void
put_label(char * label,double x,double y)3008 put_label(char *label, double x, double y)
3009 {
3010     char cmd[0xff];
3011     sprintf(cmd, "set label \"%s\" at %g,%g %s", label, x, y,
3012 	mouse_setting.labelopts ? mouse_setting.labelopts : "point pt 1");
3013     do_string_replot(cmd);
3014 }
3015 
3016 #ifdef OS2
3017 /* routine required by pm.trm: fill in information needed for (un)checking
3018    menu items in the Presentation Manager terminal
3019 */
3020 void
PM_set_gpPMmenu(struct t_gpPMmenu * gpPMmenu)3021 PM_set_gpPMmenu __PROTO((struct t_gpPMmenu * gpPMmenu))
3022 {
3023     gpPMmenu->use_mouse = mouse_setting.on;
3024     if (zoom_now == NULL)
3025 	gpPMmenu->where_zoom_queue = 0;
3026     else {
3027 	gpPMmenu->where_zoom_queue = (zoom_now == zoom_head) ? 0 : 1;
3028 	if (zoom_now->prev != NULL)
3029 	    gpPMmenu->where_zoom_queue |= 2;
3030 	if (zoom_now->next != NULL)
3031 	    gpPMmenu->where_zoom_queue |= 4;
3032     }
3033     gpPMmenu->polar_distance = mouse_setting.polardistance;
3034 }
3035 #endif
3036 
3037 /* Save current mouse position to user-accessible variables.
3038  * Save the keypress or mouse button that triggered this in MOUSE_KEY,
3039  * and define MOUSE_BUTTON if it was a button click.
3040  */
3041 static void
load_mouse_variables(double x,double y,TBOOLEAN button,int c)3042 load_mouse_variables(double x, double y, TBOOLEAN button, int c)
3043 {
3044     struct udvt_entry *current;
3045 
3046     MousePosToGraphPosReal(x, y, &real_x, &real_y, &real_x2, &real_y2);
3047 
3048     if ((current = add_udv_by_name("MOUSE_BUTTON"))) {
3049 	Ginteger(&current->udv_value, button?c:-1);
3050 	if (!button)
3051 	    current->udv_value.type = NOTDEFINED;
3052     }
3053     if ((current = add_udv_by_name("MOUSE_KEY"))) {
3054 	Ginteger(&current->udv_value,c);
3055     }
3056     if ((current = add_udv_by_name("MOUSE_CHAR"))) {
3057 	char *keychar = gp_alloc(2,"key_char");
3058 	keychar[0] = c;
3059 	keychar[1] = '\0';
3060 	gpfree_string(&current->udv_value);
3061 	Gstring(&current->udv_value,keychar);
3062     }
3063     if ((current = add_udv_by_name("MOUSE_X"))) {
3064 	Gcomplex(&current->udv_value,real_x,0);
3065     }
3066     if ((current = add_udv_by_name("MOUSE_Y"))) {
3067 	Gcomplex(&current->udv_value,real_y,0);
3068     }
3069     if ((current = add_udv_by_name("MOUSE_X2"))) {
3070 	Gcomplex(&current->udv_value,real_x2,0);
3071     }
3072     if ((current = add_udv_by_name("MOUSE_Y2"))) {
3073 	Gcomplex(&current->udv_value,real_y2,0);
3074     }
3075     if ((current = add_udv_by_name("MOUSE_SHIFT"))) {
3076 	Ginteger(&current->udv_value, modifier_mask & Mod_Shift);
3077     }
3078     if ((current = add_udv_by_name("MOUSE_ALT"))) {
3079 	Ginteger(&current->udv_value, modifier_mask & Mod_Alt);
3080     }
3081     if ((current = add_udv_by_name("MOUSE_CTRL"))) {
3082 	Ginteger(&current->udv_value, modifier_mask & Mod_Ctrl);
3083     }
3084     return;
3085 }
3086 
3087 #endif /* USE_MOUSE */
3088