1 /* windMain.c -
2  *
3  *	This package implements overlapping windows for the
4  *	Magic VLSI layout system.
5  *
6  * Design:
7  *	Windows are structures that are kept in a doubly linked list.
8  *	Windows near the front of the list are on top of windows further
9  *	towards the tail of the list.  Each window has some information
10  *	about what is in the window, as well as the size of the window
11  *	(both unclipped and clipped to accommodate windows that overlay it).
12  *	Transforms control what portion of the window's contents show up
13  *	on the screen, and at what magnification.
14  *
15  *	Each window is owned by a client (the database, a menu package, etc.).
16  *	The window package is notified of a new client by the AddClient
17  *	call.  The client supplies routines to redisplay the contents of
18  *	a window, and to do other things with the window (such as delete it).
19  *	Each window is a view onto a surface maintained by the client.  The
20  *	client may be asked to redisplay any part of this surface at any time.
21  *	The client must also supply each window with an ID that uniquely
22  *	identifies the surface.
23  *
24  *	There are currently two types of window packages supported:  Magic
25  *	windows (implemented here) and Sun Windows.  In Magic windows, all
26  * 	windows use the screen's coordinate system.  In Sun Windows, each
27  *	window has it's own coordinate system with (0, 0) being at the lower
28  *	left corner.  Also, under Sun Windows some of the screen managment
29  *	stuff (such as clipping to obscuring areas and drawing of the
30  *	screen background color) is ignored by us.
31  *
32  *
33  *     *********************************************************************
34  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
35  *     * Permission to use, copy, modify, and distribute this              *
36  *     * software and its documentation for any purpose and without        *
37  *     * fee is hereby granted, provided that the above copyright          *
38  *     * notice appear in all copies.  The University of California        *
39  *     * makes no representations about the suitability of this            *
40  *     * software for any purpose.  It is provided "as is" without         *
41  *     * express or implied warranty.  Export of this software outside     *
42  *     * of the United States of America may require an export license.    *
43  *     *********************************************************************
44  */
45 
46 #ifndef lint
47 static char rcsid[] __attribute__ ((unused)) = "$Header$";
48 #endif  /* not lint */
49 
50 #include <stdio.h>
51 #include <string.h>
52 #include <ctype.h>	/* for isalnum() */
53 
54 #include "utils/magic.h"
55 #include "utils/geometry.h"
56 #include "graphics/glyphs.h"
57 #include "windows/windows.h"
58 #include "windows/windInt.h"
59 #include "utils/stack.h"
60 #include "tiles/tile.h"
61 #include "utils/hash.h"
62 #include "database/database.h"
63 #include "textio/textio.h"
64 #include "graphics/graphics.h"
65 #include "utils/malloc.h"
66 #include "utils/utils.h"
67 #include "textio/txcommands.h"
68 
69 /* The type of windows that this package will implement */
70 int WindPackageType = WIND_MAGIC_WINDOWS;
71 
72 /* The size of our scroll bars -- may be set externally (see windows.h)
73  */
74 int WindScrollBarWidth = 7;
75 
76 /* ------ Internal variables that are global within the window package ----- */
77 clientRec *windFirstClientRec = NULL;	/* the head of the linked list
78 					 * of clients
79 					 */
80 MagWindow *windTopWindow = NULL;		/* the topmost window */
81 MagWindow *windBottomWindow = NULL;	/* ...and the bottom window */
82 extern Plane *windRedisplayArea;	/* See windDisplay.c for details. */
83 
84 
85 /*
86  * ----------------------------------------------------------------------------
87  * WindInit --
88  *
89  *	Initialize the window package.  No windows are created, but the
90  *	package will be initialized so that it can do these things in the
91  *	future.
92  *
93  * Results:
94  *	None.
95  *
96  * Side effects:
97  *	Variables internal to the window package are initialized.
98  * ----------------------------------------------------------------------------
99  */
100 
101 void
WindInit()102 WindInit()
103 {
104     extern Stack *windGrabberStack;
105     Rect ts;
106     char glyphName[30];
107 
108     windClientInit();
109     windGrabberStack = StackNew(2);
110     windRedisplayArea = DBNewPlane((ClientData) TT_SPACE);
111 
112     sprintf(glyphName, "windows%d", WindScrollBarWidth);
113     if (!GrReadGlyphs(glyphName, ".", SysLibPath, &windGlyphs))
114 	MainExit(1);
115     GrTextSize("XWyqP", GR_TEXT_DEFAULT, &ts);
116     windCaptionPixels = ts.r_ytop - ts.r_ybot + 3;
117     WindAreaChanged((MagWindow *) NULL, (Rect *) NULL);
118 }
119 
120 
121 /*
122  * ----------------------------------------------------------------------------
123  * WindAddClient --
124  *
125  *	Add a new client of the window package.  The client must supply a
126  *	set of routines, as described below.
127  *
128  * Results:
129  *	A unique ID (of type WindClient) is returned.
130  *	This is used to identify the client in future calls to the window
131  *	package.
132  *
133  * Routines supplied:
134  *
135  *	( A new window was just created for this client.  Do things to
136  *	  initialize the window, such as filling in the caption and making
137  *	  the contents be empty. The client may refuse to create a new
138  *	  window by returning FALSE, otherwise the client should return
139  *	  TRUE.  The client will get passed argc and argv, with the command
140  *	  name stripped off.  The client may do whatever it wants with this.
141  *	  It may even modify parts of the window record -- such as changing
142  *	  the window's location on the screen.)
143  *
144  *	bool
145  *	create(w, argc, argv)
146  *	    MagWindow *w;
147  *	    int argc;
148  *	    char *argv[];
149  *	{
150  *	}
151  *
152  *	( One of the client's windows is about to be deleted.  Do whatever
153  *	  needs to be done, such as freeing up dynamically allocated data
154  *	  structures. Fields manipulated by the window package, such as
155  *	  the caption, should not be freed by the client.  The client should
156  *	  normally return TRUE.  If the client returns FALSE, the window
157  *	  manager will refuse the request to delete the window.)
158  *
159  *	bool
160  *	delete(w)
161  *	    MagWindow *w;
162  *	{
163  *	}
164  *
165  *	( Redisplay an area of the screen.  The client is passed the window,
166  *	  the area in his coordinate system, and a clipping rectangle in
167  *	  screen coordinates. )
168  *
169  *	redisplay(w, clientArea, screenArea)
170  *	    MagWindow *w;
171  *	    Rect *clientArea, *screenArea;
172  *	{
173  *	}
174  *
175  *
176  *	( The window is about to be moved or resized.  This procedure will
177  *	  be called twice.
178  *
179  *	  The first time (with 'final' == FALSE), the window
180  *	  will be passed in 'w' as it is now and a suggested new w_screenarea
181  *	  is passed in 'newpos'.  The client is free to modify 'newpos' to
182  *	  be whatever screen location it desires.  The routine should not
183  *	  pass 'w' to any window procedure such as windMove since 'w' has
184  *	  the old transform, etc. instead of the new one.
185  *
186  *	  On the second call ('final' == TRUE), the window 'w' has all of
187  *	  it's fields updated, newpos is equal to w->w_frameArea, and the
188  *	  client is free to do things like windMove which require a window
189  *	  as an argument.  It should not modify newpos.
190  *
191  *	reposition(w, newpos, final)
192  *	    MagWindow *w;
193  *	    Rect *newpos	-- new w_framearea (screen area of window)
194  *	    bool final;
195  *	{
196  *	}
197  *
198  *
199  *	( A command has been issued to this window.  The client should
200  *	  process it.  It is split into Unix-style argc and argv words. )
201  *
202  *	command(w, client, cmd)
203  *	    MagWindow *w;
204  *	    TxCommand *cmd;
205  *	{
206  *	}
207  *
208  *	( A command has just finished.  Update any screen info that may have
209  *	  been changed as a result. )
210  *
211  *	update()
212  *	{
213  *	}
214  *
215  * Side effects:
216  *	Internal tables are expanded to include the new client.
217  * ----------------------------------------------------------------------------
218  */
219 
220 WindClient
WindAddClient(clientName,create,delete,redisplay,command,update,exitproc,reposition,icon)221 WindAddClient(clientName, create, delete, redisplay, command, update,
222 		exitproc, reposition, icon)
223     char *clientName;		/* A textual name for the client.  This
224 				 * name will be visable in the user
225 				 * interface as the name to use to switch
226 				 * a window over to a new client
227 				 */
228     bool (*create)();
229     bool (*delete)();
230     void (*redisplay)();
231     void (*command)();
232     void (*update)();
233     bool (*exitproc)();
234     void (*reposition)();
235     GrGlyph *icon;		/* An icon to draw when the window is closed.
236 				 * (currently for Sun Windows only).
237 				 */
238 {
239     clientRec *res;
240 
241     ASSERT( (clientName != NULL), "WindAddClient");
242     ASSERT( (command != NULL), "WindAddClient");
243 
244     res = (clientRec *) mallocMagic(sizeof(clientRec));
245     res->w_clientName = clientName;
246     res->w_create = create;
247     res->w_delete = delete;
248     res->w_redisplay = redisplay;
249     res->w_command = command;
250     res->w_update = update;
251     res->w_exit = exitproc;
252     res->w_reposition = reposition;
253     res->w_icon = icon;
254     res->w_nextClient = windFirstClientRec;
255 
256     /* The command and function tables are dynamically allocated.  */
257     /* Commands and functions should be registered with the client */
258     /* using the WindAddCommand() function.			   */
259 
260     res->w_commandTable = (char **)mallocMagic(sizeof(char *));
261     *(res->w_commandTable) = NULL;
262     res->w_functionTable = (void (**)())mallocMagic(sizeof(void (*)()));
263     *(res->w_functionTable) = NULL;
264 
265     windFirstClientRec = res;
266 
267     return (WindClient) res;
268 }
269 
270 
271 /*
272  * ----------------------------------------------------------------------------
273  * WindGetClient --
274  *
275  *	Looks up the unique ID of a client of the window package.
276  *
277  * Results:
278  *	A variable of type WindClient is returned if the client was found,
279  *	otherwise NULL is returned.
280  *
281  * Side effects:
282  *	None.
283  * ----------------------------------------------------------------------------
284  */
285 
286 WindClient
WindGetClient(clientName,exact)287 WindGetClient(clientName, exact)
288     char *clientName;	/* the textual name of the client */
289     bool exact;		/* must the name be exact, or are abbreviations allowed */
290 {
291     clientRec *cr, *found;
292     int length;
293 
294     /* Accept only an exact match */
295 
296     if (exact)
297     {
298 	for (cr = windFirstClientRec; cr != (clientRec *) NULL;
299 		cr = cr->w_nextClient)
300 	    if (!strcmp(clientName, cr->w_clientName))
301 		return (WindClient)cr;
302 	return (WindClient) NULL;
303     }
304 
305     /* Accept any unique abbreviation */
306 
307     found = NULL;
308     length = strlen(clientName);
309     for (cr = windFirstClientRec; cr != (clientRec *) NULL;
310 	    cr = cr->w_nextClient)
311     {
312 	if (!strncmp(clientName, cr->w_clientName, length))
313 	{
314 	    if (found != NULL) return (WindClient) NULL;
315 	    found = cr;
316 	}
317     }
318 
319     return (WindClient) found;
320 }
321 
322 /*
323  * ----------------------------------------------------------------------------
324  * WindNextClient --
325  *
326  *	Return the w_nextClient record to the caller as a WindClient
327  *	variable.  If "client" is 0, pass the first client record.
328  *	This allows the calling routine to enumerate all the known
329  *	clients.
330  *
331  * Results:
332  *	Type WindClient is returned.  If the end of the list is
333  *	reached, (WindClient)NULL (0) is returned.
334  *
335  * Side effects:
336  *	None.
337  * ----------------------------------------------------------------------------
338  */
339 
340 WindClient
WindNextClient(client)341 WindNextClient(client)
342     WindClient client;
343 {
344     clientRec *cr = (clientRec *)client;
345     int length;
346 
347     if (cr == NULL)
348 	return (WindClient)windFirstClientRec;
349     else
350 	return (WindClient)(cr->w_nextClient);
351 }
352 
353 /*
354  * ----------------------------------------------------------------------------
355  * WindPrintClientList --
356  *
357  *	Print the name of each client of the window package.
358  *
359  * Results:
360  *	None.
361  *
362  * Side effects:
363  *	None.
364  * ----------------------------------------------------------------------------
365  */
366 
367 void
WindPrintClientList(wizard)368 WindPrintClientList(wizard)
369     bool wizard;	/* If true print the names of ALL clients, even those
370 			 * that don't have user-visable windows */
371 {
372     clientRec *cr;
373 
374     for (cr = windFirstClientRec; cr != (clientRec *) NULL;
375 	    cr = cr->w_nextClient) {
376 	if (wizard || (cr->w_clientName[0] != '*'))
377 	    TxError("	%s\n", cr->w_clientName);
378 	}
379 }
380 
381 /*
382  * ----------------------------------------------------------------------------
383  * WindExecute --
384  *
385  *	Execute the command associated with a windClient
386  *
387  * Results:
388  *	Returns the command index on success.  Returns -1 if the
389  *	command was not found in the client's command list.  Returns
390  *	-2 if the procedure was sent an empty command.
391  *
392  * Side effects:
393  *	Whatever is done by the command execution.
394  *
395  * ----------------------------------------------------------------------------
396  */
397 
398 int
WindExecute(w,rc,cmd)399 WindExecute(w, rc, cmd)
400     MagWindow *w;
401     WindClient rc;
402     TxCommand *cmd;
403 {
404     int cmdNum;
405     clientRec *client = (clientRec *) rc;
406     char **commandTable = client->w_commandTable;
407     void (**functionTable)() = client->w_functionTable;
408 
409     if (cmd->tx_argc > 0)
410     {
411 	cmdNum = Lookup(cmd->tx_argv[0], commandTable);
412 
413 	if (cmdNum >= 0)
414 	{
415 	    (*functionTable[cmdNum])(w, cmd);
416 	    return cmdNum;
417 	}
418 	return -1;
419     }
420     return -2;
421 }
422 
423 /*
424  * ----------------------------------------------------------------------------
425  * WindAddCommand --
426  *
427  *	Add a command to the indicated client.  The command is passed
428  *	in "text", which also contains the (1-line) help text for the
429  *	command.  "func" is a function pointer, and "volatile" is TRUE
430  *	if the command "text" is dynamically allocated and must be
431  *	copied before adding to the client.
432  *
433  * Results:
434  *	None
435  *
436  * Side effects:
437  *	The memory allocated to the command and function pointers may
438  *	be reallocated and the entries in the client record updated.
439  *
440  * ----------------------------------------------------------------------------
441  */
442 
443 void
WindAddCommand(rc,text,func,dynamic)444 WindAddCommand(rc, text, func, dynamic)
445     WindClient rc;
446     char *text;
447     void (*func)();
448     bool dynamic;
449 {
450     int cidx, numCommands = 0;
451     clientRec *client = (clientRec *) rc;
452     char **commandTable = client->w_commandTable;
453     void (**functionTable)() = client->w_functionTable;
454     char **newcmdTable;
455     void (**newfnTable)();
456 
457     /* Find the number of commands and functions, increment by one, and */
458     /* Allocate a new array of pointers.				*/
459 
460     while (commandTable[numCommands] != NULL) numCommands++;
461     numCommands++;
462 
463     newcmdTable = (char **)mallocMagic((numCommands + 1) * sizeof(char *));
464     newfnTable = (void (**)())mallocMagic((numCommands + 1) * sizeof(void (*)()));
465 
466     /* Copy the old values, inserting the new command in alphabetical	*/
467     /* order.								*/
468 
469     for (cidx = 0; (commandTable[cidx] != NULL) &&
470 		(strcmp(commandTable[cidx], text) < 0); cidx++)
471     {
472 	newcmdTable[cidx] = commandTable[cidx];
473 	newfnTable[cidx] = functionTable[cidx];
474     }
475 
476     if (dynamic)
477 	newcmdTable[cidx] = StrDup(NULL, text);
478     else
479 	newcmdTable[cidx] = text;
480     newfnTable[cidx] = func;
481 
482     for (; commandTable[cidx] != NULL; cidx++)
483     {
484 	newcmdTable[cidx + 1] = commandTable[cidx];
485 	newfnTable[cidx + 1] = functionTable[cidx];
486     }
487 
488     newcmdTable[cidx + 1] = NULL;
489 
490     /* Release memory for the original pointers, and replace the	*/
491     /* pointers in the client record.					*/
492 
493     freeMagic(commandTable);
494     freeMagic(functionTable);
495 
496     client->w_commandTable = newcmdTable;
497     client->w_functionTable = newfnTable;
498 }
499 
500 /*
501  * ----------------------------------------------------------------------------
502  * WindReplaceCommand --
503  *
504  *	Change the function for the indicated command.  This routine
505  *	is mainly used by the Tcl module interface, where commands
506  *	are registered with the command interpreter pointing to an
507  *	"auto" function which loads the module, then calls this routine
508  *	to replace the auto-load function with the real one.
509  *
510  *      Note that this routine matches to the length of "command", then
511  *	checks one character beyond in the command table to ensure that
512  *	we don't inadvertently change a command which happens to be a
513  *	substring of the intended command.  In cases where this is
514  *	intended (e.g., "ext" and "extract"), the routine must be called
515  *	separately for each command string.
516  *
517  * Results:
518  *	0 on success, -1 if the command was not found.
519  *
520  * Side effects:
521  *	The clientRec structure for the DBWind interface is altered.
522  *
523  * ----------------------------------------------------------------------------
524  */
525 
526 int
WindReplaceCommand(rc,command,newfunc)527 WindReplaceCommand(rc, command, newfunc)
528     WindClient rc;
529     char *command;
530     void (*newfunc)();
531 {
532     int cidx, clen;
533     clientRec *client = (clientRec *) rc;
534     char **commandTable = client->w_commandTable;
535     void (**functionTable)() = client->w_functionTable;
536 
537     clen = strlen(command);
538 
539     for (cidx = 0; commandTable[cidx] != NULL; cidx++)
540 	if (!strncmp(commandTable[cidx], command, clen))
541 	    if (!isalnum(*(commandTable[cidx] + clen)))
542 	    {
543 		functionTable[cidx] = newfunc;
544 		return 0;
545 	    }
546 
547     return -1;
548 }
549 
550 /*
551  * ----------------------------------------------------------------------------
552  * WindGetCommandTable --
553  *
554  *	For functions wishing to parse the command table of a client
555  *	directly, this routine returns a pointer to the top of the
556  *	table.  The only purpose of this routine is to export the
557  *	w_commandTable value inside the clientRec structure, which is
558  *	not itself exported.
559  *
560  * Results:
561  *	A pointer to the top of the command table of the indicated
562  *	client.
563  *
564  * Side effects:
565  *	None.
566  * ----------------------------------------------------------------------------
567  */
568 
569 char **
WindGetCommandTable(rc)570 WindGetCommandTable(rc)
571     WindClient rc;
572 {
573     clientRec *client = (clientRec *) rc;
574     return client->w_commandTable;
575 }
576