/***********************************************************************/ /* 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 #include #include #include #include #include #include "defines.h" #include "XmUtility.h" #include "Interactor.h" #include "InteractorInstance.h" #include "../widgets/WorkspaceW.h" #include "../widgets/XmDX.h" #include "ControlPanel.h" #include "ControlPanelWorkSpace.h" #include "Network.h" #include "Strings.h" #include "DXApplication.h" #include "ntractor.bm" #include "ntractormask.bm" boolean Interactor::InteractorClassInitialized = FALSE; Widget Interactor::DragIcon; #if defined(aviion) || defined(alphax) // This constant exists in ToggleInteractor.C also, so if you make a change // then dupe it there. #define XMSTRING_COMPARE_IS_BROKEN #endif #define DXXOFFSET "DX_XOFFSET" #define DXYOFFSET "DX_YOFFSET" // // This is the value that specifies the default thickness of the white // border around the interactor (not the decorators). // Use the constant as a default value for HiLites. // // If you change HiLites either thru a change to OFFSET or a change to // dxui.interactorHighlight, you also affect the size of the interactor. #define OFFSET 2 static int HiLites = OFFSET + 1; String Interactor::DefaultResources[] = { "*borderWidth: 0", ".shadowType: SHADOW_OUT", ".resizePolicy: XmRESIZE_ANY", "*interactive_form.resizePolicy: XmRESIZE_ANY", ".shadowThickness: 1", ".marginWidth: 1", ".marginHeight: 1", ".interactor_label.marginWidth: 1", ".interactor_label.marginHeight: 0", ".interactor_label.marginLeft: 1", ".interactor_label.marginBottom: 1", ".interactor_label.marginTop: 1", ".interactor_label.marginRight: 1", NUL(char*) }; Dictionary* Interactor::DragTypeDictionary = new Dictionary; Interactor::Interactor(const char * name, InteractorInstance *ii) : WorkSpaceComponent(name, FALSE), DXDragSource(PrintCPBuffer) { this->interactorInstance = ii; this->label = this->customPart = NUL(Widget); this->currentLayout = ii->isVerticalLayout()?WorkSpaceComponent::VerticalUnset: WorkSpaceComponent::HorizontalUnset; } Interactor::~Interactor() { } // // Change any global attributes and then ask the derived class to // change the attributes of the interactive part. // void Interactor::handleInteractorStateChange( InteractorInstance *src_ii, boolean major_change) { int userw, userh, curw, curh, iw, ih; this->handleInteractivePartStateChange(src_ii, FALSE); #if 0 // // If the current label is different from the new one, then set it. // Otherwise don't since it may cause the Interactor 'flash' on the // control panel. // FIXME: should do the same filtering that this->setLabel() does. // XmString xms; char *curr_label; const char *new_label = this->interactorInstance->getInteractorLabel(); XtVaGetValues(this->label, XmNlabelString, &xms, NULL); // The charset must match what was used when the XmString was created. XmStringGetLtoR(xms, "bold", &curr_label); if (!EqualString(curr_label,new_label)) this->setLabel(new_label, FALSE); delete curr_label; XmStringFree(xms); #else // setLabel does nothing if oldlabel == newlabel const char *new_label = this->interactorInstance->getInteractorLabel(); this->setLabel(new_label, FALSE); #endif // // Remanage the interactor if the subclass' // handleInteractivePartStateChange() method unmanaged it. // if (!this->isManaged()) this->manage(); // // Growing and shrinking is a good thing, but don't get smaller than // the most recent user specified dimensions. // if (this->isUserSizeSet()) { this->getUserDimensions (&userw, &userh); this->GetDimensions (this->getRootWidget(), &curh, &curw); if ((curh!=userh) || (curw!=userw)) { Boolean avr = True, ahr = True; curw = MAX(curw, userw); curh = MAX(curh, userh); XtVaGetValues (this->getRootWidget(), XmNallowVerticalResizing, &avr, XmNallowHorizontalResizing, &ahr, NULL); if (avr) this->setXYSize (UIComponent::UnspecifiedDimension, curh); if (ahr) this->setXYSize (curw, UIComponent::UnspecifiedDimension); } } } void Interactor::initialize() { // // Initialize default resources (once only). // if (NOT Interactor::InteractorClassInitialized) { Interactor::InteractorClassInitialized = TRUE; Interactor::DragIcon = this->createDragIcon(ntractor_width, ntractor_height, (char *)ntractor_bits, (char *)ntractormask_bits); HiLites = OFFSET + 1; #if GOING_OVERBOARD Display *d = theApplication->getDisplay(); char *cp = XGetDefault (d, "dxui", "interactorHighlight"); // don't modify cp if ((cp) && (cp[0])) { sscanf (cp, "%d", &HiLites); } #endif if ((theDXApplication->appAllowsRWConfig())&& (theDXApplication->appAllowsSavingNetFile()) && (theDXApplication->appAllowsSavingCfgFile()) && (theDXApplication->appAllowsPanelEdit())) { this->addSupportedType (Interactor::Modules, DXINTERACTORS, TRUE); } if (theDXApplication->appAllowsPanelEdit()) this->addSupportedType (Interactor::Trash, DXTRASH, FALSE); } } void Interactor::setSelected (boolean state) { InteractorInstance *ii = this->interactorInstance; if (state) ii->setSelected(); else ii->clrSelected(); this->WorkSpaceComponent::setSelected(state); } // // Set the displayed label of the interactor // void Interactor::setLabel(const char *labelString, boolean ) { XmStringContext cxt; char *text, *tag; static char buf[1024]; XmStringDirection dir; Boolean sep; unsigned char rp; int i, linesNew, linesOld; XmString oldXmStr = NULL; if(NOT this->label) return; linesNew = linesOld = 1; XtVaGetValues (this->label, XmNlabelString, &oldXmStr, NULL); ASSERT(oldXmStr); char *filtered = WorkSpaceComponent::FilterLabelString (labelString); for (i=0; filtered[i]!='\0'; i++) if (filtered[i]=='\n') linesNew++; #if !defined(XMSTRING_COMPARE_IS_BROKEN) // For efficiency only XmString tmpXmStr = XmStringCreateLtoR (filtered, "bold"); if (XmStringCompare (tmpXmStr, oldXmStr)) { XmStringFree (tmpXmStr); if (filtered) delete filtered; XmStringFree(oldXmStr); return ; } XmStringFree (tmpXmStr); #endif if (XmStringInitContext (&cxt, oldXmStr)) { while (XmStringGetNextSegment (cxt, &text, &tag, &dir, &sep)) { if (sep) linesOld++; XtFree(text); if (tag) XtFree(tag); } XmStringFreeContext(cxt); } XmStringFree(oldXmStr); // toggle XmNresizePolicy so that the interactor doesn't snap back // to a reasonable size. If the old string is taller/shorter than the new string // then let it change. if (linesNew == linesOld) { XtVaGetValues (this->getRootWidget(), XmNresizePolicy, &rp, NULL); XtVaSetValues (this->getRootWidget(), XmNresizePolicy, XmRESIZE_GROW, NULL); } else { this->resetUserDimensions(); } if ((!filtered)||(!filtered[0])||(!strlen(filtered))) { this->setBlankLabelLayout(TRUE); } else { this->setBlankLabelLayout(FALSE); this->WorkSpaceComponent::SetLabelResource(this->label, labelString); } delete filtered; if (linesNew == linesOld) XtVaSetValues (this->getRootWidget(), XmNresizePolicy, rp, NULL); } void Interactor::openDefaultWindow() {this->interactorInstance->openSetAttrDialog();} // // Build an interactor widget tree. // void Interactor::createInteractor() { int n,x,y; Arg wargs[30]; const char *labelString; Widget parent; InteractorInstance *ii = this->interactorInstance; if (NOT Interactor::InteractorClassInitialized) this->Interactor::initialize(); this->workSpace = ii->getControlPanel()->getWorkSpace(); parent = this->workSpace->getRootWidget(); ASSERT(parent); // // Create the outermost frame. // ii->getXYPosition(&x,&y); n = 0; XtSetArg(wargs[n], XmNx, x); n++; XtSetArg(wargs[n], XmNy, y); n++; XtSetArg(wargs[n], XmNuserData, (caddr_t)ii); n++; Widget standInRoot = XmCreateForm(parent, this->name, wargs, n); this->setRootWidget(standInRoot); this->setDragWidget(standInRoot); this->setDragIcon(Interactor::DragIcon); this->developerStyle = this->getControlPanel()->isDeveloperStyle(); // // Restore width,height. By checking allow{Vertical,Horizontal}Resizing // we're ignoring dimensions which aren't user configurable. That way // we can read in .cfg files from other versions of dx without making // crazy looking interactors. We always get the space we need. // int width = 0; int height = 0; ii->getXYSize(&width,&height); Boolean avr = True, ahr = True; XtVaGetValues (this->getRootWidget(), XmNallowVerticalResizing, &avr, XmNallowHorizontalResizing, &ahr, NULL); if ((!avr)||(!height)) height = UIComponent::UnspecifiedDimension; if ((!ahr)||(!width)) width = UIComponent::UnspecifiedDimension; if ((avr) || (ahr)) this->setXYSize (width, height); n = 0; if (this->currentLayout & WorkSpaceComponent::Vertical) { XtSetArg (wargs[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (wargs[n], XmNrightOffset, HiLites); n++; XtSetArg (wargs[n], XmNbottomAttachment, XmATTACH_NONE); n++; } else { XtSetArg (wargs[n], XmNrightAttachment, XmATTACH_NONE); n++; XtSetArg (wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (wargs[n], XmNbottomOffset, HiLites); n++; } XtSetArg(wargs[n], XmNtopOffset, HiLites); n++; XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNleftOffset, HiLites); n++; XtSetArg(wargs[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(wargs[n], XmNalignment, XmALIGNMENT_CENTER); n++; XtSetArg(wargs[n], XmNuserData, this); n++; this->label = XmCreateLabel(standInRoot, "interactor_label", wargs, n); XtManageChild (this->label); this->passEvents (this->label, TRUE); const char *new_label = this->interactorInstance->getInteractorLabel(); if ((new_label) && (new_label[0])) this->WorkSpaceComponent::SetLabelResource(this->label, new_label); if (this->currentLayout & WorkSpaceComponent::Vertical) { XtSetArg (wargs[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (wargs[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (wargs[n], XmNtopWidget, this->label); n++; XtSetArg (wargs[n], XmNleftOffset, HiLites); n++; } else { XtSetArg (wargs[n], XmNtopOffset, HiLites); n++; XtSetArg (wargs[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (wargs[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (wargs[n], XmNleftWidget, this->label); n++; } XtSetArg (wargs[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (wargs[n], XmNbottomOffset, HiLites); n++; XtSetArg (wargs[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (wargs[n], XmNrightOffset, HiLites); n++; XtSetArg (wargs[n], XmNuserData, this); n++; this->customPart = XmCreateForm(standInRoot, "interactive_form", wargs, n); XtManageChild(this->customPart); this->createInteractivePart(this->customPart); this->passEvents (this->customPart, FALSE); // // Set the label after creating the interactive part in case the // sub-class wants to override setLabel(). // labelString = ii->getInteractorLabel(); ASSERT(labelString); this->setLabel(labelString, FALSE); // Set to ! because must be sure the function does something. this->currentLayout|= WorkSpaceComponent::NotSet; this->layoutInteractor(); // // Register selection callback. // XmWorkspaceAddCallback (this->getRootWidget(), XmNselectionCallback, (XtCallbackProc)Component_SelectWorkSpaceComponentCB, (caddr_t)this); // // Make sure the interactive part contains the correct initial value. // this->updateDisplayedInteractorValue(); this->setAppearance (this->developerStyle); this->installResizeHandler(); // // Perform any actions that need to be done after the parent is managed. // this->completeInteractivePart(); if ((avr)||(ahr)) this->setUserDimensions (width, height); } // // Pass button and motion events through to the parent of w. // void Interactor::passEvents(Widget w, boolean dnd) { this->WorkSpaceComponent::passEvents (w, dnd); if (dnd) this->setDragWidget(w); } // // O R I E N T A T I O N O R I E N T A T I O N O R I E N T A T I O N // O R I E N T A T I O N O R I E N T A T I O N O R I E N T A T I O N // O R I E N T A T I O N O R I E N T A T I O N O R I E N T A T I O N // O R I E N T A T I O N O R I E N T A T I O N O R I E N T A T I O N // // It's no longer necessary to do arithmetic inside the Interactor. // The original XmBulletinBoard implementation is replaced with an XmForm. // So things lay themselves out and stay laid out. The only quirky part is // is the XmLabel widget which must be forced to the proper size the first // time it appears on the screen. When changing the layout simply change // form attachments. // void Interactor::setBlankLabelLayout(boolean blank_label) { if ((this->currentLayout & WorkSpaceComponent::NotSet) == 0) { if ((blank_label)&& ((this->currentLayout & WorkSpaceComponent::BlankLabel)!=0)) return ; if ((!blank_label)&& ((this->currentLayout & WorkSpaceComponent::BlankLabel)==0)) return ; } else { if (blank_label) this->currentLayout|= WorkSpaceComponent::BlankLabel; else this->currentLayout&= ~WorkSpaceComponent::BlankLabel; return ; } InteractorInstance *ii = this->interactorInstance; if (blank_label) { ii->setVerticalLayout(TRUE); this->currentLayout|= WorkSpaceComponent::BlankLabel; this->layoutInteractor(); } else { this->currentLayout&= ~WorkSpaceComponent::BlankLabel; this->currentLayout|= WorkSpaceComponent::NotSet; this->layoutInteractor(); } } void Interactor::setVerticalLayout(boolean vertical) { if ((this->currentLayout & WorkSpaceComponent::NotSet) == 0) { if ((vertical)&&(this->currentLayout & WorkSpaceComponent::Vertical)) return ; if ((!vertical)&&(this->currentLayout & WorkSpaceComponent::Horizontal)) return ; } this->WorkSpaceComponent::setVerticalLayout(vertical); this->layoutInteractor(); } void Interactor::layoutInteractor() { this->resetUserDimensions(); if (this->currentLayout & WorkSpaceComponent::BlankLabel) this->layoutInteractorWithNoLabel(); else if (this->currentLayout & WorkSpaceComponent::Vertical) this->layoutInteractorVertically(); else if (this->currentLayout & WorkSpaceComponent::Horizontal) this->layoutInteractorHorizontally(); else ASSERT(0); this->currentLayout&= ~WorkSpaceComponent::NotSet; } // // WRT layoutInteractorHorizontally() and Vertically()... // the order in which you make calls to set form constraints is important. // If you change the order to can wind up with either error message to stderr, // or a mess inside an interactor. Be especially careful with margins. // void Interactor::layoutInteractorHorizontally() { boolean label_managed = XtIsManaged(this->label); if (!label_managed) XtManageChild (this->label); // // disconnect // XtVaSetValues (this->customPart, XmNtopAttachment, XmATTACH_NONE, NULL); XtVaSetValues (this->label, XmNrightAttachment, XmATTACH_NONE, NULL); // // reconnect // XtVaSetValues (this->customPart, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, this->label, XmNtopOffset, HiLites, XmNleftOffset, 0, NULL); XtVaSetValues (this->label, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, HiLites, XmNleftOffset, HiLites, XmNtopOffset, HiLites, NULL); } void Interactor::layoutInteractorVertically() { boolean label_managed = XtIsManaged(this->label); if (!label_managed) { XtManageChild (this->label); } // // disconnect // XtVaSetValues (this->label, XmNbottomAttachment, XmATTACH_NONE, XmNbottomOffset, 0, NULL); XtVaSetValues (this->customPart, XmNleftAttachment, XmATTACH_NONE, XmNleftOffset, HiLites, XmNtopAttachment, XmATTACH_NONE, NULL); // // reconnect // XtVaSetValues (this->label, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, HiLites, NULL); XtVaSetValues (this->customPart, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, this->label, XmNtopOffset, 0, XmNleftAttachment, XmATTACH_FORM, NULL); } void Interactor::layoutInteractorWithNoLabel() { // // disconnect // XtVaSetValues (this->customPart, XmNtopAttachment, XmATTACH_NONE, NULL); if (XtIsManaged(this->label)) { XtUnmanageChild (this->label); XtVaSetValues (this->getRootWidget(), XmNheight, 0, NULL); } // // reconnect // XtVaSetValues (this->customPart, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, HiLites, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, HiLites, NULL); } // D R A G - N - D R O P --- R E L A T E D S T U F F --- D R A G - N - D R O P // D R A G - N - D R O P --- R E L A T E D S T U F F --- D R A G - N - D R O P // D R A G - N - D R O P --- R E L A T E D S T U F F --- D R A G - N - D R O P // D R A G - N - D R O P --- R E L A T E D S T U F F --- D R A G - N - D R O P // // ...requires only that it be selected // int Interactor::decideToDrag(XEvent *xev) { InteractorInstance *ii = this->interactorInstance; if (!this->developerStyle) return DragSource::Abort; if (!ii->selected) return DragSource::Inactive; Display *d = XtDisplay(this->getRootWidget()); Atom xoff = XInternAtom (d, DXXOFFSET, False); Atom yoff = XInternAtom (d, DXYOFFSET, False); Screen *screen = XtScreen(this->getRootWidget()); Window root = RootWindowOfScreen(screen); int x = 0; int y = 0; ii->getXYPosition (&x, &y); int minx = x; int miny = y; int junk; this->workSpace->getSelectedBoundingBox (&minx, &miny, &junk, &junk); int xoffset = x - minx; int yoffset = y - miny; if (xev->type == ButtonPress) { xoffset+= xev->xbutton.x; yoffset+= xev->xbutton.y; } XChangeProperty (d, root, xoff, XA_INTEGER, 32, PropModeReplace, (unsigned char*)&xoffset, 1); XChangeProperty (d, root, yoff, XA_INTEGER, 32, PropModeReplace, (unsigned char*)&yoffset, 1); return DragSource::Proceed; } // // The header (which will not end up being part of the files) says // "hostname:pid, net length = %d, cfg length = %d\n" // boolean Interactor::createNetFiles(Network *netw, FILE *netf, char *cfgfile) { netw->setCPSelectionOwner(this->getControlPanel()); return (boolean)this->DXDragSource::createNetFiles (netw, netf, cfgfile); } // // if the operation was a move and not a copy, then delete // the original interactors. // void Interactor::dropFinish (long operation, int tag, unsigned char status) { // // Delete the atoms set up when starting the drag // Display *d = XtDisplay(this->getRootWidget()); Atom xoff_atom = XInternAtom (d, DXXOFFSET, True); Atom yoff_atom = XInternAtom (d, DXYOFFSET, True); Screen *screen = XtScreen(this->getRootWidget()); Window root = RootWindowOfScreen(screen); if (xoff_atom != None) XDeleteProperty (d, root, xoff_atom); if (yoff_atom != None) XDeleteProperty (d, root, yoff_atom); // If the operation was a copy and the type was Trash, // then treat it would be better to treat it like a move // so that the user isn't required to use the Shift key. if (status) { if ((operation == XmDROP_MOVE) || (tag == Interactor::Trash)) this->getControlPanel()->deleteSelectedInteractors(); } } boolean Interactor::decodeDragType (int tag, char * a, XtPointer *value, unsigned long *length, long operation) { boolean retVal; switch (tag) { case Interactor::Modules: // this-convert() comes from DXDragSource and can't be overridden. retVal = this->convert (this->getNetwork(), a, value, length, operation); break; // Don't do anything... Just say OK. this->dropFinish does the delete case Interactor::Trash: retVal = TRUE; // dummy pointer *value = (XtPointer)malloc(4); *length = 4; break; default: retVal = FALSE; break; } return retVal; } // U T I L S U T I L S U T I L S U T I L S U T I L S U T I L S // U T I L S U T I L S U T I L S U T I L S U T I L S U T I L S // U T I L S U T I L S U T I L S U T I L S U T I L S U T I L S // U T I L S U T I L S U T I L S U T I L S U T I L S U T I L S // U T I L S U T I L S U T I L S U T I L S U T I L S U T I L S // U T I L S U T I L S U T I L S U T I L S U T I L S U T I L S boolean Interactor::isA(Symbol classname) { Symbol s = theSymbolManager->registerSymbol(ClassInteractor); if (s == classname) return TRUE; return this->WorkSpaceComponent::isA(classname); } // // Indicate that the interactor is selected. // void Interactor::indicateSelect(boolean selected) { if (this->getRootWidget()) { XtVaSetValues(this->getRootWidget(), XmNselected, (selected ? True : False), NULL); } } const char *Interactor::getComponentHelpTopic() { static char topic[100]; sprintf(topic, "%sInteractor", this->getNode()->getNameString()); return topic; } void *GetUserData(Widget widget) { caddr_t data; ASSERT(widget); XtVaGetValues(widget, XmNuserData, &data, NULL); return (data); } Network *Interactor::getNetwork() { ASSERT(this->interactorInstance); return this->interactorInstance->getNetwork(); } Node *Interactor::getNode() { ASSERT(this->interactorInstance); return this->interactorInstance->getNode(); } ControlPanel *Interactor::getControlPanel() { ASSERT(this->interactorInstance); return this->interactorInstance->getControlPanel(); }