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