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