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