1 /***************************************************************************
2 SCED - Schematic Capture Editor
3 JSPICE3 adaptation of Spice3e2 - Copyright (c) Stephen R. Whiteley 1992
4 Copyright 1990 Regents of the University of California. All rights reserved.
5 Authors: 1981 Giles C. Billingsley (parts of KIC layout editor)
6 1992 Stephen R. Whiteley
7 ****************************************************************************/
8
9 /*
10 * SCED graphical and keyboard input.
11 */
12
13 #include "spice.h"
14 #include "sced.h"
15 #include "scedmacs.h"
16
17
18 #ifdef __STDC__
19 static void new_fine_window(long,long,long,long);
20 static int control_at(long,long);
21 static unsigned long actiontime(void);
22 static void point_input(long*,long*,char*,int*);
23 #else
24 static void new_fine_window();
25 static int control_at();
26 static unsigned long actiontime();
27 static void point_input();
28 #endif
29
30 #define DEBOUNCETIME 100
31
32 /* button1: basic point operation
33 * button2: pan operation
34 * button3: window operation
35 * button4: return coords, no operation
36 */
37
38 #define BUTTON1 1
39 #define BUTTON2 2
40 #define BUTTON3 4
41 #define BUTTON4 8
42
43 /* Point() command characters */
44 #define ESC 27
45 #define BSP '\b'
46 #define DEL 127
47 #define NEWL 13
48 #define SPA 32
49 #define CTRL_A 1
50 #define CTRL_F 6
51 #define CTRL_G 7
52 #define CTRL_N 14
53 #define CTRL_T 20
54 #define CTRL_U 21
55 #define CTRL_V 22
56 #define CTRL_X 24
57
58 static int OldButton3; /* remember last button 3 */
59 static long OldButton3X,OldButton3Y;
60
61 /* we keep track of the time between pointing events to debounce the cursor */
62 static unsigned long LastPointTime = 0;
63
64 /* disable screen modifying functions such as scaling, pan. */
65 int LockOut;
66
67 /* return from Point() if esc entered */
68 static int EscReturn;
69
70
71 void
Point()72 Point()
73
74 {
75 /*
76 * When user has typed Condition, do Action.
77 *
78 * Condition
79 * shortest unique prefix of Menu
80 * Action
81 * Return with command selected stored in Parameters.kpCommand.
82 *
83 * Condition
84 * ESC
85 * Action
86 * Forget remembered type in, return if EscReturn == True.
87 *
88 * Condition
89 * ctrl-a
90 * Action
91 * Execute abort routine.
92 *
93 * Condition
94 * point key or tablet stylus button Z
95 * Action
96 * If user is pointing at a layer in the layer table viewport,
97 * change the current layer.
98 * If user is pointing at a command menu selection, return with
99 * command selected stored as Parameters.kpCommand.
100 * If user is pointing inside a layout viewport--coarse or fine--
101 * return with Parameters.kpCommand[0] == EOS and cursor
102 * descriptor up-to-date.
103 *
104 * Condition
105 * ctrl-f or tablet stylus button 1
106 * Action
107 * Wait for user to point.
108 * Redisplay in fine viewport around where he pointed
109 * if fine positioning is enabled, or pan otherwise.
110 *
111 * Condition
112 * ctrl-g
113 * Action
114 * Change scale of magnifying glass, or otherwise window, using
115 * next two point actions.
116 *
117 * Condition
118 * ctrl-n
119 * Action
120 * Save the present view context in a list.
121 *
122 * Condition
123 * ctrl-t or ctrl-v
124 * Action
125 * Toggle position of magnifying glass, bottom or right.
126 *
127 */
128 char *TypeIn;
129 MENU *Menu;
130 unsigned long newtime;
131 int NumCommand,Buttons,Int1,Int2,Int3;
132 int Layer;
133 long X,Y,centerX,centerY;
134 char Key;
135 extern char *MenuABORT;
136
137 /*
138 * The best way to handle interrupts reliably is to
139 * initialize the service routines as frequently as
140 * possible. Therefore, . . .
141 */
142 InitSignals();
143 Menu = GetCurrentMenu();
144 Parameters.kpCommand[NumCommand = 0] = EOS;
145 Parameters.kpPointCoarseWindow = False;
146 if (OldButton3) {
147 OldButton3 = False;
148 FBSetRubberBanding(0);
149 }
150 LastPointTime = actiontime();
151
152 loop {
153 loop {
154 point_input(&X,&Y,&Key,&Buttons);
155
156 if (Key)
157 break;
158
159 if ((Buttons == BUTTON1 Or
160 Buttons == BUTTON2 Or
161 Buttons == BUTTON3 Or
162 Buttons == BUTTON4) And
163 (X < gi_maxx And X > 0 And Y < gi_maxy And Y > 0))
164 break;
165
166 }
167
168 newtime = actiontime();
169 if (newtime > LastPointTime && newtime - LastPointTime < DEBOUNCETIME)
170 continue;
171 LastPointTime = newtime;
172
173 switch (Key) {
174
175 case 0:
176 break;
177
178 case BSP:
179 case DEL:
180 if (NumCommand) NumCommand--;
181 Parameters.kpCommand[NumCommand] = EOS;
182 continue;
183
184 case ESC:
185 Parameters.kpCommand[NumCommand = 0] = EOS;
186 /* SRW ** so we know if esc was entered */
187 Parameters.kpCommand[1] = ESC;
188 if (EscReturn) return;
189 continue;
190
191 case NEWL:
192 Parameters.kpCommand[NumCommand = 0] = EOS;
193 if (control_at(X,Y)) return;
194 continue;
195
196 case '!':
197 /* shell command */
198 if (LockOut) continue; /* ignore */
199 ShowPrompt("! ");
200 TypeIn = FBEdit(NULL);
201 if (TypeIn != NULL) {
202 ShowProcess(TypeIn);
203 ErasePrompt();
204 }
205 Parameters.kpCommand[NumCommand = 0] = EOS;
206 continue;
207
208 case CTRL_A:
209 /* SRW ** abort gracefully */
210 strcpy(Parameters.kpCommand,MenuABORT);
211 NumCommand = 0;
212 return;
213
214 case CTRL_F:
215 if (LockOut) continue; /* ignore ^F */
216 Parameters.kpCommand[NumCommand = 0] = EOS;
217 SaveLastView();
218 /* SRW ** pan if coarse viewport only */
219 if (Parameters.kpRedisplayControl == COARSEVIEWPORTONLY) {
220 if (!InBox(X,Y,View->kvCoarseViewport)) {
221 NotPointingAtLayout();
222 continue;
223 }
224 PToL(View->kvCoarseWindow,&X,&Y);
225 InitCoarseWindow(X,Y,(long)View->kvCoarseWindow->kaWidth);
226 InitFineWindow(X,Y);
227 EraseLargeCoarseViewport();
228 Redisplay(View->kvCoarseWindow);
229 }
230 else
231 FinePosition(X,Y,Key);
232 /*
233 * This is necessary for debouncing.
234 * It takes time to redisplay, so we set the time of the last
235 * pointing event when the redisplay is finished.
236 */
237 LastPointTime = actiontime();
238 continue;
239
240 case CTRL_G:
241 if (LockOut) continue; /* ignore ^G */
242 Parameters.kpCommand[NumCommand = 0] = EOS;
243 if (Parameters.kpCellName[0] == EOS)
244 return;
245 loop {
246 ShowPrompt("Point to diagonal of area to be magnified.");
247 point_input(&X,&Y,&Key,&Buttons);
248 if (Key == ESC) goto skip;
249 if (Buttons == BUTTON1) {
250 if (InBox(X,Y,View->kvCoarseViewport))
251 PToL(View->kvCoarseWindow,&X,&Y);
252 elif (InBox(X,Y,View->kvFineViewport))
253 PToL(View->kvFineWindow,&X,&Y);
254 else {
255 NotPointingAtLayout();
256 continue;
257 }
258 OldButton3X = X;
259 OldButton3Y = Y;
260 FBSetRubberBanding('R');
261 break;
262 }
263 }
264 loop {
265 ShowPrompt("Point to second endpoint.");
266 point_input(&X,&Y,&Key,&Buttons);
267 if (Key == ESC) {
268 FBSetRubberBanding(0);
269 goto skip;
270 }
271 if (Buttons == BUTTON1) {
272 if (InBox(X,Y,View->kvCoarseViewport))
273 PToL(View->kvCoarseWindow,&X,&Y);
274 elif (InBox(X,Y,View->kvFineViewport))
275 PToL(View->kvFineWindow,&X,&Y);
276 else {
277 NotPointingAtLayout();
278 continue;
279 }
280 FBSetRubberBanding(0);
281 break;
282 }
283 }
284 new_fine_window(OldButton3X,OldButton3Y,X,Y);
285 skip:
286 ErasePrompt();
287 continue;
288
289 case CTRL_N:
290 Parameters.kpCommand[NumCommand = 0] = EOS;
291 SaveViewOnStack();
292 continue;
293
294 case CTRL_U:
295 case CTRL_X:
296 case SPA:
297 NumCommand = 0;
298 Parameters.kpCommand[0] = EOS;
299 continue;
300
301 case CTRL_T:
302 case CTRL_V:
303 if (LockOut) continue; /* ignore */
304 Parameters.kpCommand[NumCommand = 0] = EOS;
305 View->kvFineViewportOnBottom ^= 1;
306 InitViewport();
307 if (Parameters.kpRedisplayControl == COARSEVIEWPORTONLY)
308 continue;
309 SetPositioning();
310 EraseLargeCoarseViewport();
311 Redisplay(View->kvCoarseWindow);
312 continue;
313
314 default:
315 Parameters.kpCommand[NumCommand++] = Key;
316 if (NumCommand > 80) NumCommand = 80;
317 Parameters.kpCommand[NumCommand] = EOS;
318 /*
319 * Test for shortest unique prefix, or prefix matching upper
320 * case part of menu. Stupid search is plenty fast.
321 */
322 Int3 = -1;
323 Int2 = 0;
324 for (Int1 = 0; Menu[Int1].mEntry != NULL; Int1++) {
325 for (Int2 = 0; Int2 < NumCommand; Int2++) {
326 char c = Menu[Int1].mPrefix[Int2];
327 if (isupper(c)) c = tolower(c);
328 if (Parameters.kpCommand[Int2] != c) break;
329 }
330 if (Parameters.kpCommand[Int2] == EOS And Int2 > 0) {
331 if (!Menu[Int1].mPrefix[Int2]) {
332 /* found a match */
333 if (Int3 >= 0) {
334 /* oops, more than 1 match */
335 Int2 = -1;
336 break;
337 }
338 Int3 = Int1;
339 }
340 }
341 }
342 if (Int3 >= 0 && Int2 >= 0) {
343 strcpy(Parameters.kpCommand,Menu[Int3].mEntry);
344 return;
345 }
346 continue;
347 }
348 NumCommand = 0;
349 if (ButtonPress(Buttons,X,Y))
350 return;
351 }
352 }
353
354
355 int
ButtonPress(Buttons,X,Y)356 ButtonPress(Buttons,X,Y)
357
358 int Buttons;
359 long X,Y;
360 {
361
362 Parameters.kpCommand[0] = EOS;
363 if (Buttons == 0) {
364 /* shouldn't get here unless null from keyboard */
365 return (False);
366 }
367 if (Buttons == BUTTON1) {
368 if (control_at(X,Y)) return (True);
369 return (False);
370 }
371 if (Buttons == BUTTON2) {
372 if (LockOut) { /* treat like button 0 */
373 Buttons = BUTTON1;
374 if (control_at(X,Y)) return (True);
375 return (False);
376 }
377 SaveLastView();
378 /* pan if coarse viewport only */
379 if (Parameters.kpRedisplayControl == COARSEVIEWPORTONLY) {
380 if (!InBox(X,Y,View->kvCoarseViewport)) {
381 return (False);
382 }
383 PToL(View->kvCoarseWindow,&X,&Y);
384 InitCoarseWindow(X,Y,(long)View->kvCoarseWindow->kaWidth);
385 InitFineWindow(X,Y);
386 EraseLargeCoarseViewport();
387 Redisplay(View->kvCoarseWindow);
388 }
389 else
390 FinePosition(X,Y,(char)0);
391 LastPointTime = actiontime();
392 return (False);
393 }
394 if (Buttons == BUTTON3) {
395 if (Parameters.kpCellName[0] == EOS) return (False);
396 if (LockOut) { /* treat like button 0 */
397 Buttons = BUTTON1;
398 if (control_at(X,Y)) return (True);
399 return (False);
400 }
401 if (Not OldButton3) {
402 if (InBox(X,Y,View->kvCoarseViewport))
403 PToL(View->kvCoarseWindow,&X,&Y);
404 elif (InBox(X,Y,View->kvFineViewport))
405 PToL(View->kvFineWindow,&X,&Y);
406 else {
407 return (False);
408 }
409 OldButton3X = SCursor.kcRawX;
410 OldButton3Y = SCursor.kcRawY;
411 SCursor.kcRawX = X;
412 SCursor.kcRawY = Y;
413 FBSetRubberBanding('R');
414 SCursor.kcRawX = OldButton3X;
415 SCursor.kcRawY = OldButton3Y;
416 OldButton3X = X;
417 OldButton3Y = Y;
418 OldButton3 = True;
419 }
420 else {
421 if (InBox(X,Y,View->kvCoarseViewport))
422 PToL(View->kvCoarseWindow,&X,&Y);
423 elif (InBox(X,Y,View->kvFineViewport))
424 PToL(View->kvFineWindow,&X,&Y);
425 else {
426 FBSetRubberBanding(0);
427 return (False);
428 }
429 FBSetRubberBanding(0);
430 OldButton3 = False;
431 new_fine_window(OldButton3X,OldButton3Y,X,Y);
432 }
433 return (False);
434 }
435 if (Buttons == BUTTON4) {
436 return (False);
437 }
438 return (False);
439 }
440
441
442 static void
new_fine_window(OldX,OldY,X,Y)443 new_fine_window(OldX,OldY,X,Y)
444
445 long OldX,OldY,X,Y;
446 {
447 long NewWindowWidth, Hei, Wid, Tmp, CenterX, CenterY;
448
449 if (Parameters.kpRedisplayControl == COARSEVIEWPORTONLY) {
450
451 SaveLastView();
452 Wid = X - OldX;
453 Hei = Y - OldY;
454 if (Wid < 0) Wid = -Wid;
455 if (Hei < 0) Hei = -Hei;
456 Tmp = Hei*
457 (View->kvCoarseViewport->kaWidth/
458 View->kvCoarseViewport->kaHeight);
459 NewWindowWidth = Max(Wid,Tmp);
460 if (NewWindowWidth <= 0)
461 NewWindowWidth = RESOLUTION;
462 Wid /= 2;
463 Hei /= 2;
464 InitCoarseWindow(Min(X,OldX)+Wid,
465 Min(Y,OldY)+Hei,NewWindowWidth);
466 InitFineWindow(Min(X,OldX)+Wid,
467 Min(Y,OldY)+Hei);
468 EraseLargeCoarseViewport();
469 Redisplay(View->kvCoarseWindow);
470 }
471 else {
472 if (X > OldX)
473 SwapInts(X,OldX);
474 if ((OldX - X) < 2) { /* two lambda minimum width */
475 ShowPrompt("Magnifying glass width too small.");
476 return;
477 }
478 SaveLastView();
479 if (Y > OldY)
480 SwapInts(Y,OldY);
481 CenterX = (OldX - X)/2 + X;
482 CenterY = (OldY - Y)/2 + Y;
483 View->kvFineWindow->kaWidth = OldX - X;
484 View->kvFineWindow->kaHeight =
485 View->kvFineWindow->kaWidth*
486 (View->kvFineViewport->kaHeight/
487 View->kvFineViewport->kaWidth);
488 EraseFineViewport();
489 InitFineWindow(CenterX,CenterY);
490 ShowFineViewport();
491 }
492 }
493
494
495 int
PointLoop(LookedAhead)496 PointLoop(LookedAhead)
497
498 /* Loop until UNDO, a "non-safe" command, point to coarse window, or
499 * ESC is entered. Return value used for dispatching in non-safe (i.e.,
500 * cell modifying) commands.
501 */
502 int *LookedAhead;
503 {
504 extern char *MenuUNDO;
505
506 loop {
507 if (*LookedAhead == False) {
508 EscReturn = True;
509 Point();
510 EscReturn = False;
511 }
512 else
513 *LookedAhead = False;
514 if (Parameters.kpCommand[1] == ESC)
515 return (PL_ESC);
516 if (Parameters.kpCommand[0] != EOS) {
517 if (SafeCmds(LookedAhead))
518 continue;
519 if (Matching(MenuUNDO)) return (PL_UND);
520 *LookedAhead = True;
521 return (PL_CMD);
522 }
523 if (Parameters.kpPointCoarseWindow)
524 return (PL_PCW);
525 NotPointingAtLayout();
526 }
527 }
528
529
530 int
PointLoopSafe(LookedAhead)531 PointLoopSafe(LookedAhead)
532
533 /* Loop until UNDO, any command, point to coarse window, or
534 * ESC is entered. Return value used for dispatching in safe
535 * (i.e. non cell modifying) commands.
536 */
537 int *LookedAhead;
538 {
539
540 loop {
541 EscReturn = True;
542 Point();
543 EscReturn = False;
544 if (Parameters.kpCommand[1] == ESC)
545 return (PL_ESC);
546 if (Parameters.kpCommand[0] != EOS) {
547 *LookedAhead = True;
548 return (PL_CMD);
549 }
550 if (Parameters.kpPointCoarseWindow)
551 return (PL_PCW);
552 NotPointingAtLayout();
553 }
554 }
555
556
557 int
PointColor(LookedAhead)558 PointColor(LookedAhead)
559
560 /* Loop until UNDO, a non color setting command, point to coarse window, or
561 * ESC is entered.
562 */
563 int *LookedAhead;
564 {
565 extern char *MenuPLUSR,*MenuMINSR;
566 extern char *MenuPLUSG,*MenuMINSG;
567 extern char *MenuPLUSB,*MenuMINSB;
568 extern char *MenuRGB;
569 extern char *MenuUNDO;
570
571 loop {
572 if (*LookedAhead == False) {
573 EscReturn = True;
574 Point();
575 EscReturn = False;
576 }
577 else
578 *LookedAhead = False;
579 if (Parameters.kpCommand[1] == ESC)
580 return (PL_ESC);
581 if (Parameters.kpCommand[0] != EOS) {
582
583 if (Matching(MenuMINSB)) { AlterColor('b','-'); continue; }
584 if (Matching(MenuMINSG)) { AlterColor('g','-'); continue; }
585 if (Matching(MenuMINSR)) { AlterColor('r','-'); continue; }
586 if (Matching(MenuPLUSB)) { AlterColor('b','+'); continue; }
587 if (Matching(MenuPLUSG)) { AlterColor('g','+'); continue; }
588 if (Matching(MenuPLUSR)) { AlterColor('r','+'); continue; }
589 if (Matching(MenuRGB)) { RGB(); continue; }
590
591 if (Matching(MenuUNDO))
592 return (PL_UND);
593 *LookedAhead = True;
594 return (PL_CMD);
595 }
596 if (Parameters.kpPointCoarseWindow)
597 return (PL_PCW);
598 NotPointingAtLayout();
599 }
600 }
601
602
603 void
NotPointingAtLayout()604 NotPointingAtLayout()
605
606 {
607 ShowPrompt("You aren't pointing in the layout viewport.");
608 }
609
610
611 void
FinePosition(X,Y,Key)612 FinePosition(X,Y,Key)
613
614 long X,Y;
615 char Key;
616 {
617 int Buttons;
618
619 if (Parameters.kpCellName[0] == EOS)
620 return;
621 if (Parameters.kpRedisplayControl == COARSEVIEWPORTONLY) {
622 ShowPrompt("Fine positioning isn't required.");
623 Parameters.kpCommand[0] = EOS;
624 return;
625 }
626 if (Key != EOS) {
627 ShowPrompt("Point to center of area you want magnified.");
628 loop {
629 point_input(&X,&Y,&Key,&Buttons);
630 if (Key == EOS Or Key == NEWL)
631 break;
632 }
633 }
634 if (InBox(X,Y,View->kvCoarseViewport))
635 PToL(View->kvCoarseWindow,&X,&Y);
636 elif (InBox(X,Y,View->kvFineViewport))
637 PToL(View->kvFineWindow,&X,&Y);
638 else {
639 NotPointingAtLayout();
640 return;
641 }
642 EraseFineViewport();
643 InitFineWindow(X,Y);
644 ShowFineViewport();
645 }
646
647
648 static int
control_at(X,Y)649 control_at(X,Y)
650
651 long X,Y;
652 {
653 int i, Row, Column;
654 MENU *Menu;
655 extern char *MenuPLACE;
656
657 Parameters.kpPointCoarseWindow = False;
658 if (InBox(X,Y,View->kvCoarseViewport)) {
659 SCursor.kcInFine = False;
660 SCursor.kcPredX = SCursor.kcX;
661 SCursor.kcPredY = SCursor.kcY;
662 SCursor.kcX = X;
663 SCursor.kcY = Y;
664 SCursor.kcRawX = X;
665 SCursor.kcRawY = Y;
666 PToL(View->kvCoarseWindow,&SCursor.kcX,&SCursor.kcY);
667 PToL(View->kvCoarseWindow,&SCursor.kcRawX,&SCursor.kcRawY);
668 ClipToGridPoint(&SCursor.kcX,&SCursor.kcY);
669 SCursor.kcDX = SCursor.kcX-SCursor.kcPredX;
670 SCursor.kcDY = SCursor.kcY-SCursor.kcPredY;
671 Parameters.kpCommand[0] = '\0';
672 Parameters.kpPointCoarseWindow = True;
673 return (True);
674 }
675 if (InBox(X,Y,View->kvFineViewport)) {
676 SCursor.kcInFine = True;
677 SCursor.kcPredX = SCursor.kcX;
678 SCursor.kcPredY = SCursor.kcY;
679 SCursor.kcX = X;
680 SCursor.kcY = Y;
681 SCursor.kcRawX = X;
682 SCursor.kcRawY = Y;
683 PToL(View->kvFineWindow,&SCursor.kcX,&SCursor.kcY);
684 PToL(View->kvFineWindow,&SCursor.kcRawX,&SCursor.kcRawY);
685 ClipToGridPoint(&SCursor.kcX,&SCursor.kcY);
686 SCursor.kcDX = SCursor.kcX-SCursor.kcPredX;
687 SCursor.kcDY = SCursor.kcY-SCursor.kcPredY;
688 Parameters.kpCommand[0] = '\0';
689 Parameters.kpPointCoarseWindow = True;
690 return (True);
691 }
692 Menu = GetCurrentMenu();
693 Row = (gi_maxy-Y-3)/gi_fntheight+1;
694 Column = X/gi_fntwidth+1;
695 if (InBox((long)Column,(long)Row,&MenuViewport)) {
696 if (Column > 5)
697 Row += MenuViewport.kaY;
698 if (Menu == BasicMenu) {
699 for (i = 0; ; i++)
700 if (Menu[i].mEntry == NULL) break;
701
702 if (i > Row - 1) {
703 strcpy(Parameters.kpCommand,Menu[Row-1].mEntry);
704 return (True);
705 }
706 if (Column <= 5)
707 return (False);
708 if (i < MenuViewport.kaY)
709 i = MenuViewport.kaY;
710 Row -= i;
711 for (i = 0; ; i++)
712 if (DeviceMenu[i].mEntry == NULL) break;
713 if (i > Row - 1) {
714 /* hide the cell name after a null */
715 sprintf(Parameters.kpCommand,"%s %s",MenuPLACE,
716 DeviceMenu[Row-1].mEntry);
717 Parameters.kpCommand[strlen(MenuPLACE)] = '\0';
718 return (True);
719 }
720 }
721 else {
722 for (i = 0; ; i++)
723 if (Menu[i].mEntry == NULL) break;
724 if (i > Row - 1) {
725 strcpy(Parameters.kpCommand,Menu[Row-1].mEntry);
726 return (True);
727 }
728 }
729 }
730 return (False);
731 }
732
733
734 static unsigned long
actiontime()735 actiontime()
736
737 {
738 return ((unsigned long)1000*seconds());
739 }
740
741
742 static void
point_input(x,y,key,but)743 point_input(x,y,key,but)
744
745 long *x, *y;
746 char *key;
747 int *but;
748 {
749 REQUEST request;
750 RESPONSE response;
751
752 request.option = point_option;
753 DevInput(&request,&response);
754 *x = response.x;
755 *y = response.y;
756 *key = 0;
757 *but = 0;
758 if (response.option == char_option) {
759 *key = (isupper(response.reply.ch) ?
760 tolower(response.reply.ch) : response.reply.ch);
761 }
762 else {
763 *but = response.reply.button;
764 }
765 return;
766 }
767