//========================================================================== // MODULEINSPECTOR.CC - part of // // OMNeT++/OMNEST // Discrete System Simulation in C++ // //========================================================================== /*--------------------------------------------------------------* Copyright (C) 1992-2017 Andras Varga Copyright (C) 2006-2017 OpenSim Ltd. This file is distributed WITHOUT ANY WARRANTY. See the file `license' for details on this and other legal matters. *--------------------------------------------------------------*/ #include #include #include #include #include "common/stringtokenizer.h" #include "common/stringutil.h" #include "layout/graphlayouter.h" #include "layout/forcedirectedgraphlayouter.h" #include "layout/basicspringembedderlayout.h" #include "omnetpp/cdisplaystring.h" #include "omnetpp/cqueue.h" #include "omnetpp/cmessage.h" #include "omnetpp/cgate.h" #include "omnetpp/cchannel.h" #include "omnetpp/csimplemodule.h" #include "omnetpp/cfutureeventset.h" #include "moduleinspector.h" #include "figurerenderers.h" #include "tkenv.h" #include "tklib.h" #include "tkutil.h" #include "inspectorfactory.h" #include "arrow.h" #include "layouterenv.h" #include "canvasrenderer.h" using namespace omnetpp::common; using namespace omnetpp::layout; namespace omnetpp { namespace tkenv { #define UNKNOWNICON_WIDTH 32 #define UNKNOWNICON_HEIGHT 32 void _dummy_for_moduleinspector() {} class ModuleInspectorFactory : public InspectorFactory { public: ModuleInspectorFactory(const char *name) : InspectorFactory(name) {} bool supportsObject(cObject *obj) override { return dynamic_cast(obj) != nullptr; } int getInspectorType() override { return INSP_GRAPHICAL; } double getQualityAsDefault(cObject *object) override { cModule *mod = dynamic_cast(object); return mod && mod->hasSubmodules() ? 3.0 : 0.9; } Inspector *createInspector() override { return new ModuleInspector(this); } }; Register_InspectorFactory(ModuleInspectorFactory); ModuleInspector::ModuleInspector(InspectorFactory *f) : Inspector(f) { needs_redraw = false; notDrawn = false; layoutSeed = 1; canvasRenderer = new CanvasRenderer(); } ModuleInspector::~ModuleInspector() { delete canvasRenderer; } void ModuleInspector::doSetObject(cObject *obj) { if (obj == object) return; Inspector::doSetObject(obj); canvasRenderer->setCanvas(getCanvas()); CHK(Tcl_VarEval(interp, canvas, " delete all", TCL_NULL)); if (object) { layoutSeed = 0; // we'll read the "bgl" display string tag from Tcl CHK(Tcl_VarEval(interp, "ModuleInspector:onSetObject ", windowName, TCL_NULL)); } } void ModuleInspector::createWindow(const char *window, const char *geometry) { Inspector::createWindow(window, geometry); strcpy(canvas, windowName); strcat(canvas, ".c"); CHK(Tcl_VarEval(interp, "createModuleInspector ", windowName, " ", TclQuotedString(geometry).get(), TCL_NULL)); canvasRenderer->setTkCanvas(interp, canvas); } void ModuleInspector::useWindow(const char *window) { Inspector::useWindow(window); strcpy(canvas, windowName); strcat(canvas, ".c"); canvasRenderer->setTkCanvas(interp, canvas); } cCanvas *ModuleInspector::getCanvas() { cModule *mod = static_cast(object); cCanvas *canvas = mod ? mod->getCanvasIfExists() : nullptr; return canvas; } void ModuleInspector::refresh() { Inspector::refresh(); if (!object) { CHK(Tcl_VarEval(interp, canvas, " delete all", TCL_NULL)); return; } if (notDrawn) return; cCanvas *canvas = getCanvas(); if (canvas != nullptr && !canvasRenderer->hasCanvas()) // canvas was recently created canvasRenderer->setCanvas(canvas); updateBackgroundColor(); // redraw modules only if really needed if (needs_redraw) { needs_redraw = false; redraw(); } else { if (canvas) canvas->getRootFigure()->callRefreshDisplay(); refreshFigures(); redrawNextEventMarker(); redrawMessages(); refreshSubmodules(); adjustSubmodulesZOrder(); } } void ModuleInspector::relayoutAndRedrawAll() { ASSERT(object != nullptr); cModule *mod = (cModule *)object; int submoduleCount = 0; int estimatedGateCount = mod->gateCount(); for (cModule::SubmoduleIterator it(mod); !it.end(); ++it) { submoduleCount++; // note: estimatedGateCount will count unconnected gates in the gate array as well estimatedGateCount += (*it)->gateCount(); } notDrawn = false; if (submoduleCount > 1000 || estimatedGateCount > 4000) { char problem[200]; if (submoduleCount > 1000) sprintf(problem, "contains more than 1000 submodules (exactly %d)", submoduleCount); else sprintf(problem, "may contain a lot of connections (modules have a large number of gates)"); CHK(Tcl_VarEval(interp, "tk_messageBox -parent ", windowName, " -type yesno -title Warning -icon question " "-message {Module '", object->getFullName(), "' ", problem, ", it may take a long time to display the graphics. " "Do you want to proceed with drawing?}", TCL_NULL)); bool answer = (Tcl_GetStringResult(interp)[0] == 'y'); if (answer == false) { notDrawn = true; CHK(Tcl_VarEval(interp, canvas, " delete all", TCL_NULL)); // this must be done, still return; } } recalculateLayout(); redrawModules(); redrawFigures(); redrawNextEventMarker(); redrawMessages(); refreshSubmodules(); adjustSubmodulesZOrder(); // let the model re-read new positions via cEnvir::getSubmodulePosition(), and update the graphics int simState = getTkenv()->getSimulationState(); if (simState == Tkenv::SIM_NEW || simState == Tkenv::SIM_READY || simState == Tkenv::SIM_RUNNING || simState == Tkenv::SIM_TERMINATED) { getTkenv()->callRefreshDisplay(); getTkenv()->refreshInspectors(); } } void ModuleInspector::redraw() { if (object == nullptr) { CHK(Tcl_VarEval(interp, canvas, " delete all", TCL_NULL)); return; } if (cCanvas *canvas = getCanvas()) canvas->getRootFigure()->callRefreshDisplay(); updateBackgroundColor(); refreshLayout(); redrawModules(); redrawFigures(); redrawNextEventMarker(); redrawMessages(); refreshSubmodules(); adjustSubmodulesZOrder(); } void ModuleInspector::adjustSubmodulesZOrder() { cCanvas *canvas = getCanvas(); if (canvas) { cFigure *submodulesLayer = canvas->getSubmodulesLayer(); if (submodulesLayer) { char tag[32]; sprintf(tag, "f%d", submodulesLayer->getId()); CHK(Tcl_VarEval(interp, this->canvas, " lower submodext ", tag, TCL_NULL)); CHK(Tcl_VarEval(interp, this->canvas, " raise submodext ", tag, TCL_NULL)); } } } void ModuleInspector::clearObjectChangeFlags() { cCanvas *canvas = getCanvas(); if (canvas) canvas->getRootFigure()->clearChangeFlags(); } void ModuleInspector::getSubmoduleCoords(cModule *submod, bool& explicitcoords, bool& obeysLayout, double& x, double& y, double& sx, double& sy) { const cDisplayString blank; const cDisplayString& ds = submod->hasDisplayString() && submod->parametersFinalized() ? submod->getDisplayString() : blank; // get size -- we'll need to return that too, and may be needed for matrix, ring etc. layout double boxsx = 0, boxsy = 0; int iconsx = 0, iconsy = 0; if (ds.containsTag("b")) { boxsx = resolveDoubleDispStrArg(ds.getTagArg("b", 0), submod, 40); boxsy = resolveDoubleDispStrArg(ds.getTagArg("b", 1), submod, 24); } if (ds.containsTag("i") || !ds.containsTag("b")) { const char *imgName = ds.getTagArg("i", 0); const char *imgSize = ds.getTagArg("is", 0); if (!imgName || !*imgName) { iconsx = UNKNOWNICON_WIDTH; iconsy = UNKNOWNICON_HEIGHT; } else { CHK(Tcl_VarEval(interp, "lookupImage ", imgName, " ", imgSize, TCL_NULL)); Tk_Image img = Tk_GetImage(interp, Tk_MainWindow(interp), Tcl_GetStringResult(interp), nullptr, nullptr); if (!img) { iconsx = UNKNOWNICON_WIDTH; iconsy = UNKNOWNICON_HEIGHT; } else { Tk_SizeOfImage(img, &iconsx, &iconsy); Tk_FreeImage(img); } } } sx = (boxsx > iconsx) ? boxsx : iconsx; sy = (boxsy > iconsy) ? boxsy : iconsy; // first, see if there's an explicit position ("p=" tag) given x = resolveDoubleDispStrArg(ds.getTagArg("p", 0), submod, -1); y = resolveDoubleDispStrArg(ds.getTagArg("p", 1), submod, -1); explicitcoords = x != -1 && y != -1; // set missing coordinates to zero if (x == -1) x = 0; if (y == -1) y = 0; const char *layout = ds.getTagArg("p", 2); // matrix, row, column, ring, exact etc. obeysLayout = (layout && *layout); // modify x,y using predefined layouts if (!layout || !*layout) { // we're happy } else if (!strcmp(layout, "e") || !strcmp(layout, "x") || !strcmp(layout, "exact")) { double dx = resolveDoubleDispStrArg(ds.getTagArg("p", 3), submod, 0); double dy = resolveDoubleDispStrArg(ds.getTagArg("p", 4), submod, 0); x += dx; y += dy; } else if (!strcmp(layout, "r") || !strcmp(layout, "row")) { // perhaps we should use the size of the 1st element in the vector? double dx = resolveDoubleDispStrArg(ds.getTagArg("p", 3), submod, 2*sx); x += submod->getIndex()*dx; } else if (!strcmp(layout, "c") || !strcmp(layout, "col") || !strcmp(layout, "column")) { double dy = resolveDoubleDispStrArg(ds.getTagArg("p", 3), submod, 2*sy); y += submod->getIndex()*dy; } else if (!strcmp(layout, "m") || !strcmp(layout, "matrix")) { // perhaps we should use the size of the 1st element in the vector? int columns = resolveLongDispStrArg(ds.getTagArg("p", 3), submod, 5); double dx = resolveDoubleDispStrArg(ds.getTagArg("p", 4), submod, 2*sx); double dy = resolveDoubleDispStrArg(ds.getTagArg("p", 5), submod, 2*sy); if (columns < 1) columns = 1; x += (submod->getIndex() % columns)*dx; y += (submod->getIndex() / columns)*dy; } else if (!strcmp(layout, "i") || !strcmp(layout, "ri") || !strcmp(layout, "ring")) { // perhaps we should use the size of the 1st element in the vector? int vectorSize = submod->getVectorSize(); double rx = resolveDoubleDispStrArg(ds.getTagArg("p", 3), submod, (sx+sy)*vectorSize/4); double ry = resolveDoubleDispStrArg(ds.getTagArg("p", 4), submod, rx); x += rx - rx*sin(submod->getIndex()*2*PI/vectorSize); y += ry - ry*cos(submod->getIndex()*2*PI/vectorSize); } else { CHK(Tcl_VarEval(interp, "messagebox {Error} " "{Error: invalid layout '", layout, "' in 'p' tag " "of display string \"", ds.str(), "\"} error ok", TCL_NULL)); } } void ModuleInspector::recalculateLayout() { // refresh layout with empty submodPosMap -- everything layouted submodPosMap.clear(); refreshLayout(); } ModuleInspector::Point ModuleInspector::getSubmodulePosition(const cModule *submodule) { auto it = submodPosMap.find(const_cast(submodule)); return it != submodPosMap.end() ? it->second : Point{NAN,NAN}; } double ModuleInspector::getZoomLevel() { // read $inspectordata($c:zoomfactor) const char *s = Tcl_GetVar2(interp, "inspectordata", TCLCONST((std::string(canvas)+":zoomfactor").c_str()), TCL_GLOBAL_ONLY); return opp_atof(s); } void ModuleInspector::refreshLayout() { // recalculate layout, using coordinates in submodPosMap as "fixed" nodes -- // only new nodes are re-layouted cModule *parentModule = static_cast(object); // Note trick avoid calling getDisplayString() directly because it'd cause // the display string object inside cModule to spring into existence const cDisplayString blank; const cDisplayString& ds = parentModule->hasDisplayString() && parentModule->parametersFinalized() ? parentModule->getDisplayString() : blank; // create and configure layouter object LayouterChoice choice = getTkenv()->opt->layouterChoice; if (choice == LAYOUTER_AUTO) { const int LIMIT = 20; // note: on test/anim/dynamic2, Advanced is already very slow with 30-40 modules int submodCountLimited = 0; for (cModule::SubmoduleIterator it(parentModule); !it.end() && submodCountLimited < LIMIT; ++it) submodCountLimited++; choice = submodCountLimited >= LIMIT ? LAYOUTER_FAST : LAYOUTER_ADVANCED; } GraphLayouter *layouter = choice == LAYOUTER_FAST ? (GraphLayouter *)new BasicSpringEmbedderLayout() : (GraphLayouter *)new ForceDirectedGraphLayouter(); layouter->setSeed(layoutSeed); // background size int sx = resolveDoubleDispStrArg(ds.getTagArg("bgb", 0), parentModule, 0); int sy = resolveDoubleDispStrArg(ds.getTagArg("bgb", 1), parentModule, 0); int border = 30; if (sx != 0 && sx < 2*border) border = sx/2; if (sy != 0 && sy < 2*border) border = sy/2; layouter->setSize(sx, sy, border); // TODO support "bgp" tag ("background position") // loop through all submodules, get their sizes and positions and feed them into layouting engine for (cModule::SubmoduleIterator it(parentModule); !it.end(); ++it) { cModule *submod = *it; bool explicitCoords, obeysLayout; double x, y, sx, sy; getSubmoduleCoords(submod, explicitCoords, obeysLayout, x, y, sx, sy); // add node into layouter: if (explicitCoords) { // e.g. "p=120,70" or "p=140,30,ring" layouter->addFixedNode(submod->getId(), x, y, sx, sy); } else if (submodPosMap.find(submod) != submodPosMap.end()) { // reuse coordinates from previous layout Point pos = submodPosMap[submod]; layouter->addFixedNode(submod->getId(), pos.x, pos.y, sx, sy); } else if (obeysLayout) { // all modules are anchored to the anchor point with the vector's name // e.g. "p=,,ring" layouter->addAnchoredNode(submod->getId(), submod->getName(), x, y, sx, sy); } else { layouter->addMovableNode(submod->getId(), sx, sy); } } // add connections into the layouter, too bool atParent = false; for (cModule::SubmoduleIterator it(parentModule); !atParent; ++it) { cModule *mod = !it.end() ? *it : (atParent = true, parentModule); for (cModule::GateIterator git(mod); !git.end(); ++git) { cGate *gate = *git; cGate *destGate = gate->getNextGate(); if (gate->getType() == (atParent ? cGate::INPUT : cGate::OUTPUT) && destGate) { cModule *destMod = destGate->getOwnerModule(); if (mod == parentModule && destMod == parentModule) { // nop } else if (destMod == parentModule) { layouter->addEdgeToBorder(mod->getId()); } else if (destMod->getParentModule() != parentModule) { // connection goes to a module under a different parent! // this in fact violates module encapsulation, but let's // accept it nevertheless. Just skip this connection. } else if (mod == parentModule) { layouter->addEdgeToBorder(destMod->getId()); } else { layouter->addEdge(mod->getId(), destMod->getId()); } } } } // set up layouter environment (responsible for "Stop" button handling and visualizing the layouting process) TkenvGraphLayouterEnvironment environment(interp, parentModule, ds); std::string stopButton = std::string(getWindowName()) + ".toolbar.stop"; bool isExpressMode = getTkenv()->getSimulationRunMode() == Tkenv::RUNMODE_EXPRESS; if (!isExpressMode) environment.setWidgetToGrab(stopButton.c_str()); // enable visualizing only if full re-layouting (no cached coordinates in submodPosMap) // if (getTkenv()->opt->showlayouting) // for debugging if (submodPosMap.empty() && getTkenv()->opt->showLayouting) environment.setCanvas(canvas); layouter->setEnvironment(&environment); layouter->execute(); environment.cleanup(); // fill the map with the results submodPosMap.clear(); for (cModule::SubmoduleIterator it(parentModule); !it.end(); ++it) { cModule *submod = *it; Point pos; layouter->getNodePosition(submod->getId(), pos.x, pos.y); submodPosMap[submod] = pos; } layoutSeed = layouter->getSeed(); delete layouter; } // requires either recalculateLayout() or refreshLayout() called before! void ModuleInspector::redrawModules() { cModule *parentModule = static_cast(object); // then display all submodules CHK(Tcl_VarEval(interp, canvas, " delete dx", TCL_NULL)); // NOT "delete all" because that'd remove "bubbles" too! for (cModule::SubmoduleIterator it(parentModule); !it.end(); ++it) { cModule *submod = *it; assert(submodPosMap.find(submod) != submodPosMap.end()); Point& pos = submodPosMap[submod]; drawSubmodule(submod, pos.x, pos.y); } // draw enclosing module drawEnclosingModule(parentModule); // loop through all submodules and enclosing module & draw their connections bool atParent = false; for (cModule::SubmoduleIterator it(parentModule); !atParent; ++it) { cModule *mod = !it.end() ? *it : (atParent = true, parentModule); for (cModule::GateIterator git(mod); !git.end(); ++git) { cGate *gate = *git; if (gate->getType() == (atParent ? cGate::INPUT : cGate::OUTPUT) && gate->getNextGate() != nullptr) { drawConnection(gate); } } } CHK(Tcl_VarEval(interp, canvas, " raise bubble", TCL_NULL)); CHK(Tcl_VarEval(interp, "ModuleInspector:setScrollRegion ", windowName, " 0", TCL_NULL)); } void ModuleInspector::drawSubmodule(cModule *submod, double x, double y) { char coords[64]; sprintf(coords, "%g %g ", x, y); const char *dispstr = submod->hasDisplayString() && submod->parametersFinalized() ? submod->getDisplayString().str() : ""; CHK(Tcl_VarEval(interp, "ModuleInspector:drawSubmodule ", canvas, " ", ptrToStr(submod), " ", coords, "{", submod->getFullName(), "} ", TclQuotedString(dispstr).get(), " ", (submod->isPlaceholder() ? "1" : "0"), TCL_NULL)); } void ModuleInspector::drawEnclosingModule(cModule *parentModule) { const char *displayString = parentModule->hasDisplayString() && parentModule->parametersFinalized() ? parentModule->getDisplayString().str() : ""; CHK(Tcl_VarEval(interp, "ModuleInspector:drawEnclosingModule ", canvas, " ", ptrToStr(parentModule), " ", "{", parentModule->getFullPath().c_str(), "} ", TclQuotedString(displayString).get(), TCL_NULL)); } void ModuleInspector::drawConnection(cGate *gate) { cModule *mod = gate->getOwnerModule(); cGate *destGate = gate->getNextGate(); char gateptr[32], srcptr[32], destptr[32], chanptr[32], indices[32]; // check if this is a two way connection (an other connection is pointing back // to the this gate's pair from the next gate's pair) bool twoWayConnection = false; // check if this gate is really part of an in/out gate pair // gate o-------------------->o dest_gate // gate_pair o<--------------------o dest_gate_pair if (gate->getNameSuffix()[0]) { const cGate *gatePair = mod->gateHalf(gate->getBaseName(), gate->getType() == cGate::INPUT ? cGate::OUTPUT : cGate::INPUT, gate->isVector() ? gate->getIndex() : -1); if (destGate->getNameSuffix()[0]) { const cGate *destGatePair = destGate->getOwnerModule()->gateHalf(destGate->getBaseName(), destGate->getType() == cGate::INPUT ? cGate::OUTPUT : cGate::INPUT, destGate->isVector() ? destGate->getIndex() : -1); twoWayConnection = destGatePair == gatePair->getPreviousGate(); } } ptrToStr(gate, gateptr); ptrToStr(mod, srcptr); ptrToStr(destGate->getOwnerModule(), destptr); sprintf(indices, "%d %d %d %d", gate->getIndex(), gate->size(), destGate->getIndex(), destGate->size()); cChannel *chan = gate->getChannel(); ptrToStr(chan, chanptr); const char *dispstr = (chan && chan->hasDisplayString() && chan->parametersFinalized()) ? chan->getDisplayString().str() : ""; CHK(Tcl_VarEval(interp, "ModuleInspector:drawConnection ", canvas, " ", gateptr, " ", TclQuotedString(dispstr).get(), " ", srcptr, " ", destptr, " ", chanptr, " ", indices, " ", twoWayConnection ? "1" : "0", TCL_NULL)); } void ModuleInspector::redrawMessages() { // refresh & cleanup from prev. events CHK(Tcl_VarEval(interp, canvas, " delete msg msgname", TCL_NULL)); // this thingy is only needed if animation is going on if (!getTkenv()->animating) return; // loop through all messages in the event queue and display them cFutureEventSet *fes = getSimulation()->getFES(); int fesLen = fes->getLength(); for (int i = 0; i < fesLen; i++) { cEvent *event = fes->get(i); if (!event->isMessage()) continue; cMessage *msg = (cMessage *)event; cModule *arrivalMod = msg->getArrivalModule(); if (arrivalMod && arrivalMod->getParentModule() == static_cast(object) && msg->getArrivalGateId() >= 0) { char msgptr[32]; ptrToStr(msg, msgptr); cGate *arrivalGate = msg->getArrivalGate(); // if arrivalGate is connected, msg arrived on a connection, otherwise via sendDirect() if (arrivalGate->getPreviousGate()) { cGate *gate = arrivalGate->getPreviousGate(); CHK(Tcl_VarEval(interp, "ModuleInspector:drawMessageOnGate ", canvas, " ", ptrToStr(gate), " ", msgptr, TCL_NULL)); } else { CHK(Tcl_VarEval(interp, "ModuleInspector:drawMessageOnModule ", canvas, " ", ptrToStr(arrivalMod), " ", msgptr, TCL_NULL)); } } } CHK(Tcl_VarEval(interp, canvas, " raise bubble", TCL_NULL)); } void ModuleInspector::redrawNextEventMarker() { cModule *mod = static_cast(object); // removing marker from previous event CHK(Tcl_VarEval(interp, canvas, " delete nexteventmarker", TCL_NULL)); // this thingy is only needed if animation is going on if (!getTkenv()->animating || !getTkenv()->opt->showNextEventMarkers) return; // if any parent of the module containing the next event is on this canvas, draw marker cModule *nextMod = getSimulation()->guessNextModule(); cModule *nextModParent = nextMod; while (nextModParent && nextModParent->getParentModule() != mod) nextModParent = nextModParent->getParentModule(); if (nextModParent) { CHK(Tcl_VarEval(interp, "ModuleInspector:drawNextEventMarker ", canvas, " ", ptrToStr(nextModParent), " ", (nextMod == nextModParent ? "2" : "1"), TCL_NULL)); } } void ModuleInspector::redrawFigures() { FigureRenderingHints hints; fillFigureRenderingHints(&hints); canvasRenderer->redraw(&hints); } void ModuleInspector::refreshFigures() { FigureRenderingHints hints; fillFigureRenderingHints(&hints); canvasRenderer->refresh(&hints); } void ModuleInspector::fillFigureRenderingHints(FigureRenderingHints *hints) { const char *s; // read $inspectordata($c:zoomfactor) s = Tcl_GetVar2(interp, "inspectordata", TCLCONST((std::string(canvas)+":zoomfactor").c_str()), TCL_GLOBAL_ONLY); hints->zoom = opp_atof(s); // read $inspectordata($c:imagesizefactor) s = Tcl_GetVar2(interp, "inspectordata", TCLCONST((std::string(canvas)+":imagesizefactor").c_str()), TCL_GLOBAL_ONLY); hints->iconMagnification = opp_atof(s); // read $inspectordata($c:showlabels) s = Tcl_GetVar2(interp, "inspectordata", TCLCONST((std::string(canvas)+":showlabels").c_str()), TCL_GLOBAL_ONLY); hints->showSubmoduleLabels = opp_atol(s) != 0; // read $inspectordata($c:showarrowheads) s = Tcl_GetVar2(interp, "inspectordata", TCLCONST((std::string(canvas)+":showarrowheads").c_str()), TCL_GLOBAL_ONLY); hints->showArrowheads = opp_atol(s) != 0; Tcl_Eval(interp, "font actual CanvasFont -family"); hints->defaultFont = Tcl_GetStringResult(interp); Tcl_Eval(interp, "font actual CanvasFont -size"); s = Tcl_GetStringResult(interp); hints->defaultFontSize = opp_atol(s); } void ModuleInspector::refreshSubmodules() { for (cModule::SubmoduleIterator it(static_cast(object)); !it.end(); ++it) { CHK(Tcl_VarEval(interp, "ModuleInspector:refreshSubmodule ", windowName, " ", ptrToStr(*it), TCL_NULL)); } } void ModuleInspector::submoduleCreated(cModule *newmodule) { needs_redraw = true; } void ModuleInspector::submoduleDeleted(cModule *module) { needs_redraw = true; } void ModuleInspector::connectionCreated(cGate *srcgate) { needs_redraw = true; } void ModuleInspector::connectionDeleted(cGate *srcgate) { needs_redraw = true; } void ModuleInspector::displayStringChanged(cModule *) { needs_redraw = true; } void ModuleInspector::displayStringChanged() { needs_redraw = true; // TODO check, probably only non-background tags have changed... } void ModuleInspector::displayStringChanged(cGate *) { needs_redraw = true; } void ModuleInspector::bubble(cComponent *subcomponent, const char *text) { if (!subcomponent->isModule()) return; // channel bubbles not yet supported // if submod position is not yet known (because e.g. we're in fast mode // and it was dynamically created since the last update), refresh layout // so that we can get coordinates for it cModule *submod = (cModule *)subcomponent; if (submodPosMap.find(submod) == submodPosMap.end()) refreshLayout(); // invoke Tcl code to display bubble char coords[64]; Point& pos = submodPosMap[submod]; sprintf(coords, " %g %g ", pos.x, pos.y); CHK(Tcl_VarEval(interp, "ModuleInspector:bubble ", canvas, coords, " ", TclQuotedString(text).get(), TCL_NULL)); } const char *ModuleInspector::animModeToStr(SendAnimMode mode) { return mode == ANIM_BEGIN ? "beg" : mode == ANIM_THROUGH ? "thru" : mode == ANIM_END ? "end" : "???"; } void ModuleInspector::animateMethodcallAscent(cModule *srcSubmod, const char *methodText) { char parentPtr[30], modPtr[30]; ptrToStr(object, parentPtr); ptrToStr(srcSubmod, modPtr); CHK(Tcl_VarEval(interp, "ModuleInspector:animateMethodcallAscent ", getWindowName(), " ", parentPtr, " ", modPtr, " ", TclQuotedString(methodText).get(), TCL_NULL)); } void ModuleInspector::animateMethodcallDescent(cModule *destSubmod, const char *methodText) { char parentPtr[30], modPtr[30]; ptrToStr(object, parentPtr); ptrToStr(destSubmod, modPtr); CHK(Tcl_VarEval(interp, "ModuleInspector:animateMethodcallDescent ", getWindowName(), " ", parentPtr, " ", modPtr, " ", TclQuotedString(methodText).get(), TCL_NULL)); } void ModuleInspector::animateMethodcallHoriz(cModule *srcSubmod, cModule *destSubmod, const char *methodText) { char srcPtr[30], destPtr[30]; ptrToStr(srcSubmod, srcPtr); ptrToStr(destSubmod, destPtr); CHK(Tcl_VarEval(interp, "ModuleInspector:animateMethodcallHoriz ", getWindowName(), " ", srcPtr, " ", destPtr, " ", TclQuotedString(methodText).get(), TCL_NULL)); } void ModuleInspector::animateMethodcallDelay(Tcl_Interp *interp) { CHK(Tcl_Eval(interp, "ModuleInspector:animateMethodcallWait")); } void ModuleInspector::animateMethodcallCleanup() { CHK(Tcl_VarEval(interp, "ModuleInspector:animateMethodcallCleanup ", getWindowName(), TCL_NULL)); } void ModuleInspector::animateSendOnConn(cGate *srcGate, cMessage *msg, SendAnimMode mode) { char gatePtr[30], msgPtr[30]; ptrToStr(srcGate, gatePtr); ptrToStr(msg, msgPtr); const char *modeStr = animModeToStr(mode); CHK(Tcl_VarEval(interp, "ModuleInspector:animateOnConn ", getWindowName(), " ", msgPtr, " ", gatePtr, " ", modeStr, TCL_NULL)); } void ModuleInspector::animateSenddirectAscent(cModule *srcSubmod, cMessage *msg) { char parentPtr[30], modPtr[30], msgPtr[30]; ptrToStr(object, parentPtr); ptrToStr(srcSubmod, modPtr); ptrToStr(msg, msgPtr); CHK(Tcl_VarEval(interp, "ModuleInspector:animateSenddirectAscent ", getWindowName(), " ", msgPtr, " ", parentPtr, " ", modPtr, " ", "thru", TCL_NULL)); } void ModuleInspector::animateSenddirectDescent(cModule *destSubmod, cMessage *msg, SendAnimMode mode) { char parentPtr[30], modPtr[30], msgPtr[30]; ptrToStr(object, parentPtr); ptrToStr(destSubmod, modPtr); ptrToStr(msg, msgPtr); const char *modeStr = animModeToStr(mode); CHK(Tcl_VarEval(interp, "ModuleInspector:animateSenddirectDescent ", getWindowName(), " ", msgPtr, " ", parentPtr, " ", modPtr, " ", modeStr, TCL_NULL)); } void ModuleInspector::animateSenddirectHoriz(cModule *srcSubmod, cModule *destSubmod, cMessage *msg, SendAnimMode mode) { char srcPtr[30], destPtr[30], msgPtr[30]; ptrToStr(srcSubmod, srcPtr); ptrToStr(destSubmod, destPtr); ptrToStr(msg, msgPtr); const char *modeStr = animModeToStr(mode); CHK(Tcl_VarEval(interp, "ModuleInspector:animateSenddirectHoriz ", getWindowName(), " ", msgPtr, " ", srcPtr, " ", destPtr, " ", modeStr, TCL_NULL)); } void ModuleInspector::animateSenddirectCleanup() { CHK(Tcl_VarEval(interp, "ModuleInspector:animateSenddirectCleanup ", getWindowName(), TCL_NULL)); } void ModuleInspector::animateSenddirectDelivery(cModule *destSubmod, cMessage *msg) { char destPtr[30], msgPtr[30]; ptrToStr(destSubmod, destPtr); ptrToStr(msg, msgPtr); CHK(Tcl_VarEval(interp, "ModuleInspector:animateSenddirectDelivery ", getWindowName(), " ", msgPtr, " ", destPtr, TCL_NULL)); } void ModuleInspector::performAnimations(Tcl_Interp *interp) { CHK(Tcl_VarEval(interp, "performAnimations", TCL_NULL)); } void ModuleInspector::updateBackgroundColor() { cCanvas *canvas = getCanvas(); if (canvas) { char buf[16]; cFigure::Color color = canvas->getBackgroundColor(); sprintf(buf, "#%2.2x%2.2x%2.2x", color.red, color.green, color.blue); CHK(Tcl_VarEval(interp, this->canvas, " config -bg {", buf, "}", TCL_NULL)); } } int ModuleInspector::inspectorCommand(int argc, const char **argv) { if (argc < 1) { Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR; } E_TRY if (strcmp(argv[0], "arrowcoords") == 0) { return arrowcoords(interp, argc, argv); } else if (strcmp(argv[0], "relayout") == 0) { TRY(relayoutAndRedrawAll()); return TCL_OK; } else if (strcmp(argv[0], "redraw") == 0) { TRY(redraw()); return TCL_OK; } else if (strcmp(argv[0], "getdefaultlayoutseed") == 0) { return getDefaultLayoutSeed(argc, argv); } else if (strcmp(argv[0], "getlayoutseed") == 0) { return getLayoutSeed(argc, argv); } else if (strcmp(argv[0], "setlayoutseed") == 0) { return setLayoutSeed(argc, argv); } else if (strcmp(argv[0], "submodulecount") == 0) { return getSubmoduleCount(argc, argv); } else if (strcmp(argv[0], "getsubmodq") == 0) { return getSubmodQ(argc, argv); } else if (strcmp(argv[0], "getsubmodqlen") == 0) { return getSubmodQLen(argc, argv); } else if (strcmp(argv[0], "hascanvas") == 0) { // needed because getalltags/getenabledtags/etc throw error if there's no canvas Tcl_SetResult(interp, TCLCONST(canvasRenderer->hasCanvas() ? "1" : "0"), TCL_STATIC); return TCL_OK; } else if (strcmp(argv[0], "getalltags") == 0) { Tcl_SetResult(interp, TCLCONST(canvasRenderer->getAllTags().c_str()), TCL_VOLATILE); return TCL_OK; } else if (strcmp(argv[0], "getenabledtags") == 0) { Tcl_SetResult(interp, TCLCONST(canvasRenderer->getEnabledTags().c_str()), TCL_VOLATILE); return TCL_OK; } else if (strcmp(argv[0], "getexcepttags") == 0) { Tcl_SetResult(interp, TCLCONST(canvasRenderer->getExceptTags().c_str()), TCL_VOLATILE); return TCL_OK; } else if (strcmp(argv[0], "setenabledtags") == 0) { if (argc != 2) { Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR; } canvasRenderer->setEnabledTags(argv[1]); return TCL_OK; } else if (strcmp(argv[0], "setexcepttags") == 0) { if (argc != 2) { Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR; } canvasRenderer->setExceptTags(argv[1]); return TCL_OK; } E_CATCH return Inspector::inspectorCommand(argc, argv); } int ModuleInspector::getDefaultLayoutSeed(int argc, const char **argv) { if (argc != 1) { Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR; } const cDisplayString blank; cModule *parentModule = static_cast(object); const cDisplayString& ds = parentModule && parentModule->hasDisplayString() && parentModule->parametersFinalized() ? parentModule->getDisplayString() : blank; long seed = resolveLongDispStrArg(ds.getTagArg("bgl", 4), parentModule, 1); Tcl_SetObjResult(interp, Tcl_NewIntObj((int)seed)); return TCL_OK; } int ModuleInspector::getLayoutSeed(int argc, const char **argv) { if (argc != 1) { Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj((int)layoutSeed)); return TCL_OK; } int ModuleInspector::setLayoutSeed(int argc, const char **argv) { if (argc != 2) { Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR; } layoutSeed = atol(argv[1]); return TCL_OK; } int ModuleInspector::getSubmoduleCount(int argc, const char **argv) { if (argc != 1) { Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR; } int count = 0; for (cModule::SubmoduleIterator it(static_cast(object)); !it.end(); ++it) count++; Tcl_SetObjResult(interp, Tcl_NewIntObj(count)); return TCL_OK; } int ModuleInspector::getSubmodQ(int argc, const char **argv) { // args: if (argc != 3) { Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR; } cModule *mod = dynamic_cast(strToPtr(argv[1])); const char *qname = argv[2]; cQueue *q = dynamic_cast(mod->findObject(qname)); char buf[21]; ptrToStr(q, buf); Tcl_SetResult(interp, buf, TCL_VOLATILE); return TCL_OK; } int ModuleInspector::getSubmodQLen(int argc, const char **argv) { // args: if (argc != 3) { Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR; } cModule *mod = dynamic_cast(strToPtr(argv[1])); const char *qname = argv[2]; cQueue *q = dynamic_cast(mod->findObject(qname)); // FIXME THIS MUST BE REFINED! SEARCHES WAY TOO DEEEEEP!!!! if (!q) { Tcl_SetResult(interp, TCLCONST(""), TCL_STATIC); return TCL_OK; } Tcl_SetObjResult(interp, Tcl_NewIntObj(q->getLength())); return TCL_OK; } } // namespace tkenv } // namespace omnetpp