/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include #include #ifndef DXD_DO_NOT_REQ_UNISTD_H #include #endif #include #include "dxl.h" #include "XmUtility.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef DXD_WIN #include #endif #include "../widgets/Stepper.h" #include "ListIterator.h" #include "Dictionary.h" #include "FileContents.h" extern Dictionary* theTypeChoiceDictionary; extern void BuildTheTypeChoiceDictionary(); #include "CommandTextPopup.h" #include "GARChooserWindow.h" #include "GARApplication.h" #include "GARMainWindow.h" #include "ToggleButtonInterface.h" #include "ButtonInterface.h" #include "NoUndoChooserCommand.h" #include "QuitCommand.h" #include "WarningDialogManager.h" #include "TypeChoice.h" #include "ConfirmedQCommand.h" #include "DataFileDialog.h" boolean GARChooserWindow::ClassInitialized = FALSE; String GARChooserWindow::DefaultResources[] = { ".title: Data Prompter", ".iconName: DX", ".geometry: +0+105", "*notebook.XmToggleButton.indicatorType: XmONE_OF_MANY", "*notebook.XmToggleButton.indicatorSize: 18", "*notebook.XmToggleButton.spacing: 15", "*notebook.XmToggleButton.leftOffset: 10", "*notebook.XmToggleButton.rightOffset: 2", "*notebook.XmToggleButton.alignment: XmALIGNMENT_BEGINNING", "*notebook.XmToggleButton.shadowThickness: 0", "*fnHeader.labelString: Data file name", "*fnHeader.foreground: #26a", "*helpHeader.labelString: Hints", "*helpHeader.foreground: #26a", "*popupButton.labelString: ...", "*fileMenu.labelString: File", "*optionsMenu.labelString: Options", "*showOption.labelString: Open Message Window...", "*openPrompterOption.labelString: Open General Array Importer...", "*quitOption.labelString: Quit", "*quitOption.mnemonic: Q", "*quitOption.accelerator: CtrlQ", "*quitOption.acceleratorText: Ctrl+Q", "*dataFileOption.labelString: Select Data File...", "*dataFileOption.mnemonic: F", "*notebookHeader.labelString: Select the format of your data:", "*notebookHeader.foreground: #26a", //"*accelerators: #augment\n" //"Return: BulletinBoardReturn()", NUL(char*) }; GARChooserWindow::GARChooserWindow() : IBMMainWindow ("SMDName", TRUE) { this->quitCmd = new ConfirmedQCommand("quit", this->commandScope, TRUE); this->showMessagesCmd = new NoUndoChooserCommand ("showMessages",this->commandScope, TRUE, this, NoUndoChooserCommand::ShowMessages); this->openPrompterCmd = new NoUndoChooserCommand ("openPrompter",this->commandScope, TRUE, this, NoUndoChooserCommand::OpenPrompter); this->selectDataCmd = new NoUndoChooserCommand ("selectData",this->commandScope, TRUE, this, NoUndoChooserCommand::SelectDataFile); this->nullCmd = new NoUndoChooserCommand ("donothing", this->commandScope, TRUE, this, NoUndoChooserCommand::NoOp); // // Install the default resources for THIS class (not the derived classes) // if (NOT GARChooserWindow::ClassInitialized) { ASSERT(theGARApplication); GARChooserWindow::ClassInitialized = TRUE; this->installDefaultResources(theApplication->getRootWidget()); BuildTheTypeChoiceDictionary(); } this->text_file = new CommandTextPopup(this); this->file_search_dir = NUL(char*); this->connection = NUL(DXLConnection*); this->mismatch_complaint = FALSE; this->dfdialog = NUL(DataFileDialog*); TypeChoiceAllocator tca; int size = theTypeChoiceDictionary->getSize(); int i; this->typeChoices = new List; for (i=1; i<=size; i++) { tca = (TypeChoiceAllocator)theTypeChoiceDictionary->getDefinition(i); Symbol sym = theTypeChoiceDictionary->getSymbol(i); TypeChoice *tc = tca(this, sym); this->typeChoices->appendElement((const void*)tc); } this->choice = NUL(TypeChoice*); this->unset_sync_wpid = NUL(XtWorkProcId); this->openPrompterCmd->deactivate( "You must create a .general file for gridded or scatterd data, first."); } // // Install the default resources for this class. // void GARChooserWindow::installDefaultResources(Widget baseWidget) { this->setDefaultResources(baseWidget, GARChooserWindow::DefaultResources); this->IBMMainWindow::installDefaultResources(baseWidget); } GARChooserWindow::~GARChooserWindow() { delete this->nullCmd; delete this->text_file; ListIterator it(*this->typeChoices); TypeChoice *tc; while (tc = (TypeChoice*)it.getNext()) { delete tc; } delete this->typeChoices; if (this->dfdialog) delete this->dfdialog; if (this->file_search_dir) delete this->file_search_dir; if (this->unset_sync_wpid) XtRemoveWorkProc (this->unset_sync_wpid); if (this->connection) DXLExitDX(this->connection); } Widget GARChooserWindow::createWorkArea (Widget parent) { // // Create the outer frame and form. // Widget frame = XtVaCreateManagedWidget ("chooFrame", xmFrameWidgetClass, parent, XmNshadowType, XmSHADOW_OUT, NULL); Widget form = XtVaCreateManagedWidget ("chooForm", xmFormWidgetClass, frame, NULL); // // Status area... includes file name, bubble help, and labeling // this->text_file->createTextPopup (form); Widget tf_root = this->text_file->getRootWidget(); XtVaSetValues (tf_root, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 10, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 10, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 20, XmNbottomAttachment, XmATTACH_OPPOSITE_FORM, XmNbottomOffset, -60, NULL); Widget label = XtVaCreateManagedWidget ("fnHeader", xmLabelWidgetClass, form, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 20, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, tf_root, XmNbottomOffset, 0, NULL); Widget help_frame = XtVaCreateManagedWidget ("helpFrame", xmFrameWidgetClass, form, XmNshadowType, XmSHADOW_IN, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_OPPOSITE_FORM, XmNleftOffset, 4, XmNrightOffset, 4, XmNbottomOffset, 4, XmNtopOffset, -23, NULL); XmString xmstr = XmStringCreateLtoR (" ", "small_normal"); Widget help_viewer = XtVaCreateManagedWidget ("helpViewer", xmLabelWidgetClass, help_frame, XmNlabelString, xmstr, XmNrecomputeSize, False, NULL); XmStringFree(xmstr); XtVaCreateManagedWidget ("helpHeader", xmLabelWidgetClass, form, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 20, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, help_frame, XmNbottomOffset, 0, NULL); XtVaCreateManagedWidget ("sep", xmSeparatorWidgetClass, form, XmNrightAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNrightOffset, 0, XmNleftOffset, 0, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, help_frame, XmNbottomOffset, 4, NULL); Widget notelab = XtVaCreateManagedWidget ("notebookHeader", xmLabelWidgetClass, form, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 20, NULL); Widget sep = XtVaCreateManagedWidget ("notesep", xmSeparatorWidgetClass, form, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 70, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); XtVaSetValues (notelab, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, sep, XmNbottomOffset, -7, XmNtopAttachment, XmATTACH_NONE, NULL); Widget notebook = XtVaCreateManagedWidget ("notebook", xmFormWidgetClass, form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, sep, XmNtopOffset, 2, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_WIDGET, XmNleftOffset, 2, XmNrightOffset, 2, XmNbottomWidget, help_frame, NULL); ListIterator it(*this->typeChoices); TypeChoice *tc; while (tc = (TypeChoice*)it.getNext()) { tc->createChoice (notebook); } theApplication->setHelpViewer(help_viewer); theApplication->enableBubbleHelp(); this->showForms(); theGARApplication->addHelpCallbacks(parent, this->name); this->setCommandActivation(); return frame; } void GARChooserWindow::manage() { this->IBMMainWindow::manage(); XmUpdateDisplay(this->getRootWidget()); // // If a data file name was specified on the command line, then insert the // name // const char *df = theGARApplication->getResourcesData(); if ((df) && (df[0])) { this->text_file->setText(df); this->fileNameCB(); } } void GARChooserWindow::createMenus (Widget menu_bar) { this->createFileMenu (menu_bar); this->createOptionsMenu (menu_bar); this->createBaseHelpMenu(menu_bar,TRUE,TRUE); } void GARChooserWindow::createFileMenu (Widget menu_bar) { // // Create "File" menu and options. // Widget pulldown = XmCreatePulldownMenu(menu_bar, "fileMenuPulldown", NUL(ArgList), 0); XtVaCreateManagedWidget ("fileMenu", xmCascadeButtonWidgetClass, menu_bar, XmNsubMenuId, pulldown, NULL); new ButtonInterface(pulldown,"dataFileOption", this->selectDataCmd); XtVaCreateManagedWidget ("sep", xmSeparatorWidgetClass, pulldown, NULL); new ButtonInterface(pulldown,"quitOption", this->quitCmd); } void GARChooserWindow::createOptionsMenu (Widget menu_bar) { // // Create "Options" menu and options. // Widget pulldown = XmCreatePulldownMenu(menu_bar, "optionsMenuPulldown", NUL(ArgList), 0); XtVaCreateManagedWidget ("optionsMenu", xmCascadeButtonWidgetClass, menu_bar, XmNsubMenuId, pulldown, NULL); new ButtonInterface(pulldown,"showOption",this->showMessagesCmd, "Messages from the DX executive"); new ButtonInterface(pulldown,"openPrompterOption",this->openPrompterCmd, "The description in your General Array Format file."); } void GARChooserWindow::setChoice (TypeChoice *new_choice) { // // We have the old type saved. Pull out the button. // if (this->choice != new_choice) { if (this->choice) this->choice->setSelected(FALSE); this->choice = new_choice; if (this->choice) this->choice->setSelected(TRUE); } if ((this->choice) && (this->choice->selected() == FALSE)) this->choice = NUL(TypeChoice*); this->setCommandActivation(); this->showForms(); // // If the new choice dislikes having the prompter open and the // prompter is already open, then get rid of it. // if ((this->choice) && (this->choice->usesPrompter()==FALSE)) { IBMMainWindow* gmw = theGARApplication->getMainWindow(FALSE); if ((gmw) && (gmw->isManaged())) theGARApplication->destroyMainWindow(); this->openPrompterCmd->deactivate( "You must create a .general file for gridded or scatterd data, first."); } if ((this->choice==NUL(TypeChoice*)) || (this->choice->selected() == FALSE)) { return ; } // // Now look for a mismatch between the file name extension and the // data type selected. ...but only complain 1 time per file. // const char *fext = this->choice->getFileExtension(); if ((!this->mismatch_complaint) && (fext) && (fext[0])) { boolean mismatch = FALSE; char *file_name = this->getFileReady(); char *ext = NUL(char*); if (file_name) ext = this->findExtension(file_name); char *cp = DuplicateString(fext); int i,len; len = strlen(cp); for (i=0; ichoice->canHandle(ext) == FALSE)) { if (cp) mismatch = (!EqualString(ext, cp)); } else if ((cp) && (cp[0])) mismatch = TRUE; if (cp) delete cp; if (ext) delete ext; if (file_name) { if (mismatch) { char msg[256]; sprintf (msg, "File extension / Data type mismatch."); WarningMessage (msg); this->mismatch_complaint = TRUE; } delete file_name; } } return ; } void GARChooserWindow::showForms() { // // minHeight should be initialized to whatever space is required // to show just the locally created widgets. It's assumed that the outliner // expands only vertically. That means that minWidth will be set to the // whatever is requested by an expanded TypeChoice, whereas minHeight will // be a cumulative sum. minHeight will automatically be larger if there // is a new subclass off TypeChoice. // Dimension minHeight = 150; Dimension minWidth = 410; ListIterator it(*this->typeChoices); TypeChoice *tc; boolean first = TRUE; Widget prev_widget = NUL(Widget); TypeChoice* unmanage_guy = NUL(TypeChoice*); TypeChoice* manage_guy = NUL(TypeChoice*); while (tc = (TypeChoice*)it.getNext()) { if (first) { XtVaSetValues (tc->getOptionWidget(), XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 4, NULL); first = FALSE; } else { ASSERT(prev_widget); XtVaSetValues (tc->getOptionWidget(), XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, prev_widget, XmNtopOffset, 4, NULL); if (unmanage_guy) unmanage_guy->unmanage(); } unmanage_guy = NUL(TypeChoice*); if ((!tc->selected()) && (tc->isManaged())) { unmanage_guy = tc; } else if ((tc->selected()) && (!tc->isManaged())) { manage_guy = tc; } #if VERTICAL_LAYOUT if (!tc->selected()) { prev_widget = tc->getOptionWidget(); } else { prev_widget = tc->getRootWidget(); minWidth = tc->getMinWidth(); } #else prev_widget = tc->getOptionWidget(); XtVaSetValues (tc->getRootWidget(), XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_WIDGET, XmNrightAttachment, XmATTACH_FORM, XmNleftWidget, tc->getOptionWidget(), XmNtopOffset, 4, XmNleftOffset, 4, XmNrightOffset, 4, NULL); if (tc->selected()) minWidth = tc->getMinWidth(); #endif minHeight+= tc->getHeightContribution(); } if (unmanage_guy) unmanage_guy->unmanage(); Dimension oldHeight, oldWidth; XtVaGetValues (this->getRootWidget(), XmNheight, &oldHeight, XmNwidth, &oldWidth, NULL); int n = 0; Arg args[9]; XtSetArg (args[n], XmNminHeight, minHeight); n++; XtSetArg (args[n], XmNminWidth, minWidth); n++; // // This constant controls if or not the window will automatically resize // itself every time you pick a new data type choice, or if it will only // resize itself (if growing)||(if all choices unselected) // #define SHRINK_WRAP 1 boolean shrink_behavior = (oldHeight < minHeight) || SHRINK_WRAP; if ((shrink_behavior) || (!this->choice)) { XtSetArg (args[n], XmNheight, minHeight); n++; } shrink_behavior = (oldWidth < minWidth) || SHRINK_WRAP; if ((shrink_behavior) || (!this->choice)) { XtSetArg (args[n], XmNwidth, minWidth); n++; } XtSetValues (this->getRootWidget(), args, n); if (manage_guy) manage_guy->manage(); } boolean GARChooserWindow::showMessages() { this->choice->showList(); return TRUE; } boolean GARChooserWindow::openExistingPrompter() { GARMainWindow* gmw = theGARApplication->getMainWindow(FALSE); if (gmw == NUL(GARMainWindow*)) { this->openPrompterCmd->deactivate( "You must create a .general file for gridded or scatterd data, first."); return FALSE; } gmw->manage(); return TRUE; } void GARChooserWindow::setCommandActivation() { if (this->choice) this->choice->setCommandActivation(); if (!this->choice) this->showMessagesCmd->deactivate("You must select an import type, first."); else this->showMessagesCmd->activate(); GARMainWindow* gmw = theGARApplication->getMainWindow(FALSE); if (gmw == NUL(GARMainWindow*)) this->openPrompterCmd->deactivate( "You must create a .general file for gridded or scatterd data, first."); else this->openPrompterCmd->activate(); } void GARChooserWindow::setDataFile (const char *file) { this->text_file->setText(file, TRUE); } void GARChooserWindow::fileNameCB() { char *full_path = this->getFileReady(); if ((full_path) && (full_path[0])) { char *ext = this->findExtension(full_path); // // If the file was found using a search path, then install the full pathname // const char *short_path = this->text_file->getText(); if (!EqualString(full_path, short_path)) { this->text_file->setText(full_path); } // // Find out if the currently selected data type wants to retain control // over the new data file name. This happens when the type is GridChoice // and the data file name inside the .general file has an extension which // matches one of the other type choices. image.general // if ((this->choice) && (this->choice->retainsControl(full_path))) { } else { ListIterator it(*this->typeChoices); TypeChoice *tc; if (ext) { while (tc = (TypeChoice*)it.getNext()) { const char *fext = tc->getFileExtension(); if (tc->canHandle(ext)) { if (tc->selected() == FALSE) this->setChoice(tc); break; } else if (fext) { char *cp = DuplicateString(fext); int i, len; len = strlen(cp); for (i=0; iselected() == FALSE) this->setChoice(tc); if (cp) delete cp; break; } if (cp) delete cp; } } delete ext; } it.setList(*this->typeChoices); while (tc = (TypeChoice*)it.getNext()) tc->hideList(); this->mismatch_complaint = FALSE; } } if (full_path) delete full_path; this->setCommandActivation(); } char *GARChooserWindow::getFileReady() { const char *fext = NUL(char*); if (this->choice) fext = this->choice->getFileExtension(); // // Does the text widget at the top contain a valid data file name? // char *cp = NULL; char *file_name = NUL(char*); if (this->text_file->getRootWidget()) cp = this->text_file->getText(); if ((cp) && (cp[0])) { file_name = theGARApplication->resolvePathName(cp, fext); } if (cp) XtFree(cp); return file_name; } char *GARChooserWindow::findExtension(const char *cp) { if ((!cp) || (!cp[0])) return NUL(char*); // // Count backwards from the end of the file name looking for the // file extension. It will automatically determine a data type. // int len = strlen(cp); int i; for (i=len-1; i>=0; i--) { if (cp[i] == '.') break; } if ((i<0) || (cp[i] != '.')) return NUL(char*); char *ext = new char[1+len-i]; strcpy (ext, &cp[i]); int j,t = strlen(ext); for (j=0; jtext_file) return NUL(char*); char *cp = this->text_file->getText(); if (!cp) return NUL(char*); if (!cp[0]) { XtFree(cp); return NUL(char*); } strcpy (tbuf, cp); XtFree(cp); return tbuf; } void GARChooserWindow::closeWindow() { this->quitCmd->execute(); } boolean GARChooserWindow::postDataFileSelector() { if (!this->dfdialog) this->dfdialog = new DataFileDialog (this->getRootWidget(), this); this->dfdialog->post(); if (this->file_search_dir) { char* cp = DuplicateString(this->file_search_dir); this->setFileSearchDir(cp); if (cp) delete cp; } return TRUE; } void GARChooserWindow::setFileSearchDir(const char *value) { if (this->file_search_dir) delete this->file_search_dir; this->file_search_dir = NUL(char*); this->file_search_dir = DuplicateString(value); if (!this->dfdialog) return; if (!this->file_search_dir) return; Widget fsb = this->dfdialog->getFileSelectionBox(); if (!fsb) return ; const char *ext = (this->choice?this->choice->getFileExtension():NUL(char*)); int extlen = 0; if (ext) extlen = strlen(ext); char *dirspec = new char[strlen(this->file_search_dir) + extlen + 3]; if (extlen) sprintf (dirspec, "%s/*%s", this->file_search_dir, ext); else sprintf (dirspec, "%s/*", this->file_search_dir); XmString xmstr = XmStringCreateLtoR (dirspec, "bold"); #ifdef DXD_WIN Widget filt = XmFileSelectionBoxGetChild(fsb, XmDIALOG_FILTER_TEXT); XmTextSetString(filt, dirspec); #endif XtVaSetValues (fsb, XmNdirMask, xmstr, NULL); XmStringFree(xmstr); delete dirspec; } // // Make a copies of the net and cfg files replacing things listed in args. // This is used as an alternative to DXLinkInputs. Nancy and Donna wanted this // in order to improve the appearance of the ezstart nets. A potential problem // is that you have to munge disk files and reload a program in order to change // a value where you used to have to simply send down new values using DXLink. // void GARChooserWindow::loadAndSet (DXLConnection* conn, const char* net_file, char* argv[], int argc) { int j; ASSERT ((net_file) && (net_file[0])); ASSERT (conn); // // The number of args must be an even number. For each pattern there is a // replacement. // ASSERT ((argc&0x1) == 0); char* cfg_file = DuplicateString(net_file); int cfg_spot = strlen(net_file) - 4; ASSERT (!strcmp(&cfg_file[cfg_spot], ".net")); strcpy (&cfg_file[cfg_spot], ".cfg"); FileContents net_to_load(net_file); FileContents cfg_to_load(cfg_file); delete cfg_file; if (net_to_load.initialize()) { const char* base_name = net_to_load.sansExtension(); cfg_to_load.initialize(base_name); for (j=0; jgetApplicationContext(); if (this->unset_sync_wpid == NUL(XtWorkProcId)) XtAppAddWorkProc (apcxt, (XtWorkProc) GARChooserWindow_SyncWP, (XtPointer)this); } else { net_to_load.close(); cfg_to_load.close(); WarningMessage ( "Unable to write to temporary directory.\nTry setting TMPDIR and restarting." ); } } extern "C" Boolean GARChooserWindow_SyncWP (XtPointer clientData) { GARChooserWindow* gcw = (GARChooserWindow*)clientData; ASSERT(gcw); // // connection can be 0 here if the user picked File/Quit while the // visual program was executing. // if (gcw->connection) DXLSetSynchronization(gcw->connection, 0); gcw->unset_sync_wpid = NUL(XtWorkProcId); return True; }