1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * Copyright (c) 1985-1988 by Supoj Sutanthavibul
4  * Parts Copyright (c) 1989-2007 by Brian V. Smith
5  * Parts Copyright (c) 1991 by Paul King
6  * Parts Copyright (c) 2004 by Chris Moller
7  *
8  * Any party obtaining a copy of these files is granted, free of charge, a
9  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
10  * nonexclusive right and license to deal in this software and documentation
11  * files (the "Software"), including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
13  * the Software, and to permit persons who receive copies from any such
14  * party to do so, with the only requirement being that the above copyright
15  * and this permission notice remain intact.
16  *
17  */
18 
19 #include <sys/types.h>
20 #include <regex.h>
21 #include <string.h>
22 
23 #include "fig.h"
24 #include "figx.h"
25 #include "resources.h"
26 #include "object.h"
27 #include "mode.h"
28 #include "f_util.h"
29 #include "w_canvas.h"
30 #include "w_setup.h"
31 #include "w_indpanel.h"
32 #include "w_util.h"
33 #include "w_keyboard.h"
34 #include "w_msgpanel.h"
35 
36 Boolean keyboard_input_available = False;
37 int keyboard_x;
38 int keyboard_y;
39 int keyboard_state;
40 
41 /* popups for keyboard input */
42 
43 typedef struct _keyboard_history_s {
44   char * str;
45   struct _keyboard_history_s * next;
46   struct _keyboard_history_s * prior;
47 } keyboard_history_s;
48 
49 static keyboard_history_s * keyboard_history = NULL;
50 static keyboard_history_s * keyboard_history_read_pointer;
51 static keyboard_history_s * keyboard_history_write_pointer;
52 static int keyboard_history_nr_etys;
53 #define KEYBOARD_HISTORY_MAX	32
54 
55 void
next_keyboard_history(w,event)56 next_keyboard_history(w, event)
57      Widget w;
58      XKeyEvent *event;
59 {
60   if (keyboard_history) {
61     char * str;
62     keyboard_history_read_pointer = keyboard_history_read_pointer->next;
63     str = keyboard_history_read_pointer->str;
64     XtVaSetValues(w, XtNstring, str, XtNinsertPosition, strlen(str) , NULL);
65   }
66 }
67 
68 void
prior_keyboard_history(w,event)69 prior_keyboard_history(w, event)
70      Widget w;
71      XKeyEvent *event;
72 {
73   if (keyboard_history) {
74     char * str;
75     keyboard_history_read_pointer = keyboard_history_read_pointer->prior;
76     str = keyboard_history_read_pointer->str;
77     XtVaSetValues(w, XtNstring, str, XtNinsertPosition, strlen(str) , NULL);
78   }
79 
80 }
81 
82 void
ignore_keyboard_input(w,event)83 ignore_keyboard_input(w, event)
84      Widget w;
85      XKeyEvent *event;
86 {
87   popdown_keyboard_panel();
88 }
89 
90 void
handle_keyboard_input(w,event)91 handle_keyboard_input(w, event)
92      Widget w;
93      XKeyEvent *event;
94 {
95 
96 #define WS "[[:space:]]*"
97 
98 #define RELABS WS "([rRaA])?" WS
99 
100 #define SLASH_DELIM WS "/" WS
101 
102   /* the comma is optional */
103 #define COMMA_DELIM  WS ",?" WS
104 
105 #define ANGLE_DELIM WS "<" WS
106 
107 #define FP_NR_TYPE1 "([+-]?[[:digit:]]+(\\.[[:digit:]]+)?)"
108 #define FP_NR_TYPE2 "([+-]?\\.[[:digit:]]+)"
109 #define FP_VALUE "(" FP_NR_TYPE1 "|" FP_NR_TYPE2 ")"
110 
111 #define ANGLE  FP_VALUE "([rRdDpP]?)"
112 
113 #define UNSIGNED_INT_VALUE "([[:digit:]]+)"
114 #define SIGNED_INT_VALUE "([+-]?[[:digit:]]+)"
115 
116 #define FRACTION UNSIGNED_INT_VALUE SLASH_DELIM UNSIGNED_INT_VALUE
117 
118 #define INT_VALUE "(" SIGNED_INT_VALUE "(-" FRACTION "))"
119 
120 #define MAG "(" INT_VALUE "|" FP_VALUE ")"
121 
122 #define POLAR_COORD "(" WS MAG ANGLE_DELIM ANGLE ")"
123 
124 #define RECT_COORD "(" RELABS MAG COMMA_DELIM RELABS MAG ")"
125 
126 #define COORD   POLAR_COORD "|" RECT_COORD
127 
128   char * coord = {COORD};
129   regex_t preg;
130   regmatch_t * pmatch = NULL;
131   int rc;
132   int origin_x, origin_y;
133 
134   DeclareArgs(2);
135   char *val;
136   double pix_per_unit = (double)(appres.INCHES? PIX_PER_INCH : PIX_PER_CM);
137 
138   if (NULL == pmatch) {
139 #if 1
140     regcomp(&preg, coord, REG_EXTENDED);
141 #else
142     fprintf(stderr, "coord = \"%s\"\n\n", coord);
143     if (REG_NOERROR != (rc = regcomp(&preg, coord, REG_EXTENDED))) {
144 #define ERRBUF_SIZE 256
145       char * errbuf = alloca(ERRBUF_SIZE);
146       regerror(rc, &preg, errbuf, ERRBUF_SIZE);
147       fprintf(stderr, "regcomp error: %s\n", errbuf);
148     }
149 #endif
150 
151     pmatch = malloc((1 + preg.re_nsub) * sizeof(regmatch_t));
152   }
153 
154 #define M_POLAR_COORD			 1
155 #define M_POLAR_MAG			 2
156 #define M_POLAR_MAG_I_FORM		 3
157 #define M_POLAR_MAG_I_FORM_INT		 4
158 #define M_POLAR_MAG_I_FORM_FRAC		 5
159 #define M_POLAR_MAG_I_FORM_FRAC_N	 6
160 #define M_POLAR_MAG_I_FORM_FRAC_D	 7
161 #define M_POLAR_MAG_F			 8
162 #define M_POLAR_PHASE			12
163 #define M_POLAR_PHASE_UNITS		16
164 
165 #define M_RECT_COORD			17
166 #define M_RECT_COORD_X_RA		18
167 #define M_RECT_COORD_X			19
168 #define M_RECT_COORD_X_I_FORM		20
169 #define M_RECT_COORD_X_I_FORM_INT	21
170 #define M_RECT_COORD_X_I_FORM_FRAC	22
171 #define M_RECT_COORD_X_I_FORM_FRAC_N	23
172 #define M_RECT_COORD_X_I_FORM_FRAC_D	24
173 #define M_RECT_COORD_X_F		25
174 
175 #define M_RECT_COORD_Y_RA		29
176 #define M_RECT_COORD_Y			30
177 #define M_RECT_COORD_Y_I_FORM		31
178 #define M_RECT_COORD_Y_I_FORM_INT	32
179 #define M_RECT_COORD_Y_I_FORM_FRAC	33
180 #define M_RECT_COORD_Y_I_FORM_FRAC_N	34
181 #define M_RECT_COORD_Y_I_FORM_FRAC_D	35
182 #define M_RECT_COORD_Y_F		36
183 
184 #define IS_POLAR			(-1 != pmatch[M_POLAR_MAG].rm_so)
185 #define IS_RECT				(-1 != pmatch[M_RECT_COORD].rm_so)
186 
187 #define IS_POLAR_MAG_I_FORM		(-1 != pmatch[M_POLAR_MAG_I_FORM].rm_so)
188 #define HAS_POLAR_MAG_I_FORM_FRAC	(-1 != pmatch[M_POLAR_MAG_I_FORM].rm_so)
189 #define POLAR_MAG_I_FORM_INT		(&val[pmatch[M_POLAR_MAG_I_FORM_INT].rm_so])
190 #define POLAR_MAG_I_FORM_FRAC_N		(&val[pmatch[M_POLAR_MAG_I_FORM_FRAC_N].rm_so])
191 #define POLAR_MAG_I_FORM_FRAC_D		(&val[pmatch[M_POLAR_MAG_I_FORM_FRAC_D].rm_so])
192 #define POLAR_MAG_F			(&val[pmatch[M_POLAR_MAG_F].rm_so])
193 #define POLAR_PHASE			(&val[pmatch[M_POLAR_PHASE].rm_so])
194 #define HAS_POLAR_PHASE_UNITS		(-1 != pmatch[M_POLAR_PHASE_UNITS].rm_so)
195 #define POLAR_PHASE_UNITS		(val[pmatch[M_POLAR_PHASE_UNITS].rm_so])
196 
197 #define HAS_RECT_X_RA			(-1 != pmatch[M_RECT_COORD_X_RA].rm_so)
198 #define RECT_X_RA			(val[pmatch[M_RECT_COORD_X_RA].rm_so])
199 #define IS_RECT_X_I_FORM		(-1 != pmatch[M_RECT_COORD_X_I_FORM].rm_so)
200 #define HAS_RECT_X_I_FORM_FRAC		(-1 != pmatch[M_RECT_COORD_X_I_FORM_FRAC].rm_so)
201 #define RECT_X_I_FORM_INT		(&val[pmatch[M_RECT_COORD_X_I_FORM_INT].rm_so])
202 #define RECT_X_I_FORM_FRAC_N		(&val[pmatch[M_RECT_COORD_X_I_FORM_FRAC_N].rm_so])
203 #define RECT_X_I_FORM_FRAC_D		(&val[pmatch[M_RECT_COORD_X_I_FORM_FRAC_D].rm_so])
204 #define RECT_X_F			(&val[pmatch[M_RECT_COORD_X_F].rm_so])
205 
206 #define HAS_RECT_Y_RA			(-1 != pmatch[M_RECT_COORD_Y_RA].rm_so)
207 #define RECT_Y_RA			(val[pmatch[M_RECT_COORD_Y_RA].rm_so])
208 #define IS_RECT_Y_I_FORM		(-1 != pmatch[M_RECT_COORD_Y_I_FORM].rm_so)
209 #define HAS_RECT_Y_I_FORM_FRAC		(-1 != pmatch[M_RECT_COORD_Y_I_FORM_FRAC].rm_so)
210 #define RECT_Y_I_FORM_INT		(&val[pmatch[M_RECT_COORD_Y_I_FORM_INT].rm_so])
211 #define RECT_Y_I_FORM_FRAC_N		(&val[pmatch[M_RECT_COORD_Y_I_FORM_FRAC_N].rm_so])
212 #define RECT_Y_I_FORM_FRAC_D		(&val[pmatch[M_RECT_COORD_Y_I_FORM_FRAC_D].rm_so])
213 #define RECT_Y_F			(&val[pmatch[M_RECT_COORD_Y_F].rm_so])
214 
215 
216   FirstArg(XtNstring, &val);
217   GetValues(w);
218 
219   if ((F_POLYLINE == cur_mode) ||
220       (F_POLYGON  == cur_mode)) {
221     if (cur_point) {
222       origin_x = cur_point->x;
223       origin_y = cur_point->y;
224     }
225     else {
226       origin_x = -1;
227       origin_y = -1;
228     }
229   }
230   else {
231     origin_x = fix_x;
232     origin_y = fix_y;
233   }
234 
235   if (REG_NOERROR == (rc = regexec(&preg, val, preg.re_nsub, pmatch, 0))) {
236 #if 0
237     int i;
238     for (i = 0; i < 1 + preg.re_nsub; i++) {
239       if (-1 != pmatch[i].rm_so) {
240 	fprintf(stderr, "match %2d: \"%.*s\"\n",
241 		i,
242 		pmatch[i].rm_eo - pmatch[i].rm_so,
243 		&val[pmatch[i].rm_so]);
244       }
245     }
246 #endif
247 
248     if (IS_POLAR) {
249       if ((0 <= origin_x) && (0 <= origin_y)) {
250 	double mag;
251 	double pha;
252 	double pha_conversion;
253 	if (IS_POLAR_MAG_I_FORM) {
254 	  mag = strtod(POLAR_MAG_I_FORM_INT, NULL);
255 	  if (HAS_POLAR_MAG_I_FORM_FRAC)
256 	    mag +=
257 	      strtod(POLAR_MAG_I_FORM_FRAC_N, NULL)/
258 	      strtod(POLAR_MAG_I_FORM_FRAC_D, NULL);
259 	}
260 	else mag = strtod(POLAR_MAG_F, NULL);
261 	pha = strtod(POLAR_PHASE, NULL);
262 	pha_conversion = M_PI/180.0;
263 	if (HAS_POLAR_PHASE_UNITS) {
264 	  switch(POLAR_PHASE_UNITS) {
265 	  case 'd':
266 	  case 'D':		/* degrees */
267 	    pha_conversion = M_PI/180.0;
268 	    break;
269 	  case 'r':
270 	  case 'R':		/* radians */
271 	    pha_conversion = 1.0;
272 	    break;
273 	  case 'p':
274 	  case 'P':		/* pi radians */
275 	    pha_conversion = M_PI;
276 	    break;
277 	  }
278 	}
279 	pha *= pha_conversion;
280 
281 	keyboard_x =  origin_x + (int)rint(pix_per_unit * mag * cos(pha));
282 	keyboard_y =  origin_y + (int)rint(pix_per_unit * mag * sin(pha));
283 	keyboard_state = event->state;
284 	keyboard_input_available = True;
285       }
286       else {
287 	put_msg("Polar coordinates must be relative to a current point.");
288 	beep();
289       }
290     }
291     else if (IS_RECT) {
292       double xv, yv;
293       Boolean x_absolute = True;
294       Boolean y_absolute = True;
295 
296       if (HAS_RECT_X_RA) {
297 	switch(RECT_X_RA) {
298 	case 'r':
299 	case 'R':
300 	  x_absolute = False;
301 	  break;
302 	case 'a':
303 	case 'A':
304 	  x_absolute = True;
305 	  break;
306 	}
307       }
308 
309       if (HAS_RECT_Y_RA) {
310 	switch(RECT_Y_RA) {
311 	case 'r':
312 	case 'R':
313 	  y_absolute = False;
314 	  break;
315 	case 'a':
316 	case 'A':
317 	  y_absolute = True;
318 	  break;
319 	}
320       }
321       else {	/* if x_abs has been set and y_abs is default, make y follow x */
322 	if (HAS_RECT_X_RA) y_absolute = x_absolute;
323       }
324 
325       if (((0 > origin_x) || (0 > origin_y)) &&
326 	  ((False == x_absolute) || (False == y_absolute))) {
327 	put_msg("Relative coordinates require a current point.");
328 	beep();
329       }
330       else {
331 	if (IS_RECT_X_I_FORM) {
332 	  xv = strtod(RECT_X_I_FORM_INT, NULL);
333 	  if (HAS_RECT_X_I_FORM_FRAC)
334 	    xv +=
335 	      strtod(RECT_X_I_FORM_FRAC_N, NULL)/
336 	      strtod(RECT_X_I_FORM_FRAC_D, NULL);
337 	}
338 	else xv = strtod(RECT_X_F, NULL);
339 
340 	if (IS_RECT_Y_I_FORM) {
341 	  yv = strtod(RECT_Y_I_FORM_INT, NULL);
342 	  if (HAS_RECT_Y_I_FORM_FRAC)
343 	    yv +=
344 	      strtod(RECT_Y_I_FORM_FRAC_N, NULL)/
345 	      strtod(RECT_Y_I_FORM_FRAC_D, NULL);
346 	}
347 	else yv = strtod(RECT_Y_F, NULL);
348 
349 	keyboard_x = (int)rint(pix_per_unit * xv);
350 	keyboard_y = (int)rint(pix_per_unit * yv);
351 	if (False == x_absolute) keyboard_x += origin_x;
352 	if (False == y_absolute) keyboard_y += origin_y;
353 	keyboard_state = event->state;
354 	keyboard_input_available = True;
355       }
356     }
357     else {
358       put_msg("Keyboard input screw-up.  Flame Chris Moller <moller@mollerware.com>");
359       beep();
360     }
361 
362     if (True == keyboard_input_available) {
363       if (NULL == keyboard_history) {
364 	keyboard_history = malloc(KEYBOARD_HISTORY_MAX * sizeof(keyboard_history_s));
365 	memset(keyboard_history, 0, KEYBOARD_HISTORY_MAX*sizeof(keyboard_history_s));
366 	keyboard_history_nr_etys = 1;
367 	keyboard_history_read_pointer = keyboard_history_write_pointer = &keyboard_history[0];
368 	keyboard_history[0].next  = keyboard_history_read_pointer;
369 	keyboard_history[0].prior = keyboard_history_read_pointer;
370 	keyboard_history[0].str = strdup(val);
371       }
372       else {
373 	if (strcmp(keyboard_history_read_pointer->str, val)) {		/* don't duplicate cur ety */
374 	  if (KEYBOARD_HISTORY_MAX <= keyboard_history_nr_etys) { /* start recycling */
375 	    keyboard_history_read_pointer
376 	      = keyboard_history_write_pointer
377 	      = keyboard_history_write_pointer->next;
378 	  }
379 	  else {	/* new entry */
380 	    keyboard_history[0].prior =
381 	      keyboard_history_write_pointer =
382 	      keyboard_history_write_pointer->next =
383 	      keyboard_history_read_pointer =
384 	      &keyboard_history[keyboard_history_nr_etys];
385 	    keyboard_history_write_pointer->next  = &keyboard_history[0];
386 	    keyboard_history_write_pointer->prior = &keyboard_history[keyboard_history_nr_etys - 1];
387 	    keyboard_history_nr_etys++;
388 	  }
389 	  if (NULL != keyboard_history_write_pointer->str) free(keyboard_history_write_pointer->str);
390 	  keyboard_history_write_pointer->str = strdup(val);
391 	}
392       }
393 
394     }
395 
396   }
397 
398   popdown_keyboard_panel();
399 }
400 
401 static
402 XtActionsRec keyboard_input_actions[] =
403 {
404     {"HandleKeyboardInput",  (XtActionProc) handle_keyboard_input} ,
405     {"IgnoreKeyboardInput",  (XtActionProc) ignore_keyboard_input} ,
406     {"NextKeyboardHistory",  (XtActionProc) next_keyboard_history} ,
407     {"PriorKeyboardHistory", (XtActionProc) prior_keyboard_history} ,
408 };
409 
410 static Widget keyboard_panel = None;
411 static Widget active_keyboard_panel = None;
412 static Widget keyboard_input = None;
413 
414 String  keyboard_translations =
415         "<Key>Return: HandleKeyboardInput()\n\
416         <Key>Escape:  IgnoreKeyboardInput()\n\
417         Ctrl<Key>n:   NextKeyboardHistory()\n\
418         <Key>Down:    NextKeyboardHistory()\n\
419         Ctrl<Key>p:   PriorKeyboardHistory()\n\
420         <Key>Up:      PriorKeyboardHistory()\n";
421 
422 static void
create_keyboard_panel()423 create_keyboard_panel()
424 {
425   Widget keyboard_form;
426   Widget label, usage_hint;
427   DeclareArgs(10);
428   char * str;
429 
430   keyboard_panel = XtVaCreatePopupShell("keyboard_menu", transientShellWidgetClass, tool,
431 					XtNtitle, "Keyboard Input", NULL);
432   keyboard_form = XtVaCreateManagedWidget("form", formWidgetClass, keyboard_panel,
433 					  XtNdefaultDistance, 0, NULL);
434 
435   FirstArg(XtNlabel, "Shift => Button2; Control => Button3");
436   NextArg(XtNjustify, XtJustifyLeft);
437   NextArg(XtNborderWidth, 0);
438   NextArg(XtNtop, XtChainTop);
439   NextArg(XtNbottom, XtChainTop);
440   NextArg(XtNleft, XtChainLeft);
441   NextArg(XtNright, XtChainLeft);
442   usage_hint = XtCreateManagedWidget("label", labelWidgetClass,
443 				     keyboard_form, Args, ArgCount);
444 
445   FirstArg(XtNlabel, "Coordinate:");
446   NextArg(XtNjustify, XtJustifyLeft);
447   NextArg(XtNborderWidth, 0);
448   NextArg(XtNtop, XtChainTop);
449   NextArg(XtNbottom, XtChainTop);
450   NextArg(XtNleft, XtChainLeft);
451   NextArg(XtNright, XtChainLeft);
452   NextArg(XtNfromVert, usage_hint);
453   label = XtCreateManagedWidget("label", labelWidgetClass,
454 				keyboard_form, Args, ArgCount);
455 
456   str = malloc(80);
457   str[0] = 0;
458   FirstArg(XtNstring, str);
459   NextArg(XtNinsertPosition, strlen(str));
460   NextArg(XtNeditType, XawtextEdit);
461   NextArg(XtNfromHoriz, label);
462   NextArg(XtNfromVert, usage_hint);
463   NextArg(XtNwidth, 320);
464   NextArg(XtNtop, XtChainTop);
465   NextArg(XtNbottom, XtChainTop);
466   NextArg(XtNleft, XtChainLeft);
467   NextArg(XtNright, XtChainLeft);
468   keyboard_input = XtCreateManagedWidget("keyboard_input", asciiTextWidgetClass,
469 					 keyboard_form, Args, ArgCount);
470   XtSetKeyboardFocus(keyboard_form, keyboard_input);
471   XtAppAddActions(tool_app, keyboard_input_actions, XtNumber(keyboard_input_actions));
472   XtOverrideTranslations(keyboard_input, XtParseTranslationTable(keyboard_translations));
473 
474 }
475 
476 void
popup_keyboard_panel(Widget widget,XButtonEvent * event,String * params,Cardinal * num_params)477 popup_keyboard_panel(Widget widget,
478 		     XButtonEvent * event,
479 		     String * params,
480 		     Cardinal * num_params)
481 {
482   Dimension wd, ht;
483 
484   /* make sure we're in a drawing mode first */
485   if (cur_mode >= FIRST_EDIT_MODE || cur_mode == F_PLACE_LIB_OBJ)
486 	return;
487 
488   if (keyboard_panel == None) {
489     create_keyboard_panel();
490     XtRealizeWidget(keyboard_panel);
491     XSetWMProtocols(tool_d, XtWindow(keyboard_panel), &wm_delete_window, 1);
492   }
493   XtVaGetValues( keyboard_panel, XtNwidth, &wd, XtNheight, &ht, NULL);
494   XtVaSetValues( keyboard_panel, XtNx, event->x_root - wd / 2,
495 		XtNy, event->y_root - ht * 2 / 3, NULL);
496   XtPopup( keyboard_panel, XtGrabNone);
497   active_keyboard_panel =  keyboard_panel;
498 }
499 
500 void
popdown_keyboard_panel()501 popdown_keyboard_panel()
502 {
503   if (active_keyboard_panel != None) {
504     XtPopdown(active_keyboard_panel);
505   }
506   active_keyboard_panel = None;
507 }
508