1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 
9 #include <dxconfig.h>
10 #include "../base/defines.h"
11 
12 #if defined(HAVE_IOSTREAM)
13 #  include <iostream>
14 #else
15 #  if defined(HAVE_IOSTREAM_H)
16 #  include <iostream.h>
17 #  endif
18 #  if defined(HAVE_STREAM_H)
19 #  include <stream.h>
20 #  endif
21 #endif
22 
23 #include <Xm/RowColumn.h>
24 #include <Xm/CascadeB.h>
25 #include <Xm/Separator.h>
26 #include "DXWindow.h"
27 #include "DXApplication.h"
28 //#include "QuitCommand.h"
29 #include "CommandInterface.h"
30 #include "CommandScope.h"
31 #include "Command.h"
32 #include "ButtonInterface.h"
33 #include "ToggleButtonInterface.h"
34 #include "Network.h"
35 #include "ProcessGroupManager.h"
36 #include "ControlPanelAccessDialog.h"
37 #include "ErrorDialogManager.h"
38 #include "NoUndoDXWindowCommand.h"
39 #include "anchor.bm"
40 #include "OpenFileCommand.h"
41 #include "ListIterator.h"
42 #include "CascadeMenu.h"
43 
44 Symbol DXWindow::lastMsg = 0;
45 const void *DXWindow::lastMsgData = NULL;
46 
47 #include "DXWDefaultResources.h"
48 
DXWindow(const char * name,boolean isAnchor,boolean usesMenuBar)49 DXWindow::DXWindow(const char*   name,
50 		   boolean isAnchor, boolean usesMenuBar):
51 		IBMMainWindow(name, usesMenuBar)
52 {
53     this->anchor       = isAnchor;
54     this->startup      = FALSE;
55     this->anchorPixmap = NUL(Pixmap);
56     this->anchorButton = NUL(Widget);
57 
58     this->executeMenu    	= NUL(Widget);
59     this->executeMenuPulldown   = NUL(Widget);
60     this->executeOnceOption     = NUL(CommandInterface*);
61     this->executeOnChangeOption = NUL(CommandInterface*);
62     this->endExecutionOption    = NUL(CommandInterface*);
63     this->sequencerOption       = NUL(CommandInterface*);
64 
65     this->connectionMenu 	= NUL(Widget);
66     this->connectionMenuPulldown = NUL(Widget);
67     this->startServerOption          = NUL(CommandInterface*);
68     this->disconnectFromServerOption = NUL(CommandInterface*);
69     this->resetServerOption          = NUL(CommandInterface*);
70     this->processGroupAssignmentOption = NUL(CommandInterface*);
71 
72     this->panelAccessDialog = NULL;
73 
74     this->toggleWindowStartupCmd =  NULL;
75     this->toggleWindowStartupOption = NULL;
76 
77     this->helpTutorialOption = NUL(CommandInterface*);
78 
79     ASSERT(!usesMenuBar || this->commandScope);
80 }
81 
~DXWindow()82 DXWindow::~DXWindow()
83 {
84     //
85     // Cause the window to be taken of the display immediately.
86     // This is useful when reading in a new network when we don't
87     // get to the X main loop until after deleting the current net
88     // and reading in the next so that windows from the last network
89     // can be left up until after the new network is read in.
90     //
91     if (this->getRootWidget() && XtIsRealized(this->getRootWidget()))
92 	XtUnmapWidget(this->getRootWidget());
93 
94     if (this->anchorPixmap && this->menuBar)
95     {
96 	XFreePixmap(XtDisplay(this->menuBar), this->anchorPixmap);
97     }
98 
99     //
100     // Execute menu
101     //
102     if (this->executeOnceOption) delete this->executeOnceOption;
103     if (this->executeOnChangeOption) delete this->executeOnChangeOption;
104     if (this->endExecutionOption) delete this->endExecutionOption;
105     if (this->sequencerOption) delete this->sequencerOption;
106 
107     //
108     // Connection menu
109     //
110     if (this->startServerOption) delete this->startServerOption;
111     if (this->disconnectFromServerOption)
112 			delete this->disconnectFromServerOption;
113     if (this->resetServerOption) delete this->resetServerOption;
114     if (this->processGroupAssignmentOption)
115 			delete this->processGroupAssignmentOption;
116 
117     if (this->panelAccessDialog) delete this->panelAccessDialog;
118 
119     if (this->helpTutorialOption) delete this->helpTutorialOption;
120 
121     if (this->toggleWindowStartupOption)
122 	delete this->toggleWindowStartupOption;
123 
124     if (this->toggleWindowStartupCmd)
125         delete this->toggleWindowStartupCmd;
126 
127     //
128     // file history menu
129     //
130     ListIterator iter(this->file_history_buttons);
131     ButtonInterface* bi;
132     while ((bi=(ButtonInterface*)iter.getNext())) delete bi;
133     iter.setList(this->file_history_commands);
134     Command* cmd;
135     while ((cmd=(Command*)iter.getNext())) delete cmd;
136 }
beginExecution()137 void DXWindow::beginExecution()
138 {
139     // Make sure the widget present
140     if (this->executeMenu != NULL) {
141         XtVaSetValues(this->executeMenu,
142                   XmNforeground,
143                   theDXApplication->getExecutionHighlightForeground(),
144                   NULL);
145     }
146 }
standBy()147 void DXWindow::standBy()
148 {
149     // Make sure the widget present
150     if (this->executeMenu != NULL) {
151         XtVaSetValues(this->executeMenu,
152                   XmNforeground,
153                   theDXApplication->getBackgroundExecutionForeground(),
154                   NULL);
155     }
156 }
endExecution()157 void DXWindow::endExecution()
158 {
159     // Make sure the widget present
160     if (this->executeMenu != NULL) {
161         XtVaSetValues(this->executeMenu,
162                   XmNforeground,
163                   theDXApplication->getForeground(),
164                   NULL);
165     }
166 }
serverDisconnected()167 void DXWindow::serverDisconnected()
168 {
169     this->endExecution();
170 }
171 
172 
173 //
174 // Install the default resources for this class.
175 //
installDefaultResources(Widget baseWidget)176 void DXWindow::installDefaultResources(Widget baseWidget)
177 {
178     this->setDefaultResources(baseWidget, DXWindow::DefaultResources);
179     this->IBMMainWindow::installDefaultResources(baseWidget);
180 }
181 
182 
createMenuBar(Widget parent)183 void DXWindow::createMenuBar(Widget parent)
184 {
185     this->IBMMainWindow::createMenuBar(parent);
186     this->setAnchor(this->anchor);
187 }
188 
189 //
190 // Create the anchor button widget and add the pixmap if desired and not
191 // already present
192 //
createAnchor(boolean addPixmap)193 void DXWindow::createAnchor(boolean addPixmap)
194 {
195     ASSERT(this->menuBar);
196 
197     if (!this->anchorButton) {
198 	this->anchorButton = XtVaCreateWidget
199 				("anchorButton",
200 				 xmCascadeButtonWidgetClass,
201 				 this->menuBar,
202 				 XmNlabelType,	XmPIXMAP,
203 				 NULL);
204 	XtUninstallTranslations (this->anchorButton);
205     }
206     //
207     // If an anchor pixmap has not yet been created, do it now.
208     //  (This code should be executed only once.)
209     //
210     if (addPixmap && !this->anchorPixmap)
211     {
212 	Pixel foreground;
213 	Pixel background;
214 	int   depth;
215         //
216         // Create the anchor button.
217         //
218         XtVaGetValues (this->menuBar,
219              XmNdepth,      &depth,
220              NULL);
221 
222 	//
223 	// It's bad to use a color name here.
224 	// I made the pixmap placement happen earlier to avoid the
225 	// flashing which resulted when doing it at a convenient time.
226 	// If you fetch colors from this->menuBar now, then on an hp700 you
227 	// wind up with workspace default colors because that's what hpvue wants.
228 	//
229 #     if defined(hp700) && 0
230 	XrmValue from, toinout;
231 	from.addr = "#b4b4b4b4b4b4";
232 	from.size = 1+strlen(from.addr);
233 	toinout.addr = (XPointer)&background;
234 	toinout.size = sizeof(Pixel);
235 	XtConvertAndStore(this->menuBar, XmRString, &from, XmRPixel, &toinout);
236 #     else
237 	XtVaGetValues (this->getRootWidget(), XmNbackground, &background, NULL);
238 #     endif
239 	foreground = theDXApplication->getForeground();
240 
241 	Window wind;
242 	Screen *scr = XtScreen(this->menuBar);
243 	wind = RootWindowOfScreen(scr);
244         this->anchorPixmap =
245             XCreatePixmapFromBitmapData
246                 (XtDisplay(this->menuBar),
247                  wind,
248                  anchor_bits,
249                  anchor_width,
250                  anchor_height,
251                  foreground,
252                  background,
253                  depth);
254 
255         XtVaSetValues
256             (this->anchorButton,
257              XmNlabelType,              XmPIXMAP,
258              XmNlabelPixmap,            this->anchorPixmap,
259              XmNlabelInsensitivePixmap, this->anchorPixmap,
260              NULL);
261 
262     }
263 
264 }
265 
closeWindow()266 void DXWindow::closeWindow()
267 {
268     if (theDXApplication->appAllowsExitOptions()) {
269 	if (this->anchor)
270 	    theDXApplication->exitCmd->execute();
271 	else
272 	    this->unmanage();
273     } else {
274 	this->iconify();
275     }
276 }
277 
manage()278 void DXWindow::manage()
279 {
280 
281     this->IBMMainWindow::manage();
282 
283     //
284     // Get notified with the last message.
285     //
286     this->notify(DXWindow::lastMsg,DXWindow::lastMsgData);
287 
288     this->setAnchor(this->anchor);
289 }
290 
notify(const Symbol message,const void * data,const char * msg)291 void DXWindow::notify(const Symbol message, const void *data, const char *msg)
292 {
293     if(NOT message)   // no previouse message.
294 	return;
295 
296     if (message == DXApplication::MsgExecute)
297     {
298     	DXWindow::lastMsgData = data;
299     	DXWindow::lastMsg = message;
300 	this->beginExecution();
301     }
302     else if (message == DXApplication::MsgStandBy)
303     {
304     	DXWindow::lastMsgData = data;
305     	DXWindow::lastMsg = message;
306 	this->standBy();
307     }
308     else if (message == DXApplication::MsgExecuteDone)
309     {
310     	DXWindow::lastMsgData = data;
311     	DXWindow::lastMsg = message;
312 	this->endExecution();
313     }
314     else if (message == DXApplication::MsgServerDisconnected)
315     {
316   	this->serverDisconnected();
317     }
318     else if (message == Command::MsgBeginExecuting)
319     {
320 	this->beginCommandExecuting();
321     }
322     else if (message == Command::MsgEndExecuting)
323     {
324 	this->endCommandExecuting();
325     }
326     else if (message == DXApplication::MsgPanelChanged)
327     {
328 	if (this->panelAccessDialog && this->panelAccessDialog->isManaged()) {
329 	    this->panelAccessDialog->update();
330 	    if (this->title) {
331 		char *s = new char[STRLEN(this->title) + 32];
332 		sprintf(s,"Control Panel Access:%s",this->title);
333 		this->panelAccessDialog->setDialogTitle(s);
334 		delete s;
335 	    } else
336                 this->panelAccessDialog->setDialogTitle(
337 					"Control Panel Access...");
338   	}
339     }
340     else
341 	this->IBMMainWindow::notify(message,data,msg);
342 }
343 
344 //
345 // Virtual function called at the beginning of Command::execute.
346 //
beginCommandExecuting()347 void DXWindow::beginCommandExecuting()
348 {
349     return;
350 }
351 //
352 // Virtual function called at the end of Command::execute.
353 //
endCommandExecuting()354 void DXWindow::endCommandExecuting()
355 {
356     return;
357 }
358 
createExecuteMenu(Widget parent)359 void DXWindow::createExecuteMenu(Widget parent)
360 {
361     ASSERT(parent);
362     Widget            pulldown;
363 
364     if (!theDXApplication->appAllowsExecuteMenus())
365 	return;
366 
367     //
368     // Create "Execute" menu and options.
369     //
370     pulldown =
371 	this->executeMenuPulldown =
372 	    XmCreatePulldownMenu
373 		(parent, "executeMenuPulldown", NUL(ArgList), 0);
374 
375     this->executeMenu =
376 	XtVaCreateManagedWidget
377 	    ("executeMenu",
378 	     xmCascadeButtonWidgetClass,
379 	     parent,
380 	     XmNsubMenuId, pulldown,
381 	     NULL);
382 
383     this->executeOnceOption =
384 	new ButtonInterface(pulldown,
385 			    "executeOnceOption",
386 			    theDXApplication->executeOnceCmd);
387 
388     this->executeOnChangeOption =
389 	new ButtonInterface(pulldown,
390 			    "executeOnChangeOption",
391 			    theDXApplication->executeOnChangeCmd);
392 
393     this->endExecutionOption =
394 	new ButtonInterface(pulldown,
395 			    "endExecutionOption",
396 			    theDXApplication->endExecutionCmd);
397 
398     XtVaCreateManagedWidget
399 	    ("optionSeparator", xmSeparatorWidgetClass, pulldown, NULL);
400 
401     this->sequencerOption =
402 	new ButtonInterface(pulldown,
403 			    "sequencerOption",
404 			    theDXApplication->openSequencerCmd);
405 #if USE_REMAP	// 6/14/93
406     XtVaCreateManagedWidget
407 	    ("optionSeparator", xmSeparatorWidgetClass, pulldown, NULL);
408 
409     Network *network = theDXApplication->network;
410     //this->sequencerOption =
411 	new ToggleButtonInterface(pulldown,
412 			    "remapInteractorOutputsOption",
413 			    theDXApplication->toggleRemapInteractorsCmd,
414 			    (network ? network->isRemapInteractorOutputMode() :
415 				DEFAULT_REMAP_INTERACTOR_MODE));
416 #endif
417 }
418 
419 
createHelpMenu(Widget parent)420 void DXWindow::createHelpMenu(Widget parent)
421 {
422     boolean addHelp = theDXApplication->appAllowsDXHelp();
423     // this->createBaseHelpMenu(parent, addHelp, addHelp && this->isAnchor());
424     // if (theDXApplication->appAllowsDXHelp() && this->isAnchor()) {
425     this->createBaseHelpMenu(parent, addHelp, addHelp);
426     if (addHelp) {
427 	XtVaCreateManagedWidget("separator", xmSeparatorWidgetClass,
428                                         this->helpMenuPulldown,
429                                         NULL);
430         this->helpTutorialOption =
431             new ButtonInterface(this->helpMenuPulldown, "helpTutorialOption",
432                 theDXApplication->helpTutorialCmd);
433     }
434 
435 }
436 
437 
createConnectionMenu(Widget parent)438 void DXWindow::createConnectionMenu(Widget parent)
439 {
440     ASSERT(parent);
441     ASSERT(this->menuBar);
442 
443     Widget            pulldown;
444 
445     if (!theDXApplication->appAllowsConnectionMenus())
446 	return;
447 
448     //
449     // Create "Connection" menu and options.
450     //
451     pulldown =
452         this->connectionMenuPulldown =
453             XmCreatePulldownMenu
454                 (parent, "connectionMenuPulldown", NUL(ArgList), 0);
455     this->connectionMenu =
456         XtVaCreateManagedWidget
457             ("connectionMenu",
458              xmCascadeButtonWidgetClass,
459              parent,
460              XmNsubMenuId, pulldown,
461              NULL);
462 
463     XtAddCallback(pulldown,
464                   XmNmapCallback,
465                   (XtCallbackProc)DXWindow_ConnectionMenuMapCB,
466                   (XtPointer)this);
467 
468     this->startServerOption =
469         new ButtonInterface(pulldown, "startServerOption",
470                             theDXApplication->connectToServerCmd);
471 
472     this->disconnectFromServerOption =
473         new ButtonInterface(pulldown, "disconnectFromServerOption",
474                             theDXApplication->disconnectFromServerCmd);
475 
476     this->resetServerOption =
477         new ButtonInterface(pulldown, "resetServerOption",
478                             theDXApplication->resetServerCmd);
479 
480 
481     if (theDXApplication->appAllowsPGroupAssignmentChange()) {
482 	XtVaCreateManagedWidget("optionSeparator",
483 			    xmSeparatorWidgetClass, pulldown, NULL);
484 
485 	this->processGroupAssignmentOption =
486 	    new ButtonInterface(pulldown, "processGroupAssignmentOption",
487                             theDXApplication->assignProcessGroupCmd);
488 
489     }
490 
491 }
492 
DXWindow_ConnectionMenuMapCB(Widget w,XtPointer clientdata,XtPointer calldata)493 extern "C" void DXWindow_ConnectionMenuMapCB(Widget w,
494                                  XtPointer clientdata,
495                                  XtPointer calldata)
496 {
497 #if WORKSPACE_PAGES
498     GroupManager *gmgr = theDXApplication->getProcessGroupManager();
499     if(gmgr->getGroupCount())
500 #else
501     if(theDXApplication->PGManager->getGroupCount())
502 #endif
503         theDXApplication->assignProcessGroupCmd->activate();
504     else
505         theDXApplication->assignProcessGroupCmd->deactivate();
506 }
507 
508 //
509 // Post the panel access dialog with this window PanelAccessManager info.
510 //
postPanelAccessDialog(PanelAccessManager * pam)511 void DXWindow::postPanelAccessDialog( PanelAccessManager *pam)
512 {
513 
514     if (!this->panelAccessDialog) {
515 	ASSERT(pam);
516 	this->panelAccessDialog = new ControlPanelAccessDialog(
517 			this->getRootWidget(),
518 			//theDXApplication->getRootWidget(),
519 			pam);
520     }
521 
522     this->panelAccessDialog->post();
523     if (this->title) {
524         char *s = new char[STRLEN(this->title) + 32];
525 	sprintf(s,"Control Panel Access:%s",this->title);
526         this->panelAccessDialog->setDialogTitle(s);
527         delete s;
528     } else
529         this->panelAccessDialog->setDialogTitle("Control Panel Access...");
530 }
printComment(FILE * f)531 boolean DXWindow::printComment(FILE *f)
532 {
533     int xsize, ysize, xpos, ypos;
534 
535     if (!this->getGeometry(&xpos, &ypos, &xsize,&ysize))
536 	return TRUE;
537 
538     if (!UIComponent::PrintGeometryComment(f,xpos,ypos,xsize,ysize))
539 	return FALSE;
540 
541     return TRUE;
542 }
parseComment(const char * line,const char * file,int lineno)543 boolean DXWindow::parseComment(const char *line, const char *file,
544 				int lineno)
545 {
546     int items, xsize=0, ysize=0, xpos=0, ypos=0;
547     float norm_xsize, norm_ysize, norm_xpos, norm_ypos;
548     int display_xsize, display_ysize;
549 
550 
551     if (!EqualSubstring(line," window: position =",19)) {
552 #if DX_MAJOR_VERSION == 3 && DX_MINOR_VERSION == 0 &&  DX_MICRO_VERSION == 0
553         if (EqualSubstring(line," window: pos=(",14)) {
554 	    ErrorMessage("Bad comment found in file '%s' line %d.\n"
555 	      "This comment is only found in unreleased versions of DX and\n"
556 	      "so is not being supported.  You can fix your .cfg by deleting\n"
557 	      "the offending line. \n"
558 	      "(Customers should never get this message)", file,lineno);
559 		}
560 #endif
561 	return FALSE;
562     }
563 
564 #if INCLUDE_FLAGS_COMMENT	// Not used as of version 2.1
565     int flags;
566     items = sscanf(line," window: position = (%f,%f), size = %fx%f, "
567 		   "flags = 0x%x\n",
568 		&norm_xpos,&norm_ypos,&norm_xsize,&norm_ysize, &flags);
569 
570     if (items == 5) {
571 		if ((norm_xsize < 3) && (norm_ysize < 3)) {
572 	    	display_xsize = DisplayWidth(theApplication->getDisplay(),0);
573 	    	display_ysize = DisplayHeight(theApplication->getDisplay(),0);
574 	    	xpos  = (int) (display_xsize * norm_xpos  + .5);
575 	    	ypos  = (int) (display_ysize * norm_ypos  + .5);
576 	    	xsize = (int) (display_xsize * norm_xsize + .5);
577 	    	ysize = (int) (display_ysize * norm_ysize + .5);
578 		}
579 #else
580 
581 	if (UIComponent::ParseGeometryComment(line, file, lineno, &xpos, &ypos,
582 		&xsize, &ysize, NULL)) {
583 
584 #endif
585 #if INCLUDE_FLAGS_COMMENT	// Not used as of version 2.1
586 	if (flags & 1)
587 	    this->setStartup(TRUE);
588 	else
589 	    this->setStartup(FALSE);
590 #endif
591     } else {
592 	ErrorMessage("Bad comment found in file '%s' line %d (ignoring)",
593 					file,lineno);
594 	return TRUE;
595     }
596 
597     if (theDXApplication->applyWindowPlacements()) {
598 	this->setGeometry(xpos, ypos, xsize,ysize);
599     }
600 
601     return TRUE;
602 }
603 //
604 // Reset the window to use the default settings for the state that is
605 // printed by the printComment() method.
606 //
607 void DXWindow::useDefaultCommentState()
608 {
609     this->setStartup(FALSE);
610 }
611 
612 
613 //
614 // Add a toggle button interface that toggles the startup up state of this
615 // window to the given parent widget.
616 //
617 Widget DXWindow::addStartupToggleOption(Widget parent)
618 {
619     if (!this->toggleWindowStartupCmd)
620 	this->toggleWindowStartupCmd =
621 		new NoUndoDXWindowCommand("toggleWindowStartup",
622                                   this->commandScope, TRUE,
623                                   this,
624                                   NoUndoDXWindowCommand::ToggleWindowStartup);
625 
626 
627     this->toggleWindowStartupOption =
628 	new ToggleButtonInterface
629 	    (parent, "toggleWindowStartupOption",
630 			this->toggleWindowStartupCmd, this->startup);
631 
632     return this->toggleWindowStartupOption->getRootWidget();
633 
634 }
635 //
636 // Changes whether or not this window is supposed to open up automatically
637 // on startup.
638 //
639 void DXWindow::toggleWindowStartup()
640 {
641    this->setStartup(!this->startup);
642 }
643 //
644 // Change whether or not this window is an startup window.
645 //
646 void DXWindow::setStartup(boolean setting)
647 {
648     this->startup = setting;
649 
650     if (this->toggleWindowStartupOption)
651     	this->toggleWindowStartupOption->setState(setting);
652 }
653 
654 //
655 // Change whether or not this window is an anchor window.
656 //
657 void DXWindow::setAnchor(boolean isAnchor)
658 {
659     this->anchor = isAnchor;
660 
661     if (isAnchor && this->hasMenuBar) {
662 	this->createAnchor(TRUE);
663 	XtManageChild(this->anchorButton);
664     } else if (this->anchorButton) {
665 	XtUnmanageChild(this->anchorButton);
666     }
667 
668 }
669 
670 
671 void DXWindow::getGeometryNameHierarchy(String names[], int* count, int max)
672 {
673     int cnt = *count;
674     if (cnt >= (max-1)) {
675 	this->IBMMainWindow::getGeometryNameHierarchy(names, count, max);
676 	return ;
677     }
678 
679 #ifdef DXD_NON_UNIX_DIR_SEPARATOR
680     char fsep = '\\';
681 #else
682     char fsep = '/';
683 #endif
684 
685     const char* fname = theDXApplication->network->getFileName();
686     //
687     // Remove the extension and all leading names, leaving
688     // only WindVorticity for examples
689     //
690     if ((!fname) || (!fname[0])) {
691 	this->IBMMainWindow::getGeometryNameHierarchy(names, count, max);
692 	return ;
693     }
694 
695     int len = strlen(fname);
696     int i;
697     int last_slash = -1;
698     for (i=len-1; i>=0; i--) {
699 	if ((fname[i] == fsep) || (fname[i] == '/')) {
700 	    last_slash = i;
701 	    break;
702 	}
703     }
704     last_slash++;
705     char* file;
706     file = DuplicateString(&fname[last_slash]);
707 
708     len = strlen(file);
709     for (i=len-1; i>=0; i--) if (file[i]=='.') {file[i] = '\0'; break; }
710 
711     if (file[0]) {
712 	names[cnt++] = file;
713 	*count = cnt;
714     }
715 
716     this->IBMMainWindow::getGeometryNameHierarchy(names, count, max);
717 }
718 
719 
720 void DXWindow::getGeometryAlternateNames(String* names, int* count, int max)
721 {
722     int cnt = *count;
723     if (cnt < (max-1)) {
724 	char* name = DuplicateString(this->name);
725 	names[cnt++] = name;
726 	*count = cnt;
727     }
728     this->IBMMainWindow::getGeometryAlternateNames(names, count, max);
729 }
730 
731 void DXWindow::createFileHistoryMenu (Widget parent)
732 {
733     if (!this->isAnchor()) return ;
734 
735     //
736     // if there is no history, and we don't have the ability to
737     // store history, then don't bother offering the menu.
738     //
739     char fname[256];
740     if (!theIBMApplication->getApplicationDefaultsFileName(fname)) {
741 	List recent_nets;
742 	theDXApplication->getRecentNets(recent_nets);
743 	if (recent_nets.getSize()==0) {
744 	    return ;
745 	}
746     }
747 
748     this->file_history_cascade = new CascadeMenu("fileHistory", parent);
749 
750     //
751     // put the callback on the menu parent in which we create the cascade
752     // because that allows us to grey out the cascade button before the
753     // user has a chance to click on it.
754     //
755     XtAddCallback(parent, XmNmapCallback,
756 	(XtCallbackProc)DXWindow_FileHistoryMenuMapCB, (XtPointer)this);
757 }
758 
759 void DXWindow::buildFileHistoryMenu()
760 {
761     if (!this->file_history_cascade) return ;
762 
763     ListIterator iter(this->file_history_buttons);
764     ButtonInterface* bi;
765     while ((bi=(ButtonInterface*)iter.getNext())) {
766 	bi->unmanage();
767 	delete bi;
768     }
769     this->file_history_buttons.clear();
770 
771     iter.setList(this->file_history_commands);
772     Command* cmd;
773     while ((cmd=(Command*)iter.getNext())) delete cmd;
774     this->file_history_commands.clear();
775 
776     Widget menu_parent = this->file_history_cascade->getMenuItemParent();
777 
778     List recent_nets;
779     theDXApplication->getRecentNets(recent_nets);
780     if (recent_nets.getSize()==0) {
781 	this->file_history_cascade->deactivate();
782 
783 	//
784 	// make a 1 button menu if there is no content available.
785 	// I don't think this necessary, but it won't hurt.  No one
786 	// will see it.
787 	//
788 	const char* cp = "(null)";
789 	Symbol s = theSymbolManager->registerSymbol(cp);
790 	cmd = new OpenFileCommand(s);
791 	this->file_history_commands.appendElement(cmd);
792 	bi = new ButtonInterface(menu_parent, "openFile", cmd);
793 	bi->setLabel(cp);
794 	this->file_history_buttons.appendElement(bi);
795 	cmd->deactivate();
796     } else {
797 	this->file_history_cascade->activate();
798 	//
799 	// Stick each button's label into this list, then before making
800 	// new buttons, ensure that the button's label is unique.  If
801 	// it isn't unique, then use the file's full path instead of
802 	// just the base name. This list actually serves 2 purposes.
803 	// It also records allocated memory so that we free it before
804 	// finishing.
805 	//
806 	List baseNames;
807 	iter.setList(recent_nets);
808 	Symbol s;
809 	while ((s=(Symbol)(long)iter.getNext())) {
810 	    cmd = new OpenFileCommand(s);
811 	    this->file_history_commands.appendElement(cmd);
812 	    bi = new ButtonInterface(menu_parent, "openFile", cmd);
813 	    this->file_history_buttons.appendElement(bi);
814 
815 	    const char* fullpath = theSymbolManager->getSymbolString(s);
816 	    char* cp = GetFileBaseName(fullpath,0);
817 	    baseNames.appendElement(cp);
818 	    boolean unique = TRUE;
819 	    ListIterator biter(baseNames);
820 	    const char* cmprtr;
821 	    while ((cmprtr = (const char*)biter.getNext())) {
822 		if ((cmprtr!=cp) && (EqualString(cmprtr, cp))) {
823 		    unique = FALSE;
824 		    break;
825 		}
826 	    }
827 	    if (!unique) {
828 		cp = DuplicateString(fullpath);
829 		baseNames.appendElement(cp);
830 	    }
831 	    bi->setLabel(cp);
832 	}
833 	iter.setList(baseNames);
834 	char* cp;
835 	while ((cp=(char*)iter.getNext())) delete cp;
836     }
837 }
838 
839 extern "C" void DXWindow_FileHistoryMenuMapCB(Widget , XtPointer clientdata, XtPointer )
840 {
841     DXWindow* dxw = (DXWindow*)clientdata;
842     dxw->buildFileHistoryMenu();
843 }
844 
845