1 /* windCmdAM.c -
2 *
3 * This file contains Magic command routines for those commands
4 * that are valid in all windows.
5 *
6 * *********************************************************************
7 * * Copyright (C) 1985, 1990 Regents of the University of California. *
8 * * Permission to use, copy, modify, and distribute this *
9 * * software and its documentation for any purpose and without *
10 * * fee is hereby granted, provided that the above copyright *
11 * * notice appear in all copies. The University of California *
12 * * makes no representations about the suitability of this *
13 * * software for any purpose. It is provided "as is" without *
14 * * express or implied warranty. Export of this software outside *
15 * * of the United States of America may require an export license. *
16 * *********************************************************************
17 */
18
19 #ifndef lint
20 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/windows/windCmdAM.c,v 1.2 2008/12/11 04:20:15 tim Exp $";
21 #endif /* not lint */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <sys/times.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <math.h> /* for round() function */
32
33 #include "tcltk/tclmagic.h"
34 #include "utils/magic.h"
35 #include "textio/textio.h"
36 #include "utils/geometry.h"
37 #include "windows/windows.h"
38 #include "utils/malloc.h"
39 #include "utils/runstats.h"
40 #include "utils/macros.h"
41 #include "utils/signals.h"
42 #include "graphics/graphics.h"
43 #include "utils/styles.h"
44 #include "textio/txcommands.h"
45 #include "graphics/glyphs.h"
46 #include "windows/windInt.h"
47 #include "tiles/tile.h"
48 #include "utils/hash.h"
49 #include "database/database.h"
50 #include "dbwind/dbwind.h"
51 #include "utils/utils.h"
52 #include "cif/cif.h"
53
54 /* Forward declarations */
55 void windDoMacro();
56
57 /*
58 * ----------------------------------------------------------------------------
59 *
60 * windBorderCmd --
61 *
62 * Change the flag which says whether new windows will have a border.
63 *
64 * Usage:
65 * windborder [on|off]
66 *
67 * Results:
68 * None.
69 *
70 * Side effects:
71 * A flag is changed.
72 * In Tcl, if no options are presented, the border status is returned
73 * in the command exit status.
74 *
75 * ----------------------------------------------------------------------------
76 */
77
78 void
windBorderCmd(w,cmd)79 windBorderCmd(w, cmd)
80 MagWindow *w;
81 TxCommand *cmd;
82 {
83 int place;
84 bool value;
85 static char *onoff[] = {"on", "off", 0};
86 static bool truth[] = {TRUE, FALSE};
87
88 if (cmd->tx_argc > 2) goto usage;
89 else if (cmd->tx_argc == 1)
90 {
91 if (w == (MagWindow *)NULL)
92 {
93 TxError("No window specified for caption command\n");
94 goto usage;
95 }
96
97 value = (w->w_flags & WIND_BORDER) ? 0 : 1;
98
99 #ifdef MAGIC_WRAPPER
100 Tcl_SetResult(magicinterp, onoff[value], TCL_STATIC);
101 #else
102 TxPrintf("Window border is %s\n", onoff[value]);
103 #endif
104 return;
105 }
106
107 place = Lookup(cmd->tx_argv[1], onoff);
108 if (place < 0)
109 goto usage;
110
111 if (truth[place])
112 {
113 WindDefaultFlags |= WIND_BORDER;
114 TxPrintf("New windows will have a border.\n");
115 }
116 else {
117 WindDefaultFlags &= ~WIND_BORDER;
118 TxPrintf("New windows will not have a border.\n");
119 }
120 return;
121
122 usage:
123 TxError("Usage: %s [on|off]\n", cmd->tx_argv[0]);
124 return;
125 }
126 /*
127 * ----------------------------------------------------------------------------
128 *
129 * windCaptionCmd --
130 *
131 * Change the flag which says whether new windows will have a title caption.
132 *
133 * Usage:
134 * windcaption [on|off]
135 *
136 * Results:
137 * None.
138 *
139 * Side effects:
140 * A flag is changed.
141 * In Tcl, if no options are presented, the window title caption is
142 * returned in the command status.
143 *
144 * ----------------------------------------------------------------------------
145 */
146
147 void
windCaptionCmd(w,cmd)148 windCaptionCmd(w, cmd)
149 MagWindow *w;
150 TxCommand *cmd;
151 {
152 int place;
153 Rect ts;
154 static char *onoff[] = {"on", "off", 0};
155 static bool truth[] = {TRUE, FALSE};
156
157 if (cmd->tx_argc > 2) goto usage;
158 else if (cmd->tx_argc == 1)
159 {
160 if (w == (MagWindow *)NULL)
161 {
162 TxError("No window specified for caption command\n");
163 goto usage;
164 }
165
166 #ifdef MAGIC_WRAPPER
167 Tcl_SetResult(magicinterp, w->w_caption, TCL_STATIC);
168 #else
169 TxPrintf("Window caption is \"%s\"\n", w->w_caption);
170 #endif
171 return;
172 }
173
174 place = Lookup(cmd->tx_argv[1], onoff);
175 if (place < 0)
176 goto usage;
177
178 if (truth[place])
179 {
180 WindDefaultFlags |= WIND_CAPTION;
181 TxPrintf("New windows will have a title caption.\n");
182 }
183 else {
184 WindDefaultFlags &= ~WIND_CAPTION;
185 TxPrintf("New windows will not have a title caption.\n");
186 }
187 return;
188
189 usage:
190 TxError("Usage: %s [on|off]\n", cmd->tx_argv[0]);
191 return;
192 }
193
194 /*
195 * ----------------------------------------------------------------------------
196 *
197 * windCenterCmd --
198 *
199 * Implement the "center" command.
200 * Move a window's view to center the point underneath the cursor, or to
201 * the specified coordinate (in surface units).
202 *
203 * Usage:
204 * center [x y]
205 * center horizontal|vertical f
206 *
207 * Results:
208 * None.
209 *
210 * Side effects:
211 * The view in the window underneath the cursor is changed
212 * to center the point underneath the cursor.
213 *
214 * ----------------------------------------------------------------------------
215 */
216
217 void
windCenterCmd(w,cmd)218 windCenterCmd(w, cmd)
219 MagWindow *w;
220 TxCommand *cmd;
221 {
222 Point rootPoint;
223 Rect newArea, oldArea;
224
225 if (w == NULL)
226 {
227 TxError("Point to a window first.\n");
228 return;
229 }
230
231 if (cmd->tx_argc == 1)
232 {
233 if ((w->w_flags & WIND_SCROLLABLE) == 0)
234 {
235 TxError("Sorry, can't scroll this window.\n");
236 return;
237 }
238 WindPointToSurface(w, &cmd->tx_p, &rootPoint, (Rect *) NULL);
239 }
240 else if (cmd->tx_argc == 3)
241 {
242 if ((w->w_flags & WIND_SCROLLABLE) == 0)
243 {
244 TxError("Sorry, can't scroll this window.\n");
245 return;
246 }
247 if (cmd->tx_argv[1][0] == 'h' || cmd->tx_argv[1][0] == 'v')
248 {
249 double frac;
250
251 if (!StrIsNumeric(cmd->tx_argv[2]))
252 {
253 TxError("Must specify a fractional value.\n");
254 return;
255 }
256 frac = atof(cmd->tx_argv[2]);
257 if (cmd->tx_argv[1][0] == 'h')
258 {
259 rootPoint.p_y = 0;
260 rootPoint.p_x = w->w_bbox->r_xbot + frac *
261 (w->w_bbox->r_xtop - w->w_bbox->r_xbot) -
262 (w->w_surfaceArea.r_xtop + w->w_surfaceArea.r_xbot)/2;
263 }
264 else
265 {
266 rootPoint.p_x = 0;
267 rootPoint.p_y = w->w_bbox->r_ybot + frac *
268 (w->w_bbox->r_ytop - w->w_bbox->r_ybot) -
269 (w->w_surfaceArea.r_ytop + w->w_surfaceArea.r_ybot)/2;
270 }
271 WindScroll(w, &rootPoint, (Point *)NULL);
272 return;
273 }
274 else
275 {
276 if (!StrIsInt(cmd->tx_argv[1]) || !StrIsInt(cmd->tx_argv[2]))
277 {
278 TxError("Coordinates must be integer values\n");
279 return;
280 }
281 rootPoint.p_x = atoi(cmd->tx_argv[1]);
282 rootPoint.p_y = atoi(cmd->tx_argv[2]);
283 }
284 }
285 else
286 {
287 TxError("Usage: center [x y]\n");
288 TxError(" center horizontal|vertical f\n");
289 return;
290 }
291
292 oldArea = w->w_surfaceArea;
293 newArea.r_xbot = rootPoint.p_x - (oldArea.r_xtop - oldArea.r_xbot)/2;
294 newArea.r_xtop = newArea.r_xbot - oldArea.r_xbot + oldArea.r_xtop;
295 newArea.r_ybot = rootPoint.p_y - (oldArea.r_ytop - oldArea.r_ybot)/2;
296 newArea.r_ytop = newArea.r_ybot - oldArea.r_ybot + oldArea.r_ytop;
297
298 WindMove(w, &newArea);
299 }
300
301
302 /*
303 * ----------------------------------------------------------------------------
304 * windCloseCmd --
305 *
306 * Close the window that is pointed at.
307 *
308 * Results:
309 * None.
310 *
311 * Side effects:
312 * The window is closed, and the client is notified. The client may
313 * refuse to have the window closed, in which case nothing happens.
314 * ----------------------------------------------------------------------------
315 */
316
317 void
windCloseCmd(w,cmd)318 windCloseCmd(w, cmd)
319 MagWindow *w;
320 TxCommand *cmd;
321 {
322 if ((cmd->tx_argc == 2) && GrWindowNamePtr)
323 {
324 char *mwname;
325
326 for (w = windTopWindow; w != (MagWindow *)NULL; w = w->w_nextWindow)
327 {
328 mwname = (*GrWindowNamePtr)(w);
329 if (!strcmp(mwname, cmd->tx_argv[1]))
330 break;
331 }
332 if (w == NULL)
333 {
334 TxError("Window named %s cannot be found\n", cmd->tx_argv[1]);
335 return;
336 }
337 }
338
339 if (w == (MagWindow *) NULL)
340 {
341 TxError("Point to a window first\n");
342 return;
343 }
344 if (!WindDelete(w))
345 {
346 TxError("Unable to close that window\n");
347 return;
348 }
349 }
350
351 #ifdef MAGIC_WRAPPER
352 /*
353 * ----------------------------------------------------------------------------
354 * windBypassCmd --
355 *
356 * Run a magic command independently of the command line. That is,
357 * if a command is being typed on the command line, the input
358 * redirection will not be reset by the execution of this command.
359 * To avoid having such commands interfere with the selection
360 * mechanism, save and restore the command count.
361 *
362 * Results:
363 * None.
364 *
365 * Side effects:
366 *
367 *
368 * ----------------------------------------------------------------------------
369 */
370
371 void
windBypassCmd(w,cmd)372 windBypassCmd(w, cmd)
373 MagWindow *w;
374 TxCommand *cmd;
375 {
376 int saveCount;
377
378 if (cmd->tx_argc == 1)
379 {
380 TxError("Usage: *bypass <command>\n");
381 return;
382 }
383
384 /* Dispatch the referenced command */
385 saveCount = TxCommandNumber;
386 TxTclDispatch((ClientData)w, cmd->tx_argc - 1, cmd->tx_argv + 1, FALSE);
387 TxCommandNumber = saveCount;
388 if (TxInputRedirect == TX_INPUT_PENDING_RESET)
389 TxInputRedirect = TX_INPUT_REDIRECTED;
390 }
391
392 #endif /* MAGIC_WRAPPER */
393
394 /*
395 * ----------------------------------------------------------------------------
396 * windCrashCmd --
397 *
398 * Generate a core dump.
399 *
400 * Results:
401 * None.
402 *
403 * Side effects:
404 * Dumps core by calling niceabort().
405 *
406 * ----------------------------------------------------------------------------
407 */
408
409 void
windCrashCmd(w,cmd)410 windCrashCmd(w, cmd)
411 MagWindow *w;
412 TxCommand *cmd;
413 {
414 if (cmd->tx_argc != 1)
415 {
416 TxError("Usage: *crash\n");
417 return;
418 }
419
420 TxPrintf("OK -- crashing...\n");
421 TxFlush();
422 niceabort();
423 }
424
425
426 /*
427 * ----------------------------------------------------------------------------
428 * windCursorCmd --
429 *
430 * Report the cursor position in Magic (internal) coordinates
431 * If an argument of a number is given, then the cursor icon
432 * is changed to the glyph of that number.
433 *
434 * Results:
435 * None.
436 *
437 * Side effects:
438 * Prints coordinates (non-Tcl version)
439 * Return value set to the cursor position as a list (Tcl version)
440 * ----------------------------------------------------------------------------
441 */
442
443 void
windCursorCmd(w,cmd)444 windCursorCmd(w, cmd)
445 MagWindow *w;
446 TxCommand *cmd;
447 {
448 Point p_in, p_out;
449 int resulttype = DBW_SNAP_INTERNAL;
450 double cursx, cursy, oscale;
451 DBWclientRec *crec;
452
453 #ifdef MAGIC_WRAPPER
454 Tcl_Obj *listxy;
455 #endif
456
457 if (cmd->tx_argc == 2)
458 {
459 if (StrIsInt(cmd->tx_argv[1]))
460 {
461 if (GrSetCursorPtr != NULL)
462 (*GrSetCursorPtr)(atoi(cmd->tx_argv[1]));
463 return;
464 }
465 else if (*cmd->tx_argv[1] == 'l')
466 {
467 resulttype = DBW_SNAP_LAMBDA;
468 }
469 else if (*cmd->tx_argv[1] == 'u')
470 {
471 resulttype = DBW_SNAP_USER;
472 }
473 else if (*cmd->tx_argv[1] == 'm')
474 {
475 resulttype = DBW_SNAP_MICRONS;
476 }
477 else if (*cmd->tx_argv[1] == 'w')
478 {
479 resulttype = -1; // Use this value for "window"
480 }
481 else if (*cmd->tx_argv[1] == 's')
482 {
483 resulttype = -2; // Use this value for "screen"
484 }
485 else if (*cmd->tx_argv[1] != 'i')
486 {
487 TxError("Usage: cursor glyphnum\n");
488 TxError(" (or): cursor [internal | lambda | microns | user | window]\n");
489 return;
490 }
491 }
492
493 if (GrGetCursorPosPtr == NULL)
494 return;
495
496 if (resulttype == -2)
497 GrGetCursorRootPos(w, &p_in);
498 else
499 GrGetCursorPos(w, &p_in);
500
501 if (resulttype >= 0)
502 {
503 WindPointToSurface(w, &p_in, &p_out, (Rect *)NULL);
504
505 /* Snap the cursor position if snap is in effect */
506 if (DBWSnapToGrid != DBW_SNAP_INTERNAL)
507 ToolSnapToGrid(w, &p_out, (Rect *)NULL);
508 }
509
510 /* Transform the result to declared units with option "lambda" or "grid" */
511 switch (resulttype) {
512 case -2:
513 case -1:
514 cursx = (double)p_in.p_x;
515 cursy = (double)p_in.p_y;
516 break;
517 case DBW_SNAP_INTERNAL:
518 cursx = (double)p_out.p_x;
519 cursy = (double)p_out.p_y;
520 break;
521 case DBW_SNAP_LAMBDA:
522 cursx = (double)(p_out.p_x * DBLambda[0]) / (double)DBLambda[1];
523 cursy = (double)(p_out.p_y * DBLambda[0]) / (double)DBLambda[1];
524 break;
525 case DBW_SNAP_MICRONS:
526 oscale = (double)CIFGetOutputScale(1000);
527 cursx = (double)(p_out.p_x * oscale);
528 cursy = (double)(p_out.p_y * oscale);
529 break;
530 case DBW_SNAP_USER:
531 crec = (DBWclientRec *)w->w_clientData;
532 cursx = (double)((p_out.p_x - crec->dbw_gridRect.r_xbot)
533 / (crec->dbw_gridRect.r_xtop - crec->dbw_gridRect.r_xbot));
534 cursy = (double)((p_out.p_y - crec->dbw_gridRect.r_ybot)
535 / (crec->dbw_gridRect.r_ytop - crec->dbw_gridRect.r_ybot));
536 break;
537 }
538
539 #ifdef MAGIC_WRAPPER
540 listxy = Tcl_NewListObj(0, NULL);
541 if ((cursx == round(cursx)) && (cursy == round(cursy)))
542 {
543 Tcl_ListObjAppendElement(magicinterp, listxy, Tcl_NewIntObj((int)cursx));
544 Tcl_ListObjAppendElement(magicinterp, listxy, Tcl_NewIntObj((int)cursy));
545 }
546 else
547 {
548 Tcl_ListObjAppendElement(magicinterp, listxy, Tcl_NewDoubleObj(cursx));
549 Tcl_ListObjAppendElement(magicinterp, listxy, Tcl_NewDoubleObj(cursy));
550 }
551 Tcl_SetObjResult(magicinterp, listxy);
552 #else
553 TxPrintf("%g %g\n", cursx, cursy);
554 #endif
555 }
556
557 /*
558 * ----------------------------------------------------------------------------
559 * windDebugCmd --
560 *
561 * Change to a new debugging mode.
562 *
563 * Results:
564 * None.
565 *
566 * Side effects:
567 * None.
568 * ----------------------------------------------------------------------------
569 */
570
571 void
windDebugCmd(w,cmd)572 windDebugCmd(w, cmd)
573 MagWindow *w;
574 TxCommand *cmd;
575 {
576 if (cmd->tx_argc != 1) goto usage;
577 windPrintCommands = !windPrintCommands;
578 TxError("Window command debugging set to %s\n",
579 (windPrintCommands ? "TRUE" : "FALSE"));
580 return;
581
582 usage:
583 TxError("Usage: *winddebug\n");
584 }
585
586 /*
587 * ----------------------------------------------------------------------------
588 * windDumpCmd --
589 *
590 * Dump out debugging info.
591 *
592 * Results:
593 * None.
594 *
595 * Side effects:
596 * None.
597 * ----------------------------------------------------------------------------
598 */
599
600 void
windDumpCmd(w,cmd)601 windDumpCmd(w, cmd)
602 MagWindow *w;
603 TxCommand *cmd;
604 {
605 (void) windDump();
606 }
607
608 #ifndef MAGIC_WRAPPER
609
610 /*
611 * ----------------------------------------------------------------------------
612 *
613 * windEchoCmd --
614 *
615 * Echo the arguments
616 *
617 * Results:
618 * None.
619 *
620 * Side effects:
621 * Text may appear on the terminal
622 *
623 * ----------------------------------------------------------------------------
624 */
625
626 void
windEchoCmd(w,cmd)627 windEchoCmd(w, cmd)
628 MagWindow *w;
629 TxCommand *cmd;
630 {
631 int i;
632 bool newline = TRUE;
633
634 for (i = 1; i < cmd->tx_argc; i++)
635 {
636 if (i != 1)
637 TxPrintf(" ");
638 if ( (i == 1) && (strcmp(cmd->tx_argv[i], "-n") == 0) )
639 newline = FALSE;
640 else
641 TxPrintf("%s", cmd->tx_argv[i]);
642 }
643
644 if (newline)
645 TxPrintf("\n");
646 TxFlush();
647 }
648
649 #endif
650
651
652 /*
653 * ----------------------------------------------------------------------------
654 *
655 * windFilesCmd --
656 *
657 * Find out what files are currently open.
658 *
659 * Usage:
660 * *files
661 *
662 * Side Effects:
663 * None.
664 *
665 * ----------------------------------------------------------------------------
666 */
667 /*ARGSUSED*/
668
669 void
windFilesCmd(w,cmd)670 windFilesCmd(w, cmd)
671 MagWindow *w;
672 TxCommand *cmd;
673 {
674 #define NUM_FD 20 /* max number of open files per process */
675 int fd;
676 struct stat buf;
677 int unopen, open;
678
679 open = unopen = 0;
680 for (fd = 0; fd < NUM_FD; fd++) {
681 if (fstat(fd, &buf) != 0) {
682 if (errno == EBADF)
683 unopen++;
684 else
685 TxError("file descriptor %d: %s\n", fd, strerror(errno));
686 }
687 else {
688 char *type;
689 switch (buf.st_mode & S_IFMT) {
690 case S_IFDIR: {type = "directory"; break;}
691 case S_IFCHR: {type = "character special"; break;}
692 case S_IFBLK: {type = "block special"; break;}
693 case S_IFREG: {type = "regular"; break;}
694 case S_IFLNK: {type = "symbolic link"; break;}
695 case S_IFSOCK: {type = "socket"; break;}
696 default: {type = "unknown"; break;}
697 }
698 TxError("file descriptor %d: open (type: '%s', inode number %ld)\n",
699 fd, type, buf.st_ino);
700 open++;
701 }
702 }
703 TxError("%d open files, %d unopened file descriptors left\n", open, unopen);
704 }
705
706
707 /*
708 * ----------------------------------------------------------------------------
709 *
710 * windGrowCmd --
711 *
712 * Grow a window to full-screen size or back to previous size.
713 *
714 * Results:
715 * None.
716 *
717 * Side effects:
718 * Text may appear on the terminal
719 *
720 * ----------------------------------------------------------------------------
721 */
722
723 void
windGrowCmd(w,cmd)724 windGrowCmd(w, cmd)
725 MagWindow *w;
726 TxCommand *cmd;
727 {
728 if (w == NULL)
729 {
730 TxError("Point to a window first.\n");
731 return;
732 };
733
734 WindFullScreen(w);
735 }
736
737
738 /*
739 * ----------------------------------------------------------------------------
740 * windGrstatsCmd --
741 *
742 * Take statistics on the graphics code.
743 *
744 * Results:
745 * None.
746 *
747 * Side effects:
748 * None.
749 * ----------------------------------------------------------------------------
750 */
751
752 void
windGrstatsCmd(w,cmd)753 windGrstatsCmd(w, cmd)
754 MagWindow *w;
755 TxCommand *cmd;
756 {
757 char *RunStats(), *rstatp;
758 static struct tms tlast, tdelta;
759 int i, style, count;
760 int us;
761 extern int GrNumClipBoxes;
762 int usPerRect, rectsPerSec;
763
764 if (cmd->tx_argc < 2 || cmd->tx_argc > 3)
765 {
766 TxError("Usage: grstats num [ style ]\n");
767 return;
768 }
769
770 if (!StrIsInt(cmd->tx_argv[1]) ||
771 (cmd->tx_argc == 3 && !StrIsInt(cmd->tx_argv[2])))
772 {
773 TxError("Count & style must be numeric\n");
774 return;
775 }
776 if (w == (MagWindow *) NULL)
777 {
778 TxError("Point to a window first.\n");
779 return;
780 }
781
782 count = atoi(cmd->tx_argv[1]);
783 if (cmd->tx_argc == 3)
784 style = atoi(cmd->tx_argv[2]);
785 else
786 style = -1;
787
788 WindUpdate();
789
790 if (style >= 0)
791 GrLock(w, TRUE);
792
793 (void) RunStats(RS_TINCR, &tlast, &tdelta);
794 GrNumClipBoxes = 0;
795 for (i = 0; i < count; i++)
796 {
797 if (SigInterruptPending)
798 break;
799 if (style < 0)
800 {
801 WindAreaChanged(w, (Rect *) NULL);
802 WindUpdate();
803 }
804 else
805 {
806 Rect r;
807 #define GRSIZE 15
808 #define GRSPACE 20
809 r.r_xbot = w->w_screenArea.r_xbot - GRSIZE/2;
810 r.r_ybot = w->w_screenArea.r_ybot - GRSIZE/2;
811 r.r_xtop = r.r_xbot + GRSIZE - 1;
812 r.r_ytop = r.r_ybot + GRSIZE - 1;
813 GrClipBox(&w->w_screenArea, STYLE_ERASEALL);
814 GrSetStuff(style);
815 while (r.r_xbot <= w->w_screenArea.r_xtop)
816 {
817 while (r.r_ybot <= w->w_screenArea.r_ytop)
818 {
819 GrFastBox(&r);
820 r.r_ybot += GRSPACE;
821 r.r_ytop += GRSPACE;
822 }
823 r.r_xbot += GRSPACE;
824 r.r_xtop += GRSPACE;
825 r.r_ybot = w->w_screenArea.r_ybot - GRSIZE/2;
826 r.r_ytop = r.r_ybot + GRSIZE - 1;
827 }
828 }
829 }
830 rstatp = RunStats(RS_TINCR, &tlast, &tdelta);
831
832 us = tdelta.tms_utime * (1000000 / 60);
833 usPerRect = us / MAX(1, GrNumClipBoxes);
834 rectsPerSec = 1000000 / MAX(1, usPerRect);
835 TxPrintf("[%s]\n%d rectangles, %d uS, %d uS/rectangle, %d rects/sec\n",
836 rstatp, GrNumClipBoxes, us, usPerRect, rectsPerSec);
837
838 if (style >= 0)
839 GrUnlock(w);
840 }
841
842
843 /*
844 * ----------------------------------------------------------------------------
845 * windHelpCmd --
846 *
847 * Just a dummy proc. (Only for this particular, global, client)
848 * This is just here so that there is an entry in our help table!
849 *
850 * Results:
851 * None.
852 *
853 * Side effects:
854 * None.
855 * ----------------------------------------------------------------------------
856 */
857
858 void
windHelpCmd(w,cmd)859 windHelpCmd(w, cmd)
860 MagWindow *w;
861 TxCommand *cmd;
862 {
863 ASSERT(FALSE, windHelpCmd);
864 }
865
866 static char *logKeywords[] =
867 {
868 "update",
869 0
870 };
871
872 /*
873 * ----------------------------------------------------------------------------
874 * windLogCommandsCmd --
875 *
876 * Log the commands and button pushes in a file.
877 *
878 * Results:
879 * None.
880 *
881 * Side effects:
882 * None.
883 * ----------------------------------------------------------------------------
884 */
885
886 void
windLogCommandsCmd(w,cmd)887 windLogCommandsCmd(w, cmd)
888 MagWindow *w;
889 TxCommand *cmd;
890 {
891 char *fileName;
892 bool update;
893
894 if ((cmd->tx_argc < 1) || (cmd->tx_argc > 3)) goto usage;
895
896 update = FALSE;
897
898 if (cmd->tx_argc == 1)
899 fileName = NULL;
900 else
901 fileName = cmd->tx_argv[1];
902
903 if (cmd->tx_argc == 3) {
904 int i;
905 i = Lookup(cmd->tx_argv[cmd->tx_argc - 1], logKeywords);
906 if (i != 0) goto usage;
907 update = TRUE;
908 }
909
910 TxLogCommands(fileName, update);
911 return;
912
913 usage:
914 TxError("Usage: %s [filename [update]]\n", cmd->tx_argv[0]);
915 }
916
917 /*
918 * ----------------------------------------------------------------------------
919 *
920 * windIntMacroCmd --
921 *
922 * Define a new interactive macro.
923 *
924 * Results:
925 * None.
926 *
927 * Side effects:
928 * Calls windDoMacro.
929 *
930 * ----------------------------------------------------------------------------
931 */
932
933 void
windIntMacroCmd(w,cmd)934 windIntMacroCmd(w, cmd)
935 MagWindow *w;
936 TxCommand *cmd;
937 {
938 windDoMacro(w, cmd, TRUE);
939 }
940
941 /*
942 * ----------------------------------------------------------------------------
943 *
944 * windMacroCmd --
945 *
946 * Define a new macro.
947 *
948 * Results:
949 * None.
950 *
951 * Side effects:
952 * Calls windDoMacro
953 *
954 * ----------------------------------------------------------------------------
955 */
956
957 void
windMacroCmd(w,cmd)958 windMacroCmd(w, cmd)
959 MagWindow *w;
960 TxCommand *cmd;
961 {
962 windDoMacro(w, cmd, FALSE);
963 }
964
965 /*
966 * ----------------------------------------------------------------------------
967 *
968 * windDoMacro --
969 *
970 * Working function for CmdIntMacro and CmdMacro
971 *
972 * Results:
973 * None.
974 *
975 * Side effects:
976 * Causes the macro package to define a new macro.
977 *
978 * ----------------------------------------------------------------------------
979 */
980
981 void
windDoMacro(w,cmd,interactive)982 windDoMacro(w, cmd, interactive)
983 MagWindow *w;
984 TxCommand *cmd;
985 bool interactive;
986 {
987 char *cp, *cn;
988 char nulltext[] = "";
989 char ch;
990 int ct, argstart, verbose;
991 bool any, iReturn;
992 bool do_list = FALSE;
993 bool do_help = FALSE;
994 bool do_reverse = FALSE;
995 char *searchterm = NULL;
996 macrodef *cMacro;
997 HashTable *clienttable;
998 HashEntry *h;
999 HashSearch hs;
1000 WindClient wc;
1001
1002 /* If the first argument is a window name, we attempt to */
1003 /* retrieve a client ID from it. This overrides the actual */
1004 /* window the command was called from, so technically we */
1005 /* can define macros for clients from inside other clients. */
1006 /* The main use, though, is to define macros for a client */
1007 /* from a script, rc file, or command-line. */
1008 /* Default to the layout window if the command has no */
1009 /* associated window. */
1010
1011 argstart = 1;
1012 if (cmd->tx_argc == 1)
1013 wc = DBWclientID; /* Added by NP 11/15/04 */
1014 else if (cmd->tx_argc > 1)
1015 wc = WindGetClient(cmd->tx_argv[1], TRUE);
1016
1017 while (cmd->tx_argc > argstart)
1018 {
1019 if (!strcmp(cmd->tx_argv[argstart], "list"))
1020 {
1021 do_list = TRUE;
1022 argstart++;
1023 }
1024 else if (!strcmp(cmd->tx_argv[argstart], "help"))
1025 {
1026 do_help = TRUE;
1027 argstart++;
1028 }
1029 else if (!strcmp(cmd->tx_argv[argstart], "search"))
1030 {
1031 if (cmd->tx_argc > (argstart + 1))
1032 {
1033 argstart++;
1034 searchterm = cmd->tx_argv[argstart];
1035 }
1036 argstart++;
1037 }
1038 else if (!strcmp(cmd->tx_argv[argstart], "-reverse"))
1039 {
1040 do_reverse = TRUE;
1041 argstart++;
1042 }
1043 else break;
1044 }
1045
1046 /* If client wasn't specified, use window default, else use */
1047 /* DBW client. */
1048
1049 if (wc == (WindClient)NULL)
1050 {
1051 if (w != NULL)
1052 wc = w->w_client;
1053 else
1054 wc = DBWclientID;
1055
1056 if (cmd->tx_argc > (argstart + 1))
1057 {
1058 /* The first argument, if there is one after resolving */
1059 /* all of the optional arugments, should be a key. */
1060 /* If it doesn't look like one, then check if the */
1061 /* next argument looks like a key, which would indicate */
1062 /* an unregistered client as the first argument. A */
1063 /* macro retrieved from an unregistered client returns */
1064 /* nothing but does not generate an error. */
1065
1066 if (MacroKey(cmd->tx_argv[argstart], &verbose) == 0)
1067 if (MacroKey(cmd->tx_argv[argstart + 1], &verbose) != 0)
1068 {
1069 wc = 0;
1070 argstart++;
1071 }
1072 }
1073 }
1074 else
1075 argstart++;
1076
1077 if (cmd->tx_argc == argstart)
1078 {
1079 h = HashLookOnly(&MacroClients, wc);
1080 if (h == NULL)
1081 return;
1082 else
1083 {
1084 clienttable = (HashTable *)HashGetValue(h);
1085 if (clienttable == (HashTable *)NULL)
1086 {
1087 TxError("No such client.\n");
1088 return;
1089 }
1090 }
1091
1092 any = FALSE;
1093 ch = 0;
1094 HashStartSearch(&hs);
1095 while (TRUE)
1096 {
1097 h = HashNext(clienttable, &hs);
1098 if (h == NULL) break;
1099 cMacro = (macrodef *) HashGetValue(h);
1100 if (cMacro == (macrodef *)NULL) break;
1101 cn = MacroName((spointertype)h->h_key.h_ptr);
1102
1103 /* "imacro list" returns only interactive macros. */
1104 if (interactive && !cMacro->interactive) continue;
1105
1106 if (do_help)
1107 cp = (cMacro->helptext == NULL) ? cMacro->macrotext : cMacro->helptext;
1108 else
1109 cp = cMacro->macrotext;
1110
1111 if (cp == (char *)NULL) cp = (char *)(&nulltext[0]);
1112
1113 if (searchterm != NULL)
1114 {
1115 /* Refine results by keyword search */
1116 if (!strstr(cp, searchterm)) continue;
1117 }
1118
1119 if (do_list)
1120 {
1121 #ifdef MAGIC_WRAPPER
1122 // NOTE: Putting cp before cn makes it easier to
1123 // generate a reverse lookup hash table for matching
1124 // against menu items, to automatically generate
1125 // the "accelerator" text.
1126 if (do_reverse)
1127 Tcl_AppendElement(magicinterp, cp);
1128 Tcl_AppendElement(magicinterp, cn);
1129 if (!do_reverse)
1130 Tcl_AppendElement(magicinterp, cp);
1131 #else
1132 TxPrintf("%s = \"%s\"\n", cn, cp);
1133 #endif
1134 }
1135 else
1136 {
1137 if (cMacro->interactive)
1138 TxPrintf("Interactive macro '%s' %s \"%s\"\n",
1139 cn, (do_help) ? "" : "contains", cp);
1140 else
1141 TxPrintf("Macro '%s' %s \"%s\"\n",
1142 cn, (do_help) ? "" : "contains", cp);
1143 }
1144 freeMagic(cn);
1145 any = TRUE;
1146 }
1147 if (!any)
1148 {
1149 if (!do_list) TxPrintf("No macros are defined for this client.\n");
1150 }
1151 return;
1152 }
1153 else if (cmd->tx_argc == (argstart + 1))
1154 {
1155 ct = MacroKey(cmd->tx_argv[argstart], &verbose);
1156 if (ct == 0)
1157 {
1158 if (verbose)
1159 TxError("Unrecognized macro name %s\n", cmd->tx_argv[argstart]);
1160 return;
1161 }
1162 if (do_help)
1163 cp = MacroRetrieveHelp(wc, ct);
1164 else
1165 cp = MacroRetrieve(wc, ct, &iReturn);
1166 if (cp != NULL)
1167 {
1168 cn = MacroName(ct);
1169 if (do_list)
1170 {
1171 #ifdef MAGIC_WRAPPER
1172 Tcl_SetResult(magicinterp, cp, TCL_VOLATILE);
1173 #else
1174 TxPrintf("%s\n", cp);
1175 #endif
1176 }
1177 else
1178 {
1179 if (iReturn)
1180 {
1181 TxPrintf("Interactive macro '%s' contains \"%s\"\n",
1182 cn, cp);
1183 }
1184 else
1185 {
1186 TxPrintf("Macro '%s' contains \"%s\"\n",
1187 cn, cp);
1188 }
1189 }
1190 freeMagic(cp);
1191 freeMagic(cn);
1192 }
1193 return;
1194 }
1195 else if (cmd->tx_argc == (argstart + 2))
1196 {
1197 int verbose;
1198 ct = MacroKey(cmd->tx_argv[argstart], &verbose);
1199 if (ct == 0)
1200 {
1201 if (verbose)
1202 TxError("Unrecognized macro name %s\n", cmd->tx_argv[argstart]);
1203 return;
1204 }
1205 argstart++;
1206 if (cmd->tx_argv[argstart][0] == '\0') MacroDelete(wc, ct);
1207 else if (do_help)
1208 MacroDefineHelp(wc, ct, cmd->tx_argv[argstart]);
1209 else if (interactive)
1210 MacroDefine(wc, ct, cmd->tx_argv[argstart], NULL, TRUE);
1211 else
1212 MacroDefine(wc, ct, cmd->tx_argv[argstart], NULL, FALSE);
1213 return;
1214 }
1215 else if (cmd->tx_argc == (argstart + 3))
1216 {
1217 int verbose;
1218 ct = MacroKey(cmd->tx_argv[argstart], &verbose);
1219 if (ct == 0)
1220 {
1221 if (verbose)
1222 TxError("Unrecognized macro name %s\n", cmd->tx_argv[argstart]);
1223 return;
1224 }
1225 argstart++;
1226 if (cmd->tx_argv[argstart][0] == '\0') MacroDelete(wc, ct);
1227 else if (interactive) MacroDefine(wc, ct, cmd->tx_argv[argstart],
1228 cmd->tx_argv[argstart + 1], TRUE);
1229 else MacroDefine(wc, ct, cmd->tx_argv[argstart],
1230 cmd->tx_argv[argstart + 1], FALSE);
1231 return;
1232 }
1233
1234 TxError("Usage: %s [macro_name [string] [help_text]]\n", cmd->tx_argv[0]);
1235 }
1236