1 /*
2  * DBWprocs.c --
3  *
4  *	Procedures to interface the database with the window package
5  *	for the purposes of window creation, deletion, and modification.
6  *
7  *     *********************************************************************
8  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
9  *     * Permission to use, copy, modify, and distribute this              *
10  *     * software and its documentation for any purpose and without        *
11  *     * fee is hereby granted, provided that the above copyright          *
12  *     * notice appear in all copies.  The University of California        *
13  *     * makes no representations about the suitability of this            *
14  *     * software for any purpose.  It is provided "as is" without         *
15  *     * express or implied warranty.  Export of this software outside     *
16  *     * of the United States of America may require an export license.    *
17  *     *********************************************************************
18  */
19 
20 #ifndef lint
21 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/dbwind/DBWprocs.c,v 1.3 2008/06/01 18:37:39 tim Exp $";
22 #endif  /* not lint */
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 
28 #include "utils/main.h"
29 #include "utils/magic.h"
30 #include "utils/geometry.h"
31 #include "windows/windows.h"
32 #include "tiles/tile.h"
33 #include "utils/hash.h"
34 #include "database/database.h"
35 #include "utils/main.h"
36 #include "commands/commands.h"
37 #include "dbwind/dbwind.h"
38 #include "graphics/graphics.h"
39 #include "textio/textio.h"
40 #include "textio/txcommands.h"
41 #include "utils/utils.h"
42 #include "utils/undo.h"
43 #include "graphics/glyphs.h"
44 #include "utils/malloc.h"
45 #include "utils/styles.h"
46 
47 global WindClient DBWclientID;
48 static int dbwBitMask = 0;
49 #define MAX_BITMASK INT_MAX
50 #define MAX_BITS_IN_MASK (sizeof(unsigned int) * 8 - 1)
51 
52 extern void DBWredisplay();		/* Defined in DBWdisplay.c */
53 
54 /* The following variable is used to allow somebody besides ourselves
55  * to take over button handling for database windows.  If it is NULL
56  * (which it usually is) we handle buttons.  If it isn't NULL, it gives
57  * the address of a procedure to handle buttons.
58  */
59 
60 static int (*dbwButtonHandler)() = NULL;
61 
62 /*
63  * ----------------------------------------------------------------------------
64  *
65  * DBWcreate
66  *
67  *	A new window has been created, create and initialize the needed
68  *	structures.
69  *
70  * Results:
71  *	FALSE if we have too many windows, TRUE otherwise.
72  *
73  * Side effects:
74  *	Load the given cell into the window.  If no cell is given, then
75  *	if the box exists, load a zoomed-in view of the box.  Otherwise
76  *	load a new blank cell.
77  *
78  * ----------------------------------------------------------------------------
79  */
80 
81 bool
DBWcreate(window,argc,argv)82 DBWcreate(window, argc, argv)
83     MagWindow *window;
84     int argc;
85     char *argv[];
86 {
87     int bitMask, newBitMask, expand;
88     DBWclientRec *crec;
89     CellDef *boxDef;
90     Rect box;
91 
92     /*
93      * See if we can create a new window without running out
94      * of bits in our bitMask.
95      */
96 
97     newBitMask = (dbwBitMask + 1) | dbwBitMask;
98     if (newBitMask > MAX_BITMASK)
99 	return FALSE;
100     bitMask = newBitMask ^ dbwBitMask;
101     dbwBitMask = newBitMask;
102 
103     crec = (DBWclientRec *) mallocMagic(sizeof(DBWclientRec));
104     crec->dbw_flags = DBW_SEELABELS | DBW_SEECELLS;
105     crec->dbw_watchPlane = -1;
106     crec->dbw_watchDef = (CellDef *) NULL;
107     crec->dbw_bitmask = bitMask;
108     crec->dbw_expandAmounts.r_xbot = 0;
109     crec->dbw_expandAmounts.r_ybot = 0;
110     crec->dbw_expandAmounts.r_xtop = 0;
111     crec->dbw_expandAmounts.r_ytop = 0;
112     crec->dbw_gridRect.r_xbot = 0;
113     crec->dbw_gridRect.r_ybot = 0;
114     crec->dbw_gridRect.r_xtop = 1;
115     crec->dbw_gridRect.r_ytop = 1;
116     crec->dbw_visibleLayers = DBAllTypeBits;
117     crec->dbw_hlErase = DBNewPlane((ClientData) TT_SPACE);
118     crec->dbw_hlRedraw = DBNewPlane((ClientData) TT_SPACE);
119     crec->dbw_labelSize = 0;
120     crec->dbw_scale = -1;
121     crec->dbw_surfaceArea.r_xbot = 0;
122     crec->dbw_surfaceArea.r_ybot = 0;
123     crec->dbw_surfaceArea.r_xtop = -1;
124     crec->dbw_surfaceArea.r_ytop = -1;
125     crec->dbw_origin.p_x = 0;
126     crec->dbw_origin.p_y = 0;
127 
128     window->w_clientData = (ClientData) crec;
129     if (argc > 0)
130 	DBWloadWindow(window, argv[0], DBW_LOAD_IGNORE_TECH);
131     else if (ToolGetBox(&boxDef, &box))
132     {
133 	DBWloadWindow(window, boxDef->cd_name, DBW_LOAD_IGNORE_TECH);
134 
135 	/* Zoom in on the box, leaving a 10% border or at least 2 units
136 	 * on each side.
137 	 */
138 
139 	expand = (box.r_xtop - box.r_xbot)/20;
140 	if (expand < 2) expand = 2;
141 	box.r_xtop += expand;
142 	box.r_xbot -= expand;
143 	expand = (box.r_ytop - box.r_ybot)/20;
144 	if (expand < 2) expand = 2;
145 	box.r_ytop += expand;
146 	box.r_ybot -= expand;
147 	WindMove(window, &box);
148     }
149     else
150     {
151 	DBWloadWindow(window, (char *) NULL, DBW_LOAD_IGNORE_TECH);
152     }
153     return TRUE;
154 }
155 
156 
157 /*
158  * ----------------------------------------------------------------------------
159  * DBWdelete --
160  *
161  *	Clean up the data structures before deleting a window.
162  *
163  * Results:
164  *	TRUE if we really want to delete the window, FALSE otherwise.
165  *
166  * Side effects:
167  *	A DBWclientRec is freed.
168  * ----------------------------------------------------------------------------
169  */
170 
171 bool
DBWdelete(window)172 DBWdelete(window)
173     MagWindow *window;
174 {
175     DBWclientRec *cr;
176 
177     cr = (DBWclientRec *) window->w_clientData;
178     dbwBitMask &= ~(cr->dbw_bitmask);
179     DBFreePaintPlane(cr->dbw_hlErase);
180     DBFreePaintPlane(cr->dbw_hlRedraw);
181     TiFreePlane(cr->dbw_hlErase);
182     TiFreePlane(cr->dbw_hlRedraw);
183     freeMagic( (char *) cr);
184     return TRUE;
185 }
186 
187 
188 /*
189  * ----------------------------------------------------------------------------
190  *
191  * dbwLoadFunc --
192  *
193  * 	This is a utility function passed to WindSearch by
194  *	DBWloadWindow.  Its job is to see if the edit cell is
195  *	present in any window except a given one.
196  *
197  * Results:
198  *	1 is returned if the window doesn't match the ClientData
199  *	but contains the Edit Cell.  0 is returned otherwise.
200  *
201  * Side effects:
202  *	None.
203  *
204  * ----------------------------------------------------------------------------
205  */
206 
207 int
dbwLoadFunc(w,clientData)208 dbwLoadFunc(w, clientData)
209     MagWindow *w;		/* A window found in the search. */
210     MagWindow *clientData;	/* Window to ignore (passed as ClientData). */
211 {
212     if (w == clientData) return 0;
213     if (((CellUse *) w->w_surfaceID)->cu_def == EditRootDef)
214 	return 1;
215     return 0;
216 }
217 
218 
219 /*
220  * ----------------------------------------------------------------------------
221  *
222  * DBWreload --
223  *
224  * Re-load all windows to contain the named cell as a root.
225  * This is intended to be called during startup or when restarting a saved
226  * image of Magic.
227  *
228  * Results:
229  *	None.
230  *
231  * Side effects:
232  *	Loads windows with the named cell.
233  *
234  * ----------------------------------------------------------------------------
235  */
236 
237 void
DBWreload(name)238 DBWreload(name)
239     char *name;
240 {
241     int dbwReloadFunc();
242 
243     (void) WindSearch(DBWclientID, (ClientData) NULL, (Rect *) NULL,
244 		dbwReloadFunc, (ClientData) name);
245 }
246 
247 int
dbwReloadFunc(w,name)248 dbwReloadFunc(w, name)
249     MagWindow *w;
250     char *name;
251 {
252     DBWloadWindow(w, name, DBW_LOAD_IGNORE_TECH);
253     return (0);
254 }
255 
256 /*
257  * ----------------------------------------------------------------------------
258  *
259  * DBWloadWindow
260  *
261  *	Replace the root cell of a window by the specified cell.
262  *
263  *	A cell name of NULL causes the cell with name "(UNNAMED)" to be
264  *	created if it does not already exist, or used if it does.
265  *
266  * Results:
267  *	None.
268  *
269  * Side effects:
270  *	If there is currently no edit use, or if this is the only
271  *	window containing the edit cell, then set the edit cell
272  *	to the topmost cell in the new window.  Otherwise, the edit
273  *	cell doesn't change.
274  *
275  * Flags:
276  *	If "expand" is true, unexpands all subcells of the root cell.
277  *	If "dereference" is true, ignore path reference in the input file.
278  *	If "fail" is true, do not create a new cell if no file is found.
279  *
280  * ----------------------------------------------------------------------------
281  */
282 
283 void
DBWloadWindow(window,name,flags)284 DBWloadWindow(window, name, flags)
285     MagWindow *window;		/* Identifies window to which cell is to be bound */
286     char *name;			/* Name of new cell to be bound to this window */
287     unsigned char flags;	/* See flags below */
288 {
289     CellDef *newEditDef, *deleteDef;
290     CellUse *newEditUse;
291     void DisplayWindow();
292     int res, newEdit, error_val;
293     int xadd, yadd;
294     Rect loadBox;
295     char *rootname;
296     bool isUnnamed;
297     int UnexpandFunc();		/* forward declaration */
298 
299     bool ignoreTech;	/* If FALSE, indicates that the technology of
300 			 * the layout must match the current technology.
301 			 */
302     bool expand;	/* Indicates whether or not to expand the cell */
303     bool dereference;	/* If TRUE, ignore path references in the input */
304     bool dofail;	/* If TRUE, do not create a cell if file not found */
305     bool beQuiet;	/* If TRUE, do not print messages during load */
306 
307     ignoreTech = ((flags & DBW_LOAD_IGNORE_TECH) == 0) ? FALSE : TRUE;
308     expand = ((flags & DBW_LOAD_EXPAND) == 0) ? FALSE : TRUE;
309     dereference = ((flags & DBW_LOAD_DEREFERENCE) == 0) ? FALSE : TRUE;
310     dofail = ((flags & DBW_LOAD_FAIL) == 0) ? FALSE : TRUE;
311     beQuiet = ((flags & DBW_LOAD_QUIET) == 0) ? FALSE : TRUE;
312 
313     loadBox.r_xbot = loadBox.r_ybot = 0;
314     loadBox.r_xtop = loadBox.r_ytop = 1;
315 
316     /* See if we're to change the edit cell */
317     newEdit = !WindSearch((WindClient) DBWclientID, (ClientData) NULL,
318 			(Rect *) NULL, dbwLoadFunc, (ClientData) window);
319 
320     /* The (UNNAMED) cell generally gets in the way, so delete it if	*/
321     /* any new cell is loaded and (UNNAMED) has no contents.		*/
322 
323     if (window->w_surfaceID == (ClientData)NULL)
324 	deleteDef = NULL;
325     else
326     {
327 	deleteDef = ((CellUse *)window->w_surfaceID)->cu_def;
328 	if (strcmp(deleteDef->cd_name, "(UNNAMED)") ||
329 		(GrDisplayStatus == DISPLAY_SUSPEND) ||
330 		deleteDef->cd_flags & (CDMODIFIED|CDBOXESCHANGED|CDSTAMPSCHANGED))
331 	    deleteDef = NULL;
332     }
333 
334     if ((name == (char *) NULL) || (name[0] == '\0'))
335     {
336 	/*
337 	 * If there is an existing unnamed cell, we use it.
338 	 * Otherwise, we create one afresh.
339 	 */
340 	newEditDef = DBCellLookDef(UNNAMED);
341 	if (newEditDef == (CellDef *) NULL)
342 	{
343 	    newEditDef = DBCellNewDef(UNNAMED);
344 	    DBCellSetAvail(newEditDef);
345 	}
346     }
347     else
348     {
349 	/*
350 	 * Name specified.
351 	 * First try to find it in main memory, then try to
352 	 * read it from disk.
353 	 */
354 
355 	char *dotptr;
356 
357 	rootname = strrchr(name, '/');
358 	if (rootname == NULL)
359 	    rootname = name;
360 	else
361 	    rootname++;
362 
363 	/* Strip off any ".mag" extension from the name */
364 	dotptr = strrchr(rootname, '.');
365 
366 	if (dotptr != NULL)
367 	    if (!strcmp(dotptr, ".mag"))
368 		*dotptr = '\0';
369 
370 	newEditDef = DBCellLookDef(rootname);
371 
372 	if ((newEditDef != (CellDef *)NULL) && (newEditDef->cd_file != NULL))
373  	{
374 	    /* If the cellname exists already, check if we are really	*/
375 	    /* looking at the same file.  If not, and two files in two	*/
376 	    /* different paths have the same root cellname, then keep	*/
377 	    /* the full pathname for the new cellname.			*/
378 
379 	    char *fullpath;
380 	    struct stat statbuf;
381 	    ino_t inode;
382 
383 	    if (DBTestOpen(name, &fullpath))
384 	    {
385 		if (stat(fullpath, &statbuf) == 0)
386 		{
387 		    inode = statbuf.st_ino;
388 		    if (stat(newEditDef->cd_file, &statbuf) == 0)
389 		    {
390 			if (inode != statbuf.st_ino)
391 			    newEditDef = (CellDef *)NULL;
392 		    }
393 		    else
394 			newEditDef = (CellDef *)NULL;
395 		}
396 		else
397 		    newEditDef = (CellDef *)NULL;
398 	    }
399 	    else
400 		newEditDef = (CellDef *)NULL;
401 
402 	    /* If the cells have the same name but different 	*/
403 	    /* paths, then give the new cell the full path name	*/
404 
405 	    if (newEditDef == NULL)
406 	    {
407 		if (dofail)
408 		{
409 		    if (!beQuiet)
410 			TxError("No file \"%s\" found or readable.\n", name);
411 		    return;
412 		}
413 		rootname = name;
414 		newEditDef = DBCellLookDef(rootname);
415 	    }
416 	}
417 	if (newEditDef == (CellDef *) NULL)
418 	{
419 	    /* "-fail" option:  If no file is readable, then do not	*/
420 	    /* create a new cell.					*/
421 	    if (dofail)
422 	    {
423 		if (!beQuiet)
424 		    TxError("No file \"%s\" found or readable.\n", name);
425 		return;
426 	    }
427 	    newEditDef = DBCellNewDef(rootname);
428 	}
429 
430 	if (dereference) newEditDef->cd_flags |= CDDEREFERENCE;
431 
432 	if (!DBCellRead(newEditDef, name, ignoreTech, dereference, &error_val))
433 	{
434 	    if (error_val == ENOENT)
435 	    {
436 		if (!beQuiet)
437 		    TxPrintf("Creating new cell\n");
438 		DBCellSetAvail(newEditDef);
439 	    }
440 	    else
441 	    {
442 		/* File exists but some error has occurred like
443 		 * file is unreadable or max file descriptors
444 		 * was reached, in which csae we don't want to
445 		 * create a new cell, so delete the new celldef
446 		 * and return.
447 		 */
448 		UndoDisable();
449 		DBCellDeleteDef(newEditDef);
450 		UndoEnable();
451 
452 		/*
453 		 * Go back to loading cell (UNNAMED) if
454 		 * there is no EditRootDef or EditCellUse.
455 		 * Otherwise, on error, keep whatever was already
456 		 * the root def & use.
457 		 */
458 		if (EditRootDef == NULL || EditCellUse == NULL)
459 		{
460 		    newEditDef = DBCellLookDef(UNNAMED);
461 		    if (newEditDef == (CellDef *) NULL)
462 		    {
463 			newEditDef = DBCellNewDef(UNNAMED);
464 			DBCellSetAvail(newEditDef);
465 		    }
466 		}
467 		else
468 		    return;
469 	    }
470 	}
471 	else
472 	{
473 	    /* DBCellRead doesn't dare to change bounding boxes, so
474 	     * we have to call DBReComputeBbox here (we know that it's
475 	     * safe).
476 	     */
477 	    DBReComputeBbox(newEditDef);
478 	    loadBox = newEditDef->cd_bbox;
479 	}
480     }
481 
482     /*
483      * Attach the new cell to the selected window.
484      */
485 
486     if (window != NULL)
487     {
488 	newEditUse = DBCellNewUse(newEditDef, (char *) NULL);
489 	(void) StrDup(&(newEditUse->cu_id), "Topmost cell in the window");
490 	DBExpand(newEditUse,
491 		((DBWclientRec *)window->w_clientData)->dbw_bitmask, TRUE);
492 
493 	if (expand)
494 	    DBExpandAll(newEditUse, &(newEditUse->cu_bbox),
495 			((DBWclientRec *)window->w_clientData)->dbw_bitmask,
496 			FALSE, UnexpandFunc, (ClientData)
497 			(((DBWclientRec *)window->w_clientData)->dbw_bitmask));
498 
499 	if (newEdit)
500 	{
501 	    if ((EditCellUse && EditRootDef) && (deleteDef == NULL))
502 	    {
503 		DBWUndoOldEdit(EditCellUse, EditRootDef,
504 			&EditToRootTransform, &RootToEditTransform);
505 		DBWUndoNewEdit(newEditUse, newEditDef,
506 			&GeoIdentityTransform, &GeoIdentityTransform);
507 	    }
508 	    if (newEditUse->cu_def->cd_flags & CDNOEDIT)
509 	    {
510 		newEdit = FALSE;
511 		EditCellUse = NULL;
512 		EditRootDef = NULL;
513 	    }
514 	    else
515 	    {
516 		EditCellUse = newEditUse;
517 		EditRootDef = newEditDef;
518 	    }
519 
520 	    EditToRootTransform = GeoIdentityTransform;
521 	    RootToEditTransform = GeoIdentityTransform;
522 	}
523 
524 	/* enforce a minimum size of 60 and a border of 10% around the sides */
525 	xadd = MAX(0, (60 - (loadBox.r_xtop - loadBox.r_xbot)) / 2) +
526 		(loadBox.r_xtop - loadBox.r_xbot + 1) / 10;
527 	yadd = MAX(0, (60 - (loadBox.r_ytop - loadBox.r_ybot)) / 2) +
528 		(loadBox.r_ytop - loadBox.r_ybot + 1) / 10;
529 	loadBox.r_xbot -= xadd;  loadBox.r_xtop += xadd;
530 	loadBox.r_ybot -= yadd;  loadBox.r_ytop += yadd;
531 
532 	window->w_bbox = &(newEditUse->cu_def->cd_bbox);
533 	res = WindLoad(window, DBWclientID, (ClientData) newEditUse, &loadBox);
534 	ASSERT(res, "DBWcreate");
535 
536 	/* Update the captions in all windows to reflect the new
537 	 * edit cell.  Also, if we've got a new edit cell, we need
538 	 * to explicitly ask for redisplay, because there could be
539 	 * another window on this cell somewhere else, and it needs
540 	 * to be redisplayed too (if it's just the new window, that
541 	 * is taken care of during WindLoad).
542 	 */
543 	CmdSetWindCaption(EditCellUse, EditRootDef);
544     }
545 
546     if (newEdit)
547 	DBWAreaChanged(newEditDef, &newEditDef->cd_bbox, DBW_ALLWINDOWS,
548 	    &DBAllButSpaceBits);
549 
550     /* If the cell before loading was (UNNAMED) and it was	*/
551     /* never modified, then delete it now.			*/
552 
553     /* Caveat: The (UNNAMED) cell could be on a push stack;	*/
554     /* that is not fatal but should be avoided.  Since the most	*/
555     /* common use is from the toolkit scripts, then make sure	*/
556     /* this doesn't happen within suspendall ... resumeall.	*/
557 
558     if (deleteDef != NULL)
559 	DBCellDelete(deleteDef->cd_name, TRUE);
560 }
561 
562 /* This function is called for each cell whose expansion status changed.
563  * It forces the cells area to be redisplayed, then returns 0 to keep
564  * looking for more cells to unexpand.
565  */
566 
567 int
UnexpandFunc(use,windowMask)568 UnexpandFunc(use, windowMask)
569     CellUse *use;		/* Use that was just unexpanded. */
570     int windowMask;		/* Window where it was unexpanded. */
571 {
572     if (use->cu_parent == NULL) return 0;
573     DBWAreaChanged(use->cu_parent, &use->cu_bbox, windowMask,
574 	    (TileTypeBitMask *) NULL);
575     return 0;
576 }
577 
578 
579 /*
580  * ----------------------------------------------------------------------------
581  * DBWexit --
582  *
583  *	Magic is about to exit.  Check to see if any cells need to be written.
584  *
585  * Results:
586  *	TRUE if it is OK to exit.
587  *	FALSE otherwise.
588  *
589  * Side effects:
590  *	The user is asked if he wants to write out any cells that are left.
591  * ----------------------------------------------------------------------------
592  */
593 
594 bool
DBWexit()595 DBWexit()
596 {
597     return (CmdWarnWrite() == 1);
598 }
599 
600 /*
601  * ----------------------------------------------------------------------------
602  *
603  * DBWcommands --
604  *
605  * 	This procedure is called by the window package whenever a
606  *	button is pushed or command is typed while the cursor is over
607  *	a database window.  This procedure dispatches to the handler
608  *	for the command.
609  *
610  * Results:
611  *	None.
612  *
613  * Side effects:
614  *	Whatever the command procedure does.
615  *
616  * ----------------------------------------------------------------------------
617  */
618 
619 void
DBWcommands(w,cmd)620 DBWcommands(w, cmd)
621     MagWindow *w;
622     TxCommand *cmd;
623 {
624     int cmdNum;
625 
626     /* If it was a keyboard command, just dispatch to the proper
627      * command routine.
628      */
629 
630     if (cmd->tx_button == TX_NO_BUTTON)
631     {
632 	WindExecute(w, DBWclientID, cmd);
633     }
634     else
635     {
636 	/* It's a button. */
637 
638 	(*DBWButtonCurrentProc)(w, cmd);
639     }
640 
641     UndoNext();
642 
643     /* Update timestamps now that it's safe */
644 
645     DBFixMismatch();
646 }
647 
648 /*
649  * ----------------------------------------------------------------------------
650  *
651  * DBWNewButtonHandler --
652  *
653  * 	This procedure permits anyone else in Magic to take over
654  *	handling of button pushes in database windows.  One example
655  *	of this is the netlist editor.
656  *
657  * Results:
658  *	The return value is the old button handler.  NULL means the
659  *	default handler (moving the box and painting) was the old
660  *	handler.  The caller must restore the old handler when it is
661  *	finished.
662  *
663  * Side effects:
664  *	From now on, all button pushes within database windows are
665  *	passed to buttonProc, in the following form:
666  *
667  *	    int
668  *	    buttonProc(w, cmd)
669  *		MagWindow *w;
670  *		TxCommand *cmd;
671  *	    {
672  *	    }
673  *
674  * ----------------------------------------------------------------------------
675  */
676 
677 int (*(DBWNewButtonHandler(buttonProc)))()
678     int (*buttonProc)();		/* New button handler. */
679 {
680     int (*result)();
681 
682     result = dbwButtonHandler;
683     dbwButtonHandler = buttonProc;
684     return result;
685 }
686 
687 /*
688  * ----------------------------------------------------------------------------
689  * DBWupdate--
690  *
691  *	This procedure is called once during each WindUpdate call.  It
692  *	takes care of redisplay stuff that's not already handled by
693  *	DBWAreaChanged (e.g. highlight redisplay).
694  *
695  * Results:
696  *	None.
697  *
698  * Side effects:
699  *	Stuff gets redisplayed.
700  * ----------------------------------------------------------------------------
701  */
702 
703 void
DBWupdate()704 DBWupdate()
705 {
706     DBWFeedbackShow();
707     DBWHLUpdate();
708 }
709 
710 /*
711  * ----------------------------------------------------------------------------
712  * DBWinit --
713  *
714  *	Initialize this module and open an initial window.
715  *
716  * Results:
717  *	None.
718  *
719  * Side effects:
720  *	A client is added, and an initial empty window is made
721  * ----------------------------------------------------------------------------
722  */
723 
724 void
DBWinit()725 DBWinit()
726 {
727     MagWindow *initialWindow;
728     int i;
729     static char *doc =
730 	"You are currently using the \"box\" tool.  The button actions are:\n"
731 	"   left    - move the box so its lower-left corner is at cursor position\n"
732 	"   right   - resize box by moving upper-right corner to cursor position\n"
733 	"   middle  - paint box area with material underneath cursor\n"
734 	"You can move or resize the box by different corners by pressing left\n"
735 	"    or right, holding it down, moving the cursor near a different corner\n"
736 	"    and clicking the other (left or right) button down then up without\n"
737 	"    releasing the initial button.\n";
738 
739     /* Initialize */
740     DBWclientID = WindAddClient("layout", DBWcreate, DBWdelete,
741 	    DBWredisplay, DBWcommands, DBWupdate, DBWexit,
742 	    (void (*)()) NULL,
743 	    (GrGlyph *) NULL);
744 
745     /* Add all of the commands for the DBWind interface.  These	*/
746     /* are defined in DBWCommands.c.  Commands added by other	*/
747     /* modules are registered with the client by each module.	*/
748 
749     DBWInitCommands();
750 
751     DBWHLAddClient(DBWDrawBox);
752     DBWAddButtonHandler("box", DBWBoxHandler, STYLE_CURS_NORMAL, doc);
753     (void) DBWChangeButtonHandler("box");
754 
755     UndoDisable();
756     DBCellInit();
757     DBUndoInit();
758     dbwUndoInit();
759 
760     /* Create initial window, but don't load anything into it. */
761     WIND_MAX_WINDOWS(MAX_BITS_IN_MASK);
762 
763 #ifdef MAGIC_WRAPPER
764     if (MakeMainWindow)
765 #endif
766 
767     initialWindow = WindCreate(DBWclientID, (Rect *) NULL, TRUE,
768 	0, (char **) NULL);
769 
770 #ifndef MAGIC_WRAPPER
771     ASSERT(initialWindow != (MagWindow *) NULL, "DBWinit");
772 #endif
773 
774     /* Other initialization routines in the DBW package */
775     dbwFeedbackInit();
776     dbwElementInit();
777     dbwCrosshairInit();
778 
779     UndoEnable();
780 }
781