Statistics
| Branch: | Revision:

root / src / tkenv / modinsp.cc @ 7c7d95e5

History | View | Annotate | Download (44.2 KB)

1 01873262 Georg Kunz
//==========================================================================
2
//  MODINSP.CC - part of
3
//
4
//                     OMNeT++/OMNEST
5
//            Discrete System Simulation in C++
6
//
7
//  Implementation of
8
//    inspectors
9
//
10
//==========================================================================
11
12
/*--------------------------------------------------------------*
13
  Copyright (C) 1992-2008 Andras Varga
14
  Copyright (C) 2006-2008 OpenSim Ltd.
15

16
  This file is distributed WITHOUT ANY WARRANTY. See the file
17
  `license' for details on this and other legal matters.
18
*--------------------------------------------------------------*/
19
20
#include <string.h>
21
#include <stdlib.h>
22
#include <math.h>
23
#include <assert.h>
24
25
#include "modinsp.h"
26
#include "cchannel.h"
27
#include "tkenv.h"
28
#include "tklib.h"
29
#include "tkutil.h"
30
#include "inspfactory.h"
31
#include "arrow.h"
32
#include "graphlayouter.h"
33
#include "layouterenv.h"
34
#include "forcedirectedgraphlayouter.h"
35
#include "basicspringembedderlayout.h"
36
#include "stringtokenizer.h"
37
38
USING_NAMESPACE
39
40
#define UNKNOWNICON_WIDTH  32
41
#define UNKNOWNICON_HEIGHT 32
42
43
void _dummy_for_modinsp() {}
44
45
46
class TModuleWindowFactory : public cInspectorFactory
47
{
48
  public:
49
    TModuleWindowFactory(const char *name) : cInspectorFactory(name) {}
50
51
    bool supportsObject(cObject *obj) {return dynamic_cast<cModule *>(obj)!=NULL;}
52
    int inspectorType() {return INSP_MODULEOUTPUT;}
53
    double qualityAsDefault(cObject *object) {return 0.5;}
54
55
    TInspector *createInspectorFor(cObject *object,int type,const char *geom,void *data) {
56
        return new TModuleWindow(object, type, geom, data);
57
    }
58
};
59
60
Register_InspectorFactory(TModuleWindowFactory);
61
62
63
TModuleWindow::TModuleWindow(cObject *obj,int typ,const char *geom,void *dat) :
64
    TInspector(obj,typ,geom,dat)
65
{
66
}
67
68
void TModuleWindow::createWindow()
69
{
70
    TInspector::createWindow(); // create window name etc.
71
    strcpy(textWidget,windowname); strcat(textWidget, ".main.text");
72
73
    // create inspector window by calling the specified proc with
74
    // the object's pointer. Window name will be like ".ptr80003a9d-1"
75
    Tcl_Interp *interp = getTkenv()->getInterp();
76
    cModule *mod = static_cast<cModule *>(object);
77
    const char *createcommand = mod->isSimple() ?
78
             "create_simplemodulewindow " : "create_compoundmodulewindow ";
79
    CHK(Tcl_VarEval(interp, createcommand, windowname, " \"", geometry, "\"", NULL ));
80
    redisplay(getTkenv()->getLogBuffer());
81
}
82
83
void TModuleWindow::update()
84
{
85
    TInspector::update();
86
87
    Tcl_Interp *interp = getTkenv()->getInterp();
88
    CHK(Tcl_VarEval(interp, "modulewindow_trimlines ", windowname, NULL));
89
}
90
91
void TModuleWindow::printLastLineOf(const LogBuffer& logBuffer)
92
{
93
    printLastLineOf(getTkenv()->getInterp(), textWidget, logBuffer, excludedModuleIds);
94
}
95
96
void TModuleWindow::redisplay(const LogBuffer& logBuffer)
97
{
98
    redisplay(getTkenv()->getInterp(), textWidget, logBuffer, static_cast<cModule *>(object), excludedModuleIds);
99
}
100
101
void TModuleWindow::printLastLineOf(Tcl_Interp *interp, const char *textWidget, const LogBuffer& logBuffer, const std::set<int>& excludedModuleIds)
102
{
103
    const LogBuffer::Entry& entry = logBuffer.getEntries().back();
104
    if (!entry.moduleIds)
105
    {
106
        if (entry.lines.empty())
107
            textWidget_insert(interp, textWidget, entry.banner, "log");
108
        else
109
            textWidget_insert(interp, textWidget, entry.lines.back());
110
    }
111
    else if (excludedModuleIds.find(entry.moduleIds[0])==excludedModuleIds.end())
112
    {
113
        if (entry.lines.empty())
114
            textWidget_insert(interp, textWidget, entry.banner, "event");
115
        else
116
            textWidget_insert(interp, textWidget, entry.lines.back());
117
    }
118
    textWidget_gotoEnd(interp, textWidget);
119
}
120
121
void TModuleWindow::redisplay(Tcl_Interp *interp, const char *textWidget, const LogBuffer& logBuffer, cModule *mod, const std::set<int>& excludedModuleIds)
122
{
123
    textWidget_clear(interp, textWidget);
124
125
    if (!mod)
126
        return;
127
128
    int inspModuleId = mod->getId();
129
    const std::list<LogBuffer::Entry>& entries = logBuffer.getEntries();
130
    for (std::list<LogBuffer::Entry>::const_iterator it=entries.begin(); it!=entries.end(); it++)
131
    {
132
        const LogBuffer::Entry& entry = *it;
133
        if (!entry.moduleIds)
134
        {
135
            textWidget_insert(interp, textWidget, entry.banner, "log");
136
            for (int i=0; i<(int)entry.lines.size(); i++)
137
                textWidget_insert(interp, textWidget, entry.lines[i]); //?
138
        }
139
        else
140
        {
141
            // check that this module is covered in entry.moduleIds[] (module path up to the root)
142
            bool found = false;
143
            for (int *p = entry.moduleIds; !found && *p; p++)
144
                if (*p == inspModuleId)
145
                    found = true;
146
147
            // if so, and is not excluded, display log
148
            if (found && excludedModuleIds.find(entry.moduleIds[0])==excludedModuleIds.end())
149
            {
150
                textWidget_insert(interp, textWidget, entry.banner, "event");
151
                for (int i=0; i<(int)entry.lines.size(); i++)
152
                    textWidget_insert(interp, textWidget, entry.lines[i]);
153
            }
154
        }
155
    }
156
    textWidget_gotoEnd(interp, textWidget);
157
}
158
159
int TModuleWindow::inspectorCommand(Tcl_Interp *interp, int argc, const char **argv)
160
{
161
    if (argc<1) {Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR;}
162
163
    // supported commands: redisplay, getexcludedmoduleids, setexcludedmoduleids
164
165
    if (strcmp(argv[0],"redisplay")==0)
166
    {
167
       if (argc!=1) {Tcl_SetResult(interp, TCLCONST("wrong argcount"), TCL_STATIC); return TCL_ERROR;}
168
       TRY(redisplay(getTkenv()->getLogBuffer()));
169
       return TCL_OK;
170
    }
171
    else if (strcmp(argv[0],"getexcludedmoduleids")==0)
172
    {
173
       if (argc!=1) {Tcl_SetResult(interp, TCLCONST("wrong argcount"), TCL_STATIC); return TCL_ERROR;}
174
       Tcl_Obj *listobj = Tcl_NewListObj(0, NULL);
175
       for (std::set<int>::iterator it=excludedModuleIds.begin(); it!=excludedModuleIds.end(); it++)
176
           Tcl_ListObjAppendElement(interp, listobj, Tcl_NewIntObj(*it));
177
       Tcl_SetObjResult(interp, listobj);
178
       return TCL_OK;
179
    }
180
    else if (strcmp(argv[0],"setexcludedmoduleids")==0)
181
    {
182
       if (argc!=2) {Tcl_SetResult(interp, TCLCONST("wrong argcount"), TCL_STATIC); return TCL_ERROR;}
183
       excludedModuleIds.clear();
184
       StringTokenizer tokenizer(argv[1]);
185
       while (tokenizer.hasMoreTokens())
186
           excludedModuleIds.insert(atoi(tokenizer.nextToken()));
187
       return TCL_OK;
188
    }
189
    return TCL_ERROR;
190
}
191
192
//=======================================================================
193
194
class TGraphicalModWindowFactory : public cInspectorFactory
195
{
196
  public:
197
    TGraphicalModWindowFactory(const char *name) : cInspectorFactory(name) {}
198
199
    bool supportsObject(cObject *obj) {return dynamic_cast<cModule *>(obj)!=NULL;}
200
    int inspectorType() {return INSP_GRAPHICAL;}
201
    double qualityAsDefault(cObject *object) {
202
        return dynamic_cast<cSimpleModule *>(object) ? 0.9 : 3.0;
203
    }
204
205
    TInspector *createInspectorFor(cObject *object,int type,const char *geom,void *data) {
206
        return new TGraphicalModWindow(object, type, geom, data);
207
    }
208
};
209
210
Register_InspectorFactory(TGraphicalModWindowFactory);
211
212
213
TGraphicalModWindow::TGraphicalModWindow(cObject *obj,int typ,const char *geom,void *dat) :
214
    TInspector(obj,typ,geom,dat)
215
{
216
   needs_redraw = false;
217
   not_drawn = false;
218
219
   const cDisplayString blank;
220
   cModule *parentmodule = static_cast<cModule *>(object);
221
   const cDisplayString& ds = parentmodule->hasDisplayString() ? parentmodule->getDisplayString() : blank;
222
   random_seed = resolveLongDispStrArg(ds.getTagArg("bgl",4), parentmodule, 1);
223
}
224
225
TGraphicalModWindow::~TGraphicalModWindow()
226
{
227
}
228
229
void TGraphicalModWindow::createWindow()
230
{
231
   TInspector::createWindow(); // create window name etc.
232
   strcpy(canvas,windowname); strcat(canvas,".c");
233
234
   // create inspector window by calling the specified proc with
235
   // the object's pointer. Window name will be like ".ptr80003a9d-1"
236
   Tcl_Interp *interp = getTkenv()->getInterp();
237
   CHK(Tcl_VarEval(interp, "create_graphicalmodwindow ", windowname, " \"", geometry, "\"", NULL ));
238
}
239
240
void TGraphicalModWindow::update()
241
{
242
   TInspector::update();
243
244
   if (not_drawn) return;
245
246
   // redraw modules only if really needed
247
   if (needs_redraw)
248
   {
249
       needs_redraw = false;
250
       redrawAll();
251
   }
252
   else
253
   {
254
       redrawNextEventMarker();
255
       redrawMessages();
256
       updateSubmodules();
257
   }
258
}
259
260
void TGraphicalModWindow::relayoutAndRedrawAll()
261
{
262
   cModule *mod = (cModule *)object;
263
   int submodcount = 0;
264
   int gatecountestimate = mod->gateCount();
265
   for (cModule::SubmoduleIterator submod(mod); !submod.end(); submod++)
266
   {
267
       submodcount++;
268
       // note: gatecountestimate will count unconnected gates in the gate array as well
269
       gatecountestimate += submod()->gateCount();
270
   }
271
272
   not_drawn = false;
273
   if (submodcount>1000 || gatecountestimate>4000)
274
   {
275
       Tcl_Interp *interp = getTkenv()->getInterp();
276
       char problem[200];
277
       if (submodcount>1000)
278
           sprintf(problem, "contains more than 1000 submodules (exactly %d)", submodcount);
279
       else
280
           sprintf(problem, "may contain a lot of connections (modules have a large number of gates)");
281
       CHK(Tcl_VarEval(interp,"tk_messageBox -parent ",windowname," -type yesno -title Warning -icon question "
282
                              "-message {Module '", object->getFullName(), "' ", problem,
283
                              ", it may take a long time to display the graphics. "
284
                              "Do you want to proceed with drawing?}", NULL));
285
       bool answer = (Tcl_GetStringResult(interp)[0]=='y');
286
       if (answer==false)
287
       {
288
           not_drawn = true;
289
           CHK(Tcl_VarEval(interp, canvas, " delete all",NULL)); // this must be done, still
290
           return;
291
       }
292
   }
293
294
   // go to next seed
295
   random_seed++;
296
   recalculateLayout();
297
   redrawModules();
298
   redrawNextEventMarker();
299
   redrawMessages();
300
   updateSubmodules();
301
}
302
303
void TGraphicalModWindow::redrawAll()
304
{
305
   refreshLayout();
306
   redrawModules();
307
   redrawNextEventMarker();
308
   redrawMessages();
309
   updateSubmodules();
310
}
311
312
void TGraphicalModWindow::getSubmoduleCoords(cModule *submod, bool& explicitcoords, bool& obeyslayout,
313
                                                              int& x, int& y, int& sx, int& sy)
314
{
315
    const cDisplayString blank;
316
    const cDisplayString& ds = submod->hasDisplayString() ? submod->getDisplayString() : blank;
317
318
    // get size -- we'll need to return that too, and may be needed for matrix, ring etc. layout
319
    int boxsx=0, boxsy=0, iconsx=0, iconsy=0;
320
    if (ds.containsTag("b") || !ds.containsTag("i"))
321
    {
322
        boxsx = resolveLongDispStrArg(ds.getTagArg("b",0), submod, 40);
323
        boxsy = resolveLongDispStrArg(ds.getTagArg("b",1), submod, 24);
324
    }
325
    if (ds.containsTag("i"))
326
    {
327
        const char *imgname = ds.getTagArg("i",0);
328
        const char *imgsize = ds.getTagArg("is",0);
329
        if (!imgname || !*imgname)
330
        {
331
            iconsx = UNKNOWNICON_WIDTH;
332
            iconsy = UNKNOWNICON_HEIGHT;
333
        }
334
        else
335
        {
336
            Tcl_Interp *interp = getTkenv()->getInterp();
337
            Tcl_VarEval(interp, "lookup_image ", imgname, " ", imgsize, NULL);
338
            Tk_Image img = Tk_GetImage(interp, Tk_MainWindow(interp), Tcl_GetStringResult(interp), NULL, NULL);
339
            if (!img)
340
            {
341
                iconsx = UNKNOWNICON_WIDTH;
342
                iconsy = UNKNOWNICON_HEIGHT;
343
            }
344
            else
345
            {
346
                Tk_SizeOfImage(img, &iconsx, &iconsy);
347
                Tk_FreeImage(img);
348
            }
349
        }
350
    }
351
    sx = (boxsx>iconsx) ? boxsx : iconsx;
352
    sy = (boxsy>iconsy) ? boxsy : iconsy;
353
354
    // first, see if there's an explicit position ("p=" tag) given
355
    x = resolveLongDispStrArg(ds.getTagArg("p",0), submod, -1);
356
    y = resolveLongDispStrArg(ds.getTagArg("p",1), submod, -1);
357
    explicitcoords = x!=-1 && y!=-1;
358
359
    // set missing coordinates to zero
360
    if (x==-1) x = 0;
361
    if (y==-1) y = 0;
362
363
    const char *layout = ds.getTagArg("p",2); // matrix, row, column, ring, exact etc.
364
    obeyslayout = (layout && *layout);
365
366
    // modify x,y using predefined layouts
367
    if (!layout || !*layout)
368
    {
369
        // we're happy
370
    }
371
    else if (!strcmp(layout,"e") || !strcmp(layout,"x") || !strcmp(layout,"exact"))
372
    {
373
        int dx = resolveLongDispStrArg(ds.getTagArg("p",3), submod, 0);
374
        int dy = resolveLongDispStrArg(ds.getTagArg("p",4), submod, 0);
375
        x += dx;
376
        y += dy;
377
    }
378
    else if (!strcmp(layout,"r") || !strcmp(layout,"row"))
379
    {
380
        // perhaps we should use the size of the 1st element in the vector?
381
        int dx = resolveLongDispStrArg(ds.getTagArg("p",3), submod, 2*sx);
382
        x += submod->getIndex()*dx;
383
    }
384
    else if (!strcmp(layout,"c") || !strcmp(layout,"col") || !strcmp(layout,"column"))
385
    {
386
        int dy = resolveLongDispStrArg(ds.getTagArg("p",3), submod, 2*sy);
387
        y += submod->getIndex()*dy;
388
    }
389
    else if (!strcmp(layout,"m") || !strcmp(layout,"matrix"))
390
    {
391
        // perhaps we should use the size of the 1st element in the vector?
392
        int columns = resolveLongDispStrArg(ds.getTagArg("p",3), submod, 5);
393
        int dx = resolveLongDispStrArg(ds.getTagArg("p",4), submod, 2*sx);
394
        int dy = resolveLongDispStrArg(ds.getTagArg("p",5), submod, 2*sy);
395
        x += (submod->getIndex() % columns)*dx;
396
        y += (submod->getIndex() / columns)*dy;
397
    }
398
    else if (!strcmp(layout,"i") || !strcmp(layout,"ri") || !strcmp(layout,"ring"))
399
    {
400
        // perhaps we should use the size of the 1st element in the vector?
401
        int rx = resolveLongDispStrArg(ds.getTagArg("p",3), submod, (sx+sy)*submod->size()/4);
402
        int ry = resolveLongDispStrArg(ds.getTagArg("p",4), submod, rx);
403
404
        x += (int) floor(rx - rx*sin(submod->getIndex()*2*PI/submod->size()));
405
        y += (int) floor(ry - ry*cos(submod->getIndex()*2*PI/submod->size()));
406
    }
407
    else
408
    {
409
        Tcl_Interp *interp = getTkenv()->getInterp();
410
        CHK(Tcl_VarEval(interp,"messagebox {Error} "
411
                        "{Error: invalid layout `", layout, "' in `p' tag "
412
                        "of display string \"", ds.str(), "\"} error ok", NULL));
413
    }
414
}
415
416
void TGraphicalModWindow::recalculateLayout()
417
{
418
    // refresh layout with empty submodPosMap -- everything layouted
419
    submodPosMap.clear();
420
    refreshLayout();
421
}
422
423
void TGraphicalModWindow::refreshLayout()
424
{
425
    // recalculate layout, using coordinates in submodPosMap as "fixed" nodes --
426
    // only new nodes are re-layouted
427
428
    cModule *parentmodule = static_cast<cModule *>(object);
429
430
    // Note trick avoid calling getDisplayString() directly because it'd cause
431
    // the display string object inside cModule to spring into existence
432
    const cDisplayString blank;
433
    const cDisplayString& ds = parentmodule->hasDisplayString() ? parentmodule->getDisplayString() : blank;
434
435
    // create and configure layouter object
436
    Tkenv::LayouterChoice choice = getTkenv()->opt_layouterchoice;
437
    if (choice==Tkenv::LAYOUTER_AUTO)
438
    {
439
        const int LIMIT = 20; // note: on test/anim/dynamic2, Advanced is already very slow with 30-40 modules
440
        int submodCountLimited = 0;
441
        for (cModule::SubmoduleIterator submod(parentmodule); !submod.end() && submodCountLimited<LIMIT; submod++)
442
            submodCountLimited++;
443
        choice = submodCountLimited>=LIMIT ? Tkenv::LAYOUTER_FAST : Tkenv::LAYOUTER_ADVANCED;
444
    }
445
    GraphLayouter *layouter = choice==Tkenv::LAYOUTER_FAST ?
446
                                    (GraphLayouter *) new BasicSpringEmbedderLayout() :
447
                                    (GraphLayouter *) new ForceDirectedGraphLayouter();
448
449
    layouter->setSeed(random_seed);
450
451
    // background size
452
    int sx = resolveLongDispStrArg(ds.getTagArg("bgb",0), parentmodule, 0);
453
    int sy = resolveLongDispStrArg(ds.getTagArg("bgb",1), parentmodule, 0);
454
    int border = 30;
455
    if (sx!=0 && sx < 2*border)
456
        border = sx/2;
457
    if (sy!=0 && sy < 2*border)
458
        border = sy/2;
459
    layouter->setSize(sx, sy, border);
460
    // TODO support "bgp" tag ("background position")
461
    // TODO: scaling ("bgs") support for layouter.
462
    // Layouter algorithm is NOT scale-independent, so we should divide ALL coordinates
463
    // by "scale" before passing them to the layouter, then multiply back the results.
464
465
    // loop through all submodules, get their sizes and positions and feed them into layouting engine
466
    for (cModule::SubmoduleIterator it(parentmodule); !it.end(); it++)
467
    {
468
        cModule *submod = it();
469
470
        bool explicitcoords, obeyslayout;
471
        int x, y, sx, sy;
472
        getSubmoduleCoords(submod, explicitcoords, obeyslayout, x, y, sx, sy);
473
474
        // add node into layouter:
475
        if (explicitcoords)
476
        {
477
            // e.g. "p=120,70" or "p=140,30,ring"
478
            layouter->addFixedNode(submod, x, y, sx, sy);
479
        }
480
        else if (submodPosMap.find(submod)!=submodPosMap.end())
481
        {
482
            // reuse coordinates from previous layout
483
            Point pos = submodPosMap[submod];
484
            layouter->addFixedNode(submod, pos.x, pos.y, sx, sy);
485
        }
486
        else if (obeyslayout)
487
        {
488
            // all modules are anchored to the anchor point with the vector's name
489
            // e.g. "p=,,ring"
490
            layouter->addAnchoredNode(submod, submod->getName(), x, y, sx, sy);
491
        }
492
        else
493
        {
494
            layouter->addMovableNode(submod, sx, sy);
495
        }
496
    }
497
498
    // add connections into the layouter, too
499
    bool parent = false;
500
    for (cModule::SubmoduleIterator it(parentmodule); !parent; it++)
501
    {
502
        cModule *mod = !it.end() ? it() : (parent=true,parentmodule);
503
504
        for (cModule::GateIterator i(mod); !i.end(); i++)
505
        {
506
            cGate *gate = i();
507
            cGate *destgate = gate->getNextGate();
508
            if (gate->getType()==(parent ? cGate::INPUT : cGate::OUTPUT) && destgate)
509
            {
510
                cModule *destmod = destgate->getOwnerModule();
511
                if (mod==parentmodule && destmod==parentmodule) {
512
                    // nop
513
                } else if (destmod==parentmodule) {
514
                    layouter->addEdgeToBorder(mod);
515
                } else if (destmod->getParentModule()!=parentmodule) {
516
                    // connection goes to a module under a different parent!
517
                    // this in fact violates module encapsulation, but let's
518
                    // accept it nevertheless. Just skip this connection.
519
                } else if (mod==parentmodule) {
520
                    layouter->addEdgeToBorder(destmod);
521
                } else {
522
                    layouter->addEdge(mod,destmod);
523
                }
524
            }
525
        }
526
    }
527
528
    // set up layouter environment (responsible for "Stop" button handling and visualizing the layouting process)
529
    Tcl_Interp *interp = getTkenv()->getInterp();
530
    TGraphLayouterEnvironment environment(interp, parentmodule, ds);
531
532
    std::string stopButton = std::string(windowName()) + ".toolbar.stop";
533
    bool isExpressMode = getTkenv()->getSimulationRunMode() == Tkenv::RUNMODE_EXPRESS;
534
    if (!isExpressMode)
535
        environment.setWidgetToGrab(stopButton.c_str());
536
537
    // enable visualizing only if full re-layouting (no cached coordinates in submodPosMap)
538
    // if (getTkenv()->opt_showlayouting)  // for debugging
539
    if (submodPosMap.empty() && getTkenv()->opt_showlayouting)
540
        environment.setCanvas(canvas);
541
542
    layouter->setEnvironment(&environment);
543
    layouter->execute();
544
    environment.cleanup();
545
546
    // fill the map with the results
547
    submodPosMap.clear();
548
    for (cModule::SubmoduleIterator it(parentmodule); !it.end(); it++)
549
    {
550
        cModule *submod = it();
551
552
        Point pos;
553
        layouter->getNodePosition(submod, pos.x, pos.y);
554
        submodPosMap[submod] = pos;
555
    }
556
557
    random_seed = layouter->getSeed();
558
559
    delete layouter;
560
}
561
562
// requires either recalculateLayout() or refreshLayout() called before!
563
void TGraphicalModWindow::redrawModules()
564
{
565
    cModule *parentmodule = static_cast<cModule *>(object);
566
    Tcl_Interp *interp = getTkenv()->getInterp();
567
568
    // then display all submodules
569
    CHK(Tcl_VarEval(interp, canvas, " delete dx",NULL)); // NOT "delete all" because that'd remove "bubbles" too!
570
    const cDisplayString blank;
571
    std::string buffer;
572
    const char *rawScaling = parentmodule->hasDisplayString() ? parentmodule->getDisplayString().getTagArg("bgs",0) : "";
573
    const char *scaling = substituteDisplayStringParamRefs(rawScaling, buffer, parentmodule, true);
574
575
    for (cModule::SubmoduleIterator it(parentmodule); !it.end(); it++)
576
    {
577
        cModule *submod = it();
578
        assert(submodPosMap.find(submod)!=submodPosMap.end());
579
        Point& pos = submodPosMap[submod];
580
        drawSubmodule(interp, submod, pos.x, pos.y, scaling);
581
    }
582
583
    // draw enclosing module
584
    drawEnclosingModule(interp, parentmodule, scaling);
585
586
    // loop through all submodules and enclosing module & draw their connections
587
    bool atparent = false;
588
    for (cModule::SubmoduleIterator it(parentmodule); !atparent; it++)
589
    {
590
        cModule *mod = !it.end() ? it() : (atparent=true,parentmodule);
591
592
        for (cModule::GateIterator i(mod); !i.end(); i++)
593
        {
594
            cGate *gate = i();
595
            if (gate->getType()==(atparent ? cGate::INPUT: cGate::OUTPUT) && gate->getNextGate()!=NULL)
596
            {
597
                drawConnection(interp, gate);
598
            }
599
        }
600
    }
601
    CHK(Tcl_VarEval(interp, canvas, " raise bubble",NULL));
602
    CHK(Tcl_VarEval(interp, "graphmodwin_setscrollregion ", windowname, " 0",NULL));
603
}
604
605
void TGraphicalModWindow::drawSubmodule(Tcl_Interp *interp, cModule *submod, int x, int y, const char *scaling)
606
{
607
    char coords[32];
608
    sprintf(coords,"%d %d ", x, y);
609
    const char *dispstr = submod->hasDisplayString() ? submod->getDisplayString().str() : "";
610
611
    CHK(Tcl_VarEval(interp, "draw_submod ",
612
                    canvas, " ",
613
                    ptrToStr(submod), " ",
614
                    coords,
615
                    "{", submod->getFullName(), "} ",
616
                    TclQuotedString(dispstr).get(), " ",
617
                    "{", scaling, "} ",
618
                    NULL));
619
}
620
621
void TGraphicalModWindow::drawEnclosingModule(Tcl_Interp *interp, cModule *parentmodule, const char *scaling)
622
{
623
    const char *dispstr = parentmodule->hasDisplayString() ? parentmodule->getDisplayString().str() : "";
624
    CHK(Tcl_VarEval(interp, "draw_enclosingmod ",
625
                       canvas, " ",
626
                       ptrToStr(parentmodule), " ",
627
                       "{", parentmodule->getFullPath().c_str(), "} ",
628
                       TclQuotedString(dispstr).get(), " ",
629
                       "{", scaling, "} ",
630
                       NULL ));
631
}
632
633
void TGraphicalModWindow::drawConnection(Tcl_Interp *interp, cGate *gate)
634
{
635
    cModule *mod = gate->getOwnerModule();
636
    cGate *dest_gate = gate->getNextGate();
637
638
    char gateptr[32], srcptr[32], destptr[32], chanptr[32], indices[32];
639
640
    // check if this is a two way connection (an other connection is pointing back
641
    // to the this gate's pair from the next gate's pair)
642
    bool twoWayConnection = false;
643
    // check if this gate is really part of an in/out gate pair
644
    // gate      o-------------------->o dest_gate
645
    // gate_pair o<--------------------o dest_gate_pair
646
    if (gate->getNameSuffix()[0]) {
647
      const cGate *gate_pair = mod->gateHalf(gate->getBaseName(),
648
                                        gate->getType() == cGate::INPUT ? cGate::OUTPUT : cGate::INPUT,
649
                                        gate->isVector() ? gate->getIndex() : -1);
650
651
      if (dest_gate->getNameSuffix()[0]) {
652
        const cGate *dest_gate_pair = dest_gate->getOwnerModule()->gateHalf(dest_gate->getBaseName(),
653
                                            dest_gate->getType() == cGate::INPUT ? cGate::OUTPUT : cGate::INPUT,
654
                                            dest_gate->isVector() ? dest_gate->getIndex() : -1);
655
          twoWayConnection = dest_gate_pair == gate_pair->getPreviousGate();
656
      }
657
    }
658
659
660
    ptrToStr(gate, gateptr);
661
    ptrToStr(mod, srcptr);
662
    ptrToStr(dest_gate->getOwnerModule(), destptr);
663
    sprintf(indices, "%d %d %d %d",
664
            gate->getIndex(), gate->size(),
665
            dest_gate->getIndex(), dest_gate->size());
666
    cChannel *chan = gate->getChannel();
667
    ptrToStr(chan, chanptr);
668
    const char *dispstr = (chan && chan->hasDisplayString()) ? chan->getDisplayString().str() : "";
669
670
    CHK(Tcl_VarEval(interp, "draw_connection ",
671
            canvas, " ",
672
            gateptr, " ",
673
            TclQuotedString(dispstr).get(), " ",
674
            srcptr, " ",
675
            destptr, " ",
676
            chanptr, " ",
677
            indices, " ",
678
            twoWayConnection ? "1" : "0",
679
            NULL
680
             ));
681
}
682
683
void TGraphicalModWindow::redrawMessages()
684
{
685
   Tcl_Interp *interp = getTkenv()->getInterp();
686
687
   // refresh & cleanup from prev. events
688
   CHK(Tcl_VarEval(interp, canvas, " delete msg msgname", NULL));
689
690
   // this thingy is only needed if animation is going on
691
   if (!getTkenv()->animating)
692
       return;
693
694
   // loop through all messages in the event queue and display them
695
   for (cMessageHeap::Iterator msg(simulation.msgQueue); !msg.end(); msg++)
696
   {
697
      char msgptr[32];
698
      ptrToStr(msg(),msgptr);
699
700
      cModule *arrivalmod = simulation.getModule( msg()->getArrivalModuleId() );
701
      if (arrivalmod &&
702
          arrivalmod->getParentModule()==static_cast<cModule *>(object) &&
703
          msg()->getArrivalGateId()>=0)
704
      {
705
         cGate *arrivalGate = msg()->getArrivalGate();
706
707
         // if arrivalgate is connected, msg arrived on a connection, otherwise via sendDirect()
708
         if (arrivalGate->getPreviousGate())
709
         {
710
             cGate *gate = arrivalGate->getPreviousGate();
711
             CHK(Tcl_VarEval(interp, "graphmodwin_draw_message_on_gate ",
712
                             canvas, " ",
713
                             ptrToStr(gate), " ",
714
                             msgptr,
715
                             NULL));
716
         }
717
         else
718
         {
719
             CHK(Tcl_VarEval(interp, "graphmodwin_draw_message_on_module ",
720
                             canvas, " ",
721
                             ptrToStr(arrivalmod), " ",
722
                             msgptr,
723
                             NULL));
724
         }
725
      }
726
   }
727
   CHK(Tcl_VarEval(interp, canvas, " raise bubble",NULL));
728
}
729
730
void TGraphicalModWindow::redrawNextEventMarker()
731
{
732
   Tcl_Interp *interp = getTkenv()->getInterp();
733
   cModule *mod = static_cast<cModule *>(object);
734
735
   // removing marker from previous event
736
   CHK(Tcl_VarEval(interp, canvas, " delete nexteventmarker", NULL));
737
738
   // this thingy is only needed if animation is going on
739
   if (!getTkenv()->animating || !getTkenv()->opt_nexteventmarkers)
740
       return;
741
742
   // if any parent of the module containing the next event is on this canvas, draw marker
743
   cModule *nextmod = simulation.guessNextModule();
744
   cModule *nextmodparent = nextmod;
745
   while (nextmodparent && nextmodparent->getParentModule()!=mod)
746
       nextmodparent = nextmodparent->getParentModule();
747
   if (nextmodparent)
748
   {
749
       CHK(Tcl_VarEval(interp, "graphmodwin_draw_nexteventmarker ",
750
                       canvas, " ",
751
                       ptrToStr(nextmodparent), " ",
752
                       (nextmod==nextmodparent ? "2" : "1"),
753
                       NULL));
754
   }
755
}
756
757
void TGraphicalModWindow::updateSubmodules()
758
{
759
   Tcl_Interp *interp = getTkenv()->getInterp();
760
   for (cModule::SubmoduleIterator submod(static_cast<cModule *>(object)); !submod.end(); submod++)
761
   {
762
       CHK(Tcl_VarEval(interp, "graphmodwin_update_submod ",
763
                       canvas, " ",
764
                       ptrToStr(submod()),
765
                       NULL));
766
   }
767
}
768
769
770
void TGraphicalModWindow::submoduleCreated(cModule *newmodule)
771
{
772
   needs_redraw = true;
773
}
774
775
void TGraphicalModWindow::submoduleDeleted(cModule *module)
776
{
777
   needs_redraw = true;
778
}
779
780
void TGraphicalModWindow::connectionCreated(cGate *srcgate)
781
{
782
   needs_redraw = true;
783
}
784
785
void TGraphicalModWindow::connectionDeleted(cGate *srcgate)
786
{
787
   needs_redraw = true;
788
}
789
790
void TGraphicalModWindow::displayStringChanged(cModule *)
791
{
792
   needs_redraw = true;
793
}
794
795
void TGraphicalModWindow::displayStringChanged()
796
{
797
   needs_redraw = true; //TODO check, probably only non-background tags have changed...
798
}
799
800
void TGraphicalModWindow::displayStringChanged(cGate *)
801
{
802
   needs_redraw = true;
803
}
804
805
void TGraphicalModWindow::bubble(cModule *submod, const char *text)
806
{
807
    Tcl_Interp *interp = getTkenv()->getInterp();
808
809
    // if submod position is not yet known (because e.g. we're in fast mode
810
    // and it was dynamically created since the last update), refresh layout
811
    // so that we can get coordinates for it
812
    if (submodPosMap.find(submod)==submodPosMap.end())
813
        refreshLayout();
814
815
    cModule *parentmodule = static_cast<cModule *>(object);
816
    std::string buffer;
817
    const char *rawScaling = parentmodule->hasDisplayString() ? parentmodule->getDisplayString().getTagArg("bgs",0) : "";
818
    const char *scaling = substituteDisplayStringParamRefs(rawScaling, buffer, parentmodule, true);
819
820
    // invoke Tcl code to display bubble
821
    char coords[32];
822
    Point& pos = submodPosMap[submod];
823
    sprintf(coords, " %d %d ", pos.x, pos.y);
824
    CHK(Tcl_VarEval(interp, "graphmodwin_bubble ", canvas, coords, " ", TclQuotedString(scaling).get(), " ", TclQuotedString(text).get(), NULL));
825
}
826
827
int TGraphicalModWindow::inspectorCommand(Tcl_Interp *interp, int argc, const char **argv)
828
{
829
   if (argc<1) {Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR;}
830
831
   // supported commands:
832
   //   arrowcoords, relayout, etc...
833
834
   if (strcmp(argv[0],"arrowcoords")==0)
835
   {
836
      return ::arrowcoords(interp,argc,argv);
837
   }
838
   else if (strcmp(argv[0],"relayout")==0)
839
   {
840
      TRY(relayoutAndRedrawAll());
841
      return TCL_OK;
842
   }
843
   else if (strcmp(argv[0],"redraw")==0)
844
   {
845
      TRY(redrawAll());
846
      return TCL_OK;
847
   }
848
   else if (strcmp(argv[0],"submodulecount")==0)
849
   {
850
      return getSubmoduleCount(interp,argc,argv);
851
   }
852
   else if (strcmp(argv[0],"getsubmodq")==0)
853
   {
854
      return getSubmodQ(interp,argc,argv);
855
   }
856
   else if (strcmp(argv[0],"getsubmodqlen")==0)
857
   {
858
      return getSubmodQLen(interp,argc,argv);
859
   }
860
   return TCL_ERROR;
861
}
862
863
int TGraphicalModWindow::getSubmoduleCount(Tcl_Interp *interp, int argc, const char **argv)
864
{
865
   int count = 0;
866
   for (cModule::SubmoduleIterator submod(static_cast<cModule *>(object)); !submod.end(); submod++)
867
       count++;
868
   char buf[20];
869
   sprintf(buf, "%d", count);
870
   Tcl_SetResult(interp, buf, TCL_VOLATILE);
871
   return TCL_OK;
872
}
873
874
int TGraphicalModWindow::getSubmodQ(Tcl_Interp *interp, int argc, const char **argv)
875
{
876
   // args: <module ptr> <qname>
877
   if (argc!=3) {Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR;}
878
879
   cModule *mod = dynamic_cast<cModule *>(strToPtr( argv[1] ));
880
   const char *qname = argv[2];
881
   cQueue *q = dynamic_cast<cQueue *>(mod->findObject(qname));
882
   char buf[21];
883
   ptrToStr(q,buf);
884
   Tcl_SetResult(interp, buf, TCL_VOLATILE);
885
   return TCL_OK;
886
}
887
888
int TGraphicalModWindow::getSubmodQLen(Tcl_Interp *interp, int argc, const char **argv)
889
{
890
   // args: <module ptr> <qname>
891
   if (argc!=3) {Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR;}
892
893
   cModule *mod = dynamic_cast<cModule *>(strToPtr( argv[1] ));
894
   const char *qname = argv[2];
895
   cQueue *q = dynamic_cast<cQueue *>(mod->findObject(qname)); //FIXME THIS MUST BE REFINED! SEARCHES WAY TOO DEEEEEP!!!!
896
   if (!q) {Tcl_SetResult(interp, TCLCONST(""), TCL_STATIC); return TCL_OK;}
897
898
   char buf[20];
899
   sprintf(buf, "%d", q->length());
900
   Tcl_SetResult(interp, buf, TCL_VOLATILE);
901
   return TCL_OK;
902
}
903
904
905
//=======================================================================
906
907
//
908
// class TCompoundModInspectorFactory : public cInspectorFactory
909
// {
910
//   public:
911
//     TCompoundModInspectorFactory(const char *name) : cInspectorFactory(name) {}
912
//
913
//     bool supportsObject(cObject *obj) {return dynamic_cast<cModule *>(obj)!=NULL;}
914
//     int inspectorType() {return INSP_OBJECT;}
915
//     double qualityAsDefault(cObject *object) {return 2.9;}
916
//
917
//     TInspector *createInspectorFor(cObject *object,int type,const char *geom,void *data) {
918
//         return new TCompoundModInspector(object, type, geom, data);
919
//     }
920
// };
921
//
922
// Register_InspectorFactory(TCompoundModInspectorFactory);
923
//
924
//
925
// TCompoundModInspector::TCompoundModInspector(cObject *obj,int typ,const char *geom,void *dat) :
926
//     TInspector(obj,typ,geom,dat)
927
// {
928
// }
929
//
930
// void TCompoundModInspector::createWindow()
931
// {
932
//    TInspector::createWindow(); // create window name etc.
933
//
934
//    // create inspector window by calling the specified proc with
935
//    // the object's pointer. Window name will be like ".ptr80003a9d-1"
936
//    Tcl_Interp *interp = getTkenv()->getInterp();
937
//    CHK(Tcl_VarEval(interp, "create_compoundmodinspector ", windowname, " \"", geometry, "\"", NULL ));
938
// }
939
//
940
// void TCompoundModInspector::update()
941
// {
942
//    TInspector::update();
943
//
944
//    cCompoundModule *mod = static_cast<cCompoundModule *>(object);
945
//
946
//    //setToolbarInspectButton(".toolbar.parent", mod->getParentModule(),INSP_DEFAULT);
947
//
948
//    setEntry(".nb.info.name.e", mod->getName());
949
//    char id[16]; sprintf(id,"%ld", (long)mod->getId());
950
//    setLabel(".nb.info.id.e", id);
951
//    setEntry(".nb.info.dispstr.e", mod->getDisplayString());
952
//    setEntry(".nb.info.dispstrpt.e", mod->backgroundDisplayString());
953
//
954
//    deleteInspectorListbox(".nb.contents");
955
//    fillInspectorListbox(".nb.contents", mod, false);
956
// }
957
//
958
// void TCompoundModInspector::writeBack()
959
// {
960
//    cCompoundModule *mod = static_cast<cCompoundModule *>(object);
961
//    mod->setName(getEntry(".nb.info.name.e"));
962
//    mod->getDisplayString().parse(getEntry(".nb.info.dispstr.e"));
963
//    mod->backgroundDisplayString().parse(getEntry(".nb.info.dispstrpt.e"));
964
//
965
//    TInspector::writeBack();    // must be there after all changes
966
// }
967
//
968
969
//=======================================================================
970
971
//
972
// class TSimpleModInspectorFactory : public cInspectorFactory
973
// {
974
//   public:
975
//     TSimpleModInspectorFactory(const char *name) : cInspectorFactory(name) {}
976
//
977
//     bool supportsObject(cObject *obj) {return dynamic_cast<cSimpleModule *>(obj)!=NULL;}
978
//     int inspectorType() {return INSP_OBJECT;}
979
//     double qualityAsDefault(cObject *object) {return 4.0;}
980
//
981
//     TInspector *createInspectorFor(cObject *object,int type,const char *geom,void *data) {
982
//         return new TSimpleModInspector(object, type, geom, data);
983
//     }
984
// };
985
//
986
// Register_InspectorFactory(TSimpleModInspectorFactory);
987
//
988
//
989
// TSimpleModInspector::TSimpleModInspector(cObject *obj,int typ,const char *geom,void *dat) :
990
//     TInspector(obj,typ,geom,dat)
991
// {
992
// }
993
//
994
// void TSimpleModInspector::createWindow()
995
// {
996
//    TInspector::createWindow(); // create window name etc.
997
//
998
//    // create inspector window by calling the specified proc with
999
//    // the object's pointer. Window name will be like ".ptr80003a9d-1"
1000
//    Tcl_Interp *interp = getTkenv()->getInterp();
1001
//    CHK(Tcl_VarEval(interp, "create_simplemodinspector ", windowname, " \"", geometry, "\"", NULL ));
1002
// }
1003
//
1004
// void TSimpleModInspector::update()
1005
// {
1006
//    TInspector::update();
1007
//
1008
//    cSimpleModule *mod = static_cast<cSimpleModule *>(object);
1009
//
1010
//    char buf[40];
1011
//    setEntry(".nb.info.name.e", mod->getName());
1012
//    sprintf(buf,"%ld", (long)mod->getId());
1013
//    setLabel(".nb.info.id.e", buf);
1014
//    setEntry(".nb.info.dispstr.e", mod->getDisplayString());
1015
//    setEntry(".nb.info.dispstrpt.e", mod->backgroundDisplayString());
1016
//    setLabel(".nb.info.state.e",  modstate[ mod->moduleState() ]  );
1017
//    if (mod->usesActivity())
1018
//    {
1019
//       unsigned stk = mod->getStackSize();
1020
//       unsigned extra = ev.getExtraStackForEnvir();
1021
//       unsigned used = mod->getStackUsage();
1022
//       sprintf(buf,"%u + %u = %u bytes", stk-extra, extra, stk);
1023
//       setLabel(".nb.info.stacksize.e", buf );
1024
//       sprintf(buf,"approx. %u bytes", used);
1025
//       setLabel(".nb.info.stackused.e", buf );
1026
//    }
1027
//    else
1028
//    {
1029
//       setLabel(".nb.info.stacksize.e", "n/a" );
1030
//       setLabel(".nb.info.stackused.e", "n/a" );
1031
//    }
1032
//
1033
//    deleteInspectorListbox(".nb.contents");
1034
//    fillInspectorListbox(".nb.contents", mod, false);
1035
// }
1036
//
1037
// void TSimpleModInspector::writeBack()
1038
// {
1039
//    cSimpleModule *mod = static_cast<cSimpleModule *>(object);
1040
//    mod->setName(getEntry(".nb.info.name.e"));
1041
//    mod->getDisplayString().parse(getEntry(".nb.info.dispstr.e"));
1042
//    mod->backgroundDisplayString().parse(getEntry(".nb.info.dispstrpt.e"));
1043
//
1044
//    TInspector::writeBack();    // must be there after all changes
1045
// }
1046
//
1047
1048
//=======================================================================
1049
1050
//
1051
// class TGateInspectorFactory : public cInspectorFactory
1052
// {
1053
//   public:
1054
//     TGateInspectorFactory(const char *name) : cInspectorFactory(name) {}
1055
//
1056
//     bool supportsObject(cObject *obj) {return dynamic_cast<cGate *>(obj)!=NULL;}
1057
//     int inspectorType() {return INSP_OBJECT;}
1058
//     double qualityAsDefault(cObject *object) {return 2.9;}
1059
//
1060
//     TInspector *createInspectorFor(cObject *object,int type,const char *geom,void *data) {
1061
//         return new TGateInspector(object, type, geom, data);
1062
//     }
1063
// };
1064
//
1065
// Register_InspectorFactory(TGateInspectorFactory);
1066
//
1067
// TGateInspector::TGateInspector(cObject *obj,int typ,const char *geom,void *dat) :
1068
//     TInspector(obj,typ,geom,dat)
1069
// {
1070
// }
1071
//
1072
// void TGateInspector::createWindow()
1073
// {
1074
//    TInspector::createWindow(); // create window name etc.
1075
//
1076
//    // create inspector window by calling the specified proc with
1077
//    // the object's pointer. Window name will be like ".ptr80003a9d-1"
1078
//    Tcl_Interp *interp = getTkenv()->getInterp();
1079
//    CHK(Tcl_VarEval(interp, "create_gateinspector ", windowname, " \"", geometry, "\"", NULL ));
1080
// }
1081
//
1082
// void TGateInspector::update()
1083
// {
1084
//    TInspector::update();
1085
//
1086
//    cGate *g = static_cast<cGate *>(object);
1087
//
1088
//    setEntry(".nb.info.name.e", g->getName());
1089
//    char buf[64];
1090
//    sprintf(buf,"#%d", g->getId());
1091
//    setLabel(".nb.info.id.e", buf);
1092
//    setEntry(".nb.info.dispstr.e", g->getDisplayString().str());
1093
//    cDatarateChannel *ch = dynamic_cast<cDatarateChannel*>(g->getChannel());
1094
//    if (ch)
1095
//    {
1096
//        setEntry(".nb.info.delay.e", ch->getDelay());
1097
//        setEntry(".nb.info.error.e", ch->getError());
1098
//        setEntry(".nb.info.datarate.e", ch->getDatarate());
1099
//    }
1100
//    else
1101
//    {
1102
//        setEntry(".nb.info.delay.e", 0.0);
1103
//        setEntry(".nb.info.error.e", 0.0);
1104
//        setEntry(".nb.info.datarate.e", 0.0);
1105
//    }
1106
//    setLabel(".nb.info.trfinish.e", g->getTransmissionFinishTime());
1107
//
1108
//    setInspectButton(".nb.info.from", g->getPreviousGate(), true, INSP_DEFAULT);
1109
//    setInspectButton(".nb.info.to", g->getNextGate(), true, INSP_DEFAULT);
1110
// }
1111
//
1112
// void TGateInspector::writeBack()
1113
// {
1114
//    cGate *g = static_cast<cGate *>(object);
1115
//    g->setName(getEntry(".nb.info.name.e"));
1116
//    g->getDisplayString().parse(getEntry(".nb.info.dispstr.e"));
1117
//    cDatarateChannel *ch = dynamic_cast<cDatarateChannel*>(g->getChannel());
1118
//    double delay = atof(getEntry(".nb.info.delay.e"));
1119
//    double error = atof(getEntry(".nb.info.error.e"));
1120
//    double datarate = atof(getEntry(".nb.info.datarate.e"));
1121
//    if (delay!=0 || error!=0 || datarate!=0 || ch!=NULL)
1122
//    {
1123
//        if (!ch)
1124
//        {
1125
//            ch = new cDatarateChannel("channel");
1126
//            g->setChannel(ch);
1127
//        }
1128
//        ch->setDelay(delay<0 ? 0 : delay);
1129
//        ch->setError(error<0 ? 0 : error>1 ? 1 : error);
1130
//        ch->setDatarate(datarate<0 ? 0 : datarate);
1131
//    }
1132
//
1133
//    TInspector::writeBack();    // must be there after all changes
1134
// }
1135
//
1136
1137
//=======================================================================
1138
class TGraphicalGateWindowFactory : public cInspectorFactory
1139
{
1140
  public:
1141
    TGraphicalGateWindowFactory(const char *name) : cInspectorFactory(name) {}
1142
1143
    bool supportsObject(cObject *obj) {return dynamic_cast<cGate *>(obj)!=NULL;}
1144
    int inspectorType() {return INSP_GRAPHICAL;}
1145
    double qualityAsDefault(cObject *object) {return 3.0;}
1146
1147
    TInspector *createInspectorFor(cObject *object,int type,const char *geom,void *data) {
1148
        return new TGraphicalGateWindow(object, type, geom, data);
1149
    }
1150
};
1151
1152
Register_InspectorFactory(TGraphicalGateWindowFactory);
1153
1154
1155
TGraphicalGateWindow::TGraphicalGateWindow(cObject *obj,int typ,const char *geom,void *dat) :
1156
    TInspector(obj,typ,geom,dat)
1157
{
1158
}
1159
1160
void TGraphicalGateWindow::createWindow()
1161
{
1162
   TInspector::createWindow(); // create window name etc.
1163
   strcpy(canvas,windowname); strcat(canvas,".c");
1164
1165
   // create inspector window by calling the specified proc with
1166
   // the object's pointer. Window name will be like ".ptr80003a9d-1"
1167
   Tcl_Interp *interp = getTkenv()->getInterp();
1168
   CHK(Tcl_VarEval(interp, "create_graphicalgatewindow ", windowname, " \"", geometry, "\"", NULL ));
1169
}
1170
1171
int TGraphicalGateWindow::redraw(Tcl_Interp *interp, int, const char **)
1172
{
1173
   cGate *gate = (cGate *)object;
1174
1175
   CHK(Tcl_VarEval(interp, canvas, " delete all",NULL));
1176
1177
   // draw modules
1178
   int k = 0;
1179
   int xsiz = 0;
1180
   char prevdir = ' ';
1181
   cGate *g;
1182
   for (g = gate->getPathStartGate(); g!=NULL; g=g->getNextGate(),k++)
1183
   {
1184
        if (g->getType()==prevdir)
1185
             xsiz += (g->getType()==cGate::OUTPUT) ? 1 : -1;
1186
        else
1187
             prevdir = g->getType();
1188
1189
        char modptr[32], gateptr[32], kstr[16], xstr[16], dir[2];
1190
        ptrToStr(g->getOwnerModule(),modptr);
1191
        ptrToStr(g,gateptr);
1192
        sprintf(kstr,"%d",k);
1193
        sprintf(xstr,"%d",xsiz);
1194
        dir[0] = g->getType(); dir[1]=0;
1195
        CHK(Tcl_VarEval(interp, "draw_module_gate ",
1196
                      canvas, " ",
1197
                      modptr, " ",
1198
                      gateptr, " ",
1199
                      "{",g->getOwnerModule()->getFullPath().c_str(), "} ",
1200
                      "{",g->getFullName(), "} ",
1201
                      kstr," ",
1202
                      xstr," ",
1203
                      dir, " ",
1204
                      g==gate?"1":"0",
1205
                      NULL ));
1206
   }
1207
1208
   // draw connections
1209
   for (g = gate->getPathStartGate(); g->getNextGate()!=NULL; g=g->getNextGate())
1210
   {
1211
        char srcgateptr[32], destgateptr[32], chanptr[32];
1212
        ptrToStr(g,srcgateptr);
1213
        ptrToStr(g->getNextGate(),destgateptr);
1214
        cChannel *chan = g->getChannel();
1215
        ptrToStr(chan,chanptr);
1216
        const char *dispstr = (chan && chan->hasDisplayString()) ? chan->getDisplayString().str() : "";
1217
        CHK(Tcl_VarEval(interp, "draw_conn ",
1218
                      canvas, " ",
1219
                      srcgateptr, " ",
1220
                      destgateptr, " ",
1221
                      chanptr, " ",
1222
                      TclQuotedString(chan?chan->info().c_str():"").get(), " ",
1223
                      TclQuotedString(dispstr).get(), " ",
1224
                      NULL ));
1225
   }
1226
1227
   // loop through all messages in the event queue
1228
   update();
1229
1230
   return TCL_OK;
1231
}
1232
1233
void TGraphicalGateWindow::update()
1234
{
1235
   TInspector::update();
1236
1237
   Tcl_Interp *interp = getTkenv()->getInterp();
1238
   cGate *gate = static_cast<cGate *>(object);
1239
1240
   // redraw modules only on explicit request
1241
1242
   // loop through all messages in the event queue
1243
   CHK(Tcl_VarEval(interp, canvas, " delete msg msgname", NULL));
1244
   cGate *destgate = gate->getPathEndGate();
1245
   for (cMessageHeap::Iterator msg(simulation.msgQueue); !msg.end(); msg++)
1246
   {
1247
      char gateptr[32], msgptr[32];
1248
      ptrToStr(msg(),msgptr);
1249
1250
      if (msg()->getArrivalGate()== destgate)
1251
      {
1252
         cGate *gate = msg()->getArrivalGate();
1253
         if (gate) gate = gate->getPreviousGate();
1254
         if (gate)
1255
         {
1256
             CHK(Tcl_VarEval(interp, "graphmodwin_draw_message_on_gate ",
1257
                             canvas, " ",
1258
                             ptrToStr(gate,gateptr), " ",
1259
                             msgptr,
1260
                             NULL));
1261
         }
1262
      }
1263
   }
1264
}
1265
1266
int TGraphicalGateWindow::inspectorCommand(Tcl_Interp *interp, int argc, const char **argv)
1267
{
1268
   if (argc<1) {Tcl_SetResult(interp, TCLCONST("wrong number of args"), TCL_STATIC); return TCL_ERROR;}
1269
1270
   // supported commands:
1271
   //   redraw
1272
1273
   if (strcmp(argv[0],"redraw")==0)
1274
   {
1275
      return redraw(interp,argc,argv);
1276
   }
1277
1278
   Tcl_SetResult(interp, TCLCONST("invalid arg: must be 'redraw'"), TCL_STATIC);
1279
   return TCL_ERROR;
1280
}
1281
1282
void TGraphicalGateWindow::displayStringChanged(cGate *gate)
1283
{
1284
   //XXX should defer redraw (via redraw_needed) to avoid "flickering"
1285
}
1286
1287