Statistics
| Branch: | Revision:

root / src / tkenv / tkenv.cc @ e1750c09

History | View | Annotate | Download (62.7 KB)

1
//==========================================================================
2
//  TKENV.CC - part of
3
//
4
//                     OMNeT++/OMNEST
5
//            Discrete System Simulation in C++
6
//
7
//  contains:  Tkenv member functions
8
//
9
//==========================================================================
10

    
11
/*--------------------------------------------------------------*
12
  Copyright (C) 1992-2008 Andras Varga
13
  Copyright (C) 2006-2008 OpenSim Ltd.
14

15
  This file is distributed WITHOUT ANY WARRANTY. See the file
16
  `license' for details on this and other legal matters.
17
*--------------------------------------------------------------*/
18

    
19
#include <assert.h>
20
#include <string.h>
21
#include <stdarg.h>
22
#include <stdlib.h>
23
#include <signal.h>
24
#include <stdio.h>
25
#include <string>
26

    
27
#include "appreg.h"
28
#include "csimplemodule.h"
29
#include "cmessage.h"
30
#include "speedometer.h"
31
#include "cscheduler.h"
32
#include "ccomponenttype.h"
33
#include "csimulation.h"
34
#include "cconfigoption.h"
35
#include "regmacros.h"
36
#include "cproperties.h"
37
#include "cproperty.h"
38

    
39
#include "tkdefs.h"
40
#include "tkenv.h"
41
#include "tklib.h"
42
#include "inspector.h"
43
#include "inspfactory.h"
44
#include "modinsp.h"
45
#include "timeutil.h"
46
#include "stringutil.h"
47
#include "../common/ver.h"
48
#include "platdep/platmisc.h"  // va_copy
49

    
50

    
51
// default plugin path -- allow overriding it via compiler option (-D)
52
// (default image path comes from makefile)
53
#ifndef OMNETPP_PLUGIN_PATH
54
#define OMNETPP_PLUGIN_PATH "./plugins"
55
#endif
56

    
57
#ifdef __APPLE__
58
void OSXTransformProcess();
59
#endif
60

    
61

    
62
USING_NAMESPACE
63

    
64
//
65
// Register the Tkenv user interface
66
//
67
Register_OmnetApp("Tkenv", Tkenv, 20, "graphical user interface");
68

    
69
//
70
// The following function can be used to force linking with Tkenv; specify
71
// -u _tkenv_lib (gcc) or /include:_tkenv_lib (vc++) in the link command.
72
//
73
extern "C" TKENV_API void tkenv_lib() {}
74
// on some compilers (e.g. linux gcc 4.2) the functions are generated without _
75
extern "C" TKENV_API void _tkenv_lib() {}
76

    
77
#define LL  INT64_PRINTF_FORMAT
78

    
79
// widgets in the Tk user interface
80
#define NETWORK_LABEL         ".statusbar.networklabel"
81
#define EVENT_LABEL           ".statusbar.eventlabel"
82
#define TIME_LABEL            ".statusbar.timelabel"
83
#define NEXT_LABEL            ".statusbar.nextlabel"
84

    
85
#define FESLENGTH_LABEL       ".statusbar2.feslength"
86
#define TOTALMSGS_LABEL       ".statusbar2.totalmsgs"
87
#define LIVEMSGS_LABEL        ".statusbar2.livemsgs"
88

    
89
#define SIMSECPERSEC_LABEL    ".statusbar3.simsecpersec"
90
#define EVENTSPERSEC_LABEL    ".statusbar3.eventspersec"
91
#define EVENTSPERSIMSEC_LABEL ".statusbar3.eventspersimsec"
92

    
93

    
94
#define SPEEDOMETER_UPDATEMILLISECS 1000
95

    
96

    
97
Register_GlobalConfigOptionU(CFGID_TKENV_EXTRA_STACK, "tkenv-extra-stack", "B", "48KB", "Specifies the extra amount of stack that is reserved for each activity() simple module when the simulation is run under Tkenv.");
98
Register_GlobalConfigOption(CFGID_TKENV_DEFAULT_CONFIG, "tkenv-default-config", CFG_STRING, NULL, "Specifies which config Tkenv should set up automatically on startup. The default is to ask the user.");
99
Register_GlobalConfigOption(CFGID_TKENV_DEFAULT_RUN, "tkenv-default-run", CFG_INT, "0", "Specifies which run (of the default config, see tkenv-default-config) Tkenv should set up automatically on startup. The default is to ask the user.");
100
Register_GlobalConfigOption(CFGID_TKENV_IMAGE_PATH, "tkenv-image-path", CFG_PATH, "", "Specifies the path for loading module icons.");
101
Register_GlobalConfigOption(CFGID_TKENV_PLUGIN_PATH, "tkenv-plugin-path", CFG_PATH, "", "Specifies the search path for Tkenv plugins. Tkenv plugins are .tcl files that get evaluated on startup.");
102

    
103

    
104
// utility function
105
static bool moduleContains(cModule *potentialparent, cModule *mod)
106
{
107
   while (mod)
108
   {
109
       if (mod==potentialparent)
110
           return true;
111
       mod = mod->getParentModule();
112
   }
113
   return false;
114
}
115

    
116

    
117
Tkenv::Tkenv()
118
{
119
    // Note: ctor should only contain trivial initializations, because
120
    // the class may be instantiated only for the purpose of calling
121
    // printUISpecificHelp() on it
122

    
123
    interp = NULL;  // Tcl/Tk not set up yet
124
    simstate = SIM_NONET;
125
    stopsimulation_flag = false;
126
    animating = false;
127
    hasmessagewindow = false;
128
    isconfigrun = false;
129
    rununtil_msg = NULL; // deactivate corresponding checks in eventCancelled()/objectDeleted()
130
    gettimeofday(&idleLastUICheck, NULL);
131

    
132
    // set the name here, to prevent warning from cStringPool on shutdown when Cmdenv runs
133
    inspectorfactories.getInstance()->setName("inspectorfactories");
134

    
135
    // initialize .tkenvrc config variables
136
    opt_stepdelay = 300;
137
    opt_updatefreq_fast = 500;
138
    opt_updatefreq_express = 1000;
139
    opt_animation_enabled = true;
140
    opt_nexteventmarkers = true;
141
    opt_senddirect_arrows = true;
142
    opt_anim_methodcalls = true;
143
    opt_methodcalls_delay = 200;
144
    opt_animation_msgnames = true;
145
    opt_animation_msgclassnames = true;
146
    opt_animation_msgcolors = true;
147
    opt_penguin_mode = false;
148
    opt_showlayouting = false;
149
    opt_layouterchoice = LAYOUTER_AUTO;
150
    opt_arrangevectorconnections = false;
151
    opt_iconminsize = 5;
152
    opt_bubbles = true;
153
    opt_animation_speed = 1.5;
154
    opt_event_banners = true;
155
    opt_init_banners = true;
156
    opt_short_banners = false;
157
    opt_use_mainwindow = true;
158
    opt_expressmode_autoupdate = true;
159
    opt_stoponmsgcancel = true;
160
}
161

    
162
Tkenv::~Tkenv()
163
{
164
}
165

    
166
static void signalHandler(int signum)
167
{
168
   cStaticFlag::setExiting();
169
   exit(2);
170
}
171

    
172
void Tkenv::run()
173
{
174
    //
175
    // SETUP
176
    //
177
    try
178
    {
179
        // set signal handler
180
        signal(SIGINT, signalHandler);
181
        signal(SIGTERM, signalHandler);
182

    
183
#ifdef __APPLE__
184
        OSXTransformProcess();
185
#endif
186
        // path for the Tcl user interface files
187
#ifdef OMNETPP_TKENV_DIR
188
        tkenv_dir = getenv("OMNETPP_TKENV_DIR");
189
        if (tkenv_dir.empty())
190
            tkenv_dir = OMNETPP_TKENV_DIR;
191
#endif
192

    
193
        // path for icon directories
194
        const char *image_path_env = getenv("OMNETPP_IMAGE_PATH");
195
        if (image_path_env==NULL && getenv("OMNETPP_BITMAP_PATH")!=NULL)
196
            fprintf(stderr, "\n<!> WARNING: Obsolete environment variable OMNETPP_BITMAP_PATH found -- "
197
                            "please change it to OMNETPP_IMAGE_PATH for " OMNETPP_PRODUCT " 4.0\n");
198
        std::string image_path = opp_isempty(image_path_env) ? OMNETPP_IMAGE_PATH : image_path_env;
199
        if (!opt_image_path.empty())
200
            image_path = std::string(opt_image_path.c_str()) + ";" + image_path;
201

    
202
        // path for plugins
203
        const char *plugin_path_env = getenv("OMNETPP_PLUGIN_PATH");
204
        std::string plugin_path = plugin_path_env ? plugin_path_env : OMNETPP_PLUGIN_PATH;
205
        if (!opt_plugin_path.empty())
206
            plugin_path = std::string(opt_plugin_path.c_str()) + ";" + plugin_path;
207

    
208
        // set up Tcl/Tk
209
        interp = initTk(args->getArgCount(), args->getArgVector());
210
        if (!interp)
211
            throw opp_runtime_error("Tkenv: cannot create Tcl interpreter");
212

    
213
        // add OMNeT++'s commands to Tcl
214
        createTkCommands(interp, tcl_commands);
215

    
216
        Tcl_SetVar(interp, "OMNETPP_IMAGE_PATH", TCLCONST(image_path.c_str()), TCL_GLOBAL_ONLY);
217
        Tcl_SetVar(interp, "OMNETPP_PLUGIN_PATH", TCLCONST(plugin_path.c_str()), TCL_GLOBAL_ONLY);
218

    
219
        Tcl_SetVar(interp, "OMNETPP_RELEASE", OMNETPP_RELEASE, TCL_GLOBAL_ONLY);
220
        Tcl_SetVar(interp, "OMNETPP_EDITION", OMNETPP_EDITION, TCL_GLOBAL_ONLY);
221
        Tcl_SetVar(interp, "OMNETPP_BUILDID", OMNETPP_BUILDID, TCL_GLOBAL_ONLY);
222

    
223
        // eval Tcl sources: either from .tcl files or from compiled-in string
224
        // literal (tclcode.cc)...
225

    
226
#ifdef OMNETPP_TKENV_DIR
227
        //
228
        // Case A: TCL code in separate .tcl files
229
        //
230
        Tcl_SetVar(interp, "OMNETPP_TKENV_DIR",  TCLCONST(tkenv_dir.c_str()), TCL_GLOBAL_ONLY);
231
        if (Tcl_EvalFile(interp,opp_concat(tkenv_dir.c_str(),"/tkenv.tcl"))==TCL_ERROR)
232
            throw opp_runtime_error("Tkenv: %s. (Is the OMNETPP_TKENV_DIR environment variable "
233
                                    "set correctly? When not set, it defaults to " OMNETPP_TKENV_DIR ")",
234
                                    Tcl_GetStringResult(interp));
235
#else
236
        //
237
        // Case B: compiled-in TCL code
238
        //
239
        // The tclcode.cc file is generated from the Tcl scripts
240
        // with the tcl2c program (to be compiled from tcl2c.c).
241
        //
242
#include "tclcode.cc"
243
        if (Tcl_Eval(interp,(char *)tcl_code)==TCL_ERROR)
244
            throw opp_runtime_error("Tkenv: %s", Tcl_GetStringResult(interp));
245
#endif
246

    
247
        // evaluate main script and build user interface
248
        if (Tcl_Eval(interp,"start_tkenv")==TCL_ERROR)
249
            throw opp_runtime_error("Tkenv: %s\n", Tcl_GetStringResult(interp));
250

    
251
        // create windowtitle prefix
252
        if (getParsimNumPartitions()>0)
253
        {
254
            windowtitleprefix.reserve(24);
255
            sprintf(windowtitleprefix.buffer(), "Proc %d/%d - ", getParsimProcId(), getParsimNumPartitions());
256
        }
257
    }
258
    catch (std::exception& e)
259
    {
260
        interp = NULL;
261
        throw;
262
    }
263

    
264
    //
265
    // RUN
266
    //
267
    CHK(Tcl_Eval(interp,"startup_commands"));
268
    runTk(interp);
269

    
270
    //
271
    // SHUTDOWN
272
    //
273

    
274
    // close all inspectors before exiting
275
    for(;;)
276
    {
277
        TInspectorList::iterator it = inspectors.begin();
278
        if (it==inspectors.end())
279
            break;
280
        TInspector *insp = *it;
281
        inspectors.erase(it);
282
        delete insp;
283
    }
284

    
285
    // delete network if not yet done
286
    simulation.deleteNetwork();
287

    
288
    // pull down inspector factories
289
    inspectorfactories.clear();
290
}
291

    
292
void Tkenv::printUISpecificHelp()
293
{
294
    ev << "Tkenv-specific options:\n";
295
    ev << "  -c <configname>\n";
296
    ev << "                Select a given configuration for execution. With inifile-based\n";
297
    ev << "                configuration database, this selects the [Config <configname>]\n";
298
    ev << "                section; the default is the [General] section.\n";
299
    ev << "                See also: -r.\n";
300
    ev << "  -r <run>      Set up the specified run number in the configuration selected with\n";
301
    ev << "                the -c option\n";
302
}
303

    
304
void Tkenv::rebuildSim()
305
{
306
    if (isconfigrun)
307
         newRun(std::string(getConfigEx()->getActiveConfigName()).c_str(), getConfigEx()->getActiveRunNumber());
308
    else if (simulation.getNetworkType()!=NULL)
309
         newNetwork(simulation.getNetworkType()->getName());
310
    else
311
         confirm("Choose File|New Network or File|New Run.");
312
    if (simulation.getSystemModule())
313
         inspect(simulation.getSystemModule(),INSP_DEFAULT,"",NULL);
314
}
315

    
316
void Tkenv::doOneStep()
317
{
318
    ASSERT(simstate==SIM_NEW || simstate==SIM_READY);
319

    
320
    clearNextModuleDisplay();
321
    clearPerformanceDisplay();
322
    updateSimtimeDisplay();
323

    
324
    animating = true;
325

    
326
    rununtil_msg = NULL; // deactivate corresponding checks in eventCancelled()/objectDeleted()
327

    
328
    simstate = SIM_RUNNING;
329
    startClock();
330
    simulation.getScheduler()->executionResumed();
331
    try
332
    {
333
        cMessage* msg = simulation.getScheduler()->getNextEvent();
334
        cSimpleModule* mod = (cSimpleModule*) simulation.getModule(msg->getArrivalModuleId());
335

    
336
        if (mod)  // selectNextModule() not interrupted
337
        {
338
            if (opt_event_banners)
339
               printEventBanner(simulation.msgQueue.peekFirst(), mod);
340
            simulation.doOneEvent(msg);
341
            performAnimations();
342
        }
343
        updateSimtimeDisplay();
344
        updateNextModuleDisplay();
345
        updateInspectors();
346
        simstate = SIM_READY;
347
        outvectormgr->flush();
348
        outscalarmgr->flush();
349
        if (eventlogmgr)
350
            eventlogmgr->flush();
351
    }
352
    catch (cTerminationException& e)
353
    {
354
        simstate = SIM_TERMINATED;
355
        stoppedWithTerminationException(e);
356
        displayException(e);
357
    }
358
    catch (std::exception& e)
359
    {
360
        simstate = SIM_ERROR;
361
        stoppedWithException(e);
362
        displayException(e);
363
    }
364
    stopClock();
365
    stopsimulation_flag = false;
366

    
367
    if (simstate==SIM_TERMINATED)
368
    {
369
        // call wrapper around simulation.callFinish() and simulation.endRun()
370
        //
371
        // NOTE: if the simulation is in SIM_ERROR, we don't want endRun() to be
372
        // called yet, because we want to allow the user to force finish() from
373
        // the GUI -- and finish() has to precede endRun(). endRun() will be called
374
        // just before a new network gets set up, or on Tkenv shutdown.
375
        //
376
        finishSimulation();
377
    }
378
}
379

    
380
void Tkenv::runSimulation(int mode, simtime_t until_time, eventnumber_t until_eventnum, cMessage *until_msg, cModule *until_module)
381
{
382
    ASSERT(simstate==SIM_NEW || simstate==SIM_READY);
383

    
384
    runmode = mode;
385
    rununtil_time = until_time;
386
    rununtil_eventnum = until_eventnum;
387
    rununtil_msg = until_msg;
388
    rununtil_module = until_module;  // Note: this is NOT supported with RUNMODE_EXPRESS
389

    
390
    stopsimulation_flag = false;
391

    
392
    clearNextModuleDisplay();
393
    clearPerformanceDisplay();
394
    updateSimtimeDisplay();
395
    Tcl_Eval(interp, "update");
396

    
397
    simstate = SIM_RUNNING;
398
    startClock();
399
    simulation.getScheduler()->executionResumed();
400
    try
401
    {
402
        // funky while loop to handle switching to and from EXPRESS mode....
403
        bool cont = true;
404
        while (cont)
405
        {
406
            if (runmode==RUNMODE_EXPRESS)
407
                cont = doRunSimulationExpress();
408
            else
409
                cont = doRunSimulation();
410
        }
411
        simstate = SIM_READY;
412
        outvectormgr->flush();
413
        outscalarmgr->flush();
414
        if (eventlogmgr)
415
            eventlogmgr->flush();
416
    }
417
    catch (cTerminationException& e)
418
    {
419
        simstate = SIM_TERMINATED;
420
        stoppedWithTerminationException(e);
421
        displayException(e);
422
    }
423
    catch (std::exception& e)
424
    {
425
        simstate = SIM_ERROR;
426
        stoppedWithException(e);
427
        displayException(e);
428
    }
429
    stopClock();
430
    stopsimulation_flag = false;
431

    
432
    animating = true;
433
    disable_tracing = false;
434
    rununtil_msg = NULL;
435

    
436
    if (simstate==SIM_TERMINATED)
437
    {
438
        // call wrapper around simulation.callFinish() and simulation.endRun()
439
        //
440
        // NOTE: if the simulation is in SIM_ERROR, we don't want endRun() to be
441
        // called yet, because we want to allow the user to force finish() from
442
        // the GUI -- and finish() has to precede endRun(). endRun() will be called
443
        // just before a new network gets set up, or on Tkenv shutdown.
444
        //
445
        finishSimulation();
446
    }
447

    
448
    updateNextModuleDisplay();
449
    clearPerformanceDisplay();
450
    updateSimtimeDisplay();
451
    updateInspectors();
452
}
453

    
454
void Tkenv::setSimulationRunMode(int mode)
455
{
456
    // This function (and the next one too) is called while runSimulation() is
457
    // underway, from Tcl code that gets a chance to run via the
458
    // Tcl_Eval(interp, "update") commands
459
    runmode = mode;
460
}
461

    
462
void Tkenv::setSimulationRunUntil(simtime_t until_time, eventnumber_t until_eventnum, cMessage *until_msg)
463
{
464
    rununtil_time = until_time;
465
    rununtil_eventnum = until_eventnum;
466
    rununtil_msg = until_msg;
467
}
468

    
469
void Tkenv::setSimulationRunUntilModule(cModule *until_module)
470
{
471
    rununtil_module = until_module;
472
}
473

    
474
// note: also updates "since" (sets it to the current time) if answer is "true"
475
inline bool elapsed(long millis, struct timeval& since)
476
{
477
    struct timeval now;
478
    gettimeofday(&now, NULL);
479
    bool ret = timeval_diff_usec(now, since) > 1000*millis;
480
    if (ret)
481
        since = now;
482
    return ret;
483
}
484

    
485
inline void resetElapsedTime(struct timeval& t)
486
{
487
    gettimeofday(&t, NULL);
488
}
489

    
490
bool Tkenv::doRunSimulation()
491
{
492
    //
493
    // IMPORTANT:
494
    // The following variables may change during execution (as a result of user interaction
495
    // during Tcl_Eval("update"):
496
    //  - runmode, rununtil_time, rununtil_eventnum, rununtil_msg, rununtil_module;
497
    //  - stopsimulation_flag
498
    //
499
    Speedometer speedometer;
500
    speedometer.start(simulation.getSimTime());
501
    disable_tracing = false;
502
    bool firstevent = true;
503

    
504
    struct timeval last_update;
505
    gettimeofday(&last_update, NULL);
506

    
507
    while(1)
508
    {
509
        if (runmode==RUNMODE_EXPRESS)
510
            return true;  // should continue, but in a different mode
511

    
512
        cMessage* msg = simulation.getScheduler()->getNextEvent();
513
        cSimpleModule* mod = (cSimpleModule*) simulation.getModule(msg->getArrivalModuleId());
514

    
515
        if (!mod) break; // selectNextModule() interrupted (parsim)
516

    
517
        // "run until message": stop if desired event was reached
518
        if (rununtil_msg && simulation.msgQueue.peekFirst()==rununtil_msg) break;
519

    
520
        // if stepping locally in module, we stop both immediately
521
        // *before* and *after* executing the event in that module,
522
        // but we always execute at least one event
523
        bool untilmodule_reached = rununtil_module && moduleContains(rununtil_module,mod);
524
        if (untilmodule_reached && !firstevent)
525
            break;
526
        firstevent = false;
527

    
528
        animating = (runmode==RUNMODE_NORMAL) || (runmode==RUNMODE_SLOW) || untilmodule_reached;
529
        bool frequent_updates = (runmode==RUNMODE_NORMAL) || (runmode==RUNMODE_SLOW);
530

    
531
        speedometer.addEvent(simulation.getSimTime());
532

    
533
        // do a simulation step
534
        if (opt_event_banners)
535
            printEventBanner(simulation.msgQueue.peekFirst(), mod);
536

    
537
        simulation.doOneEvent(msg);
538
        performAnimations();
539

    
540
        // flush so that output from different modules don't get mixed
541
        flushLastLine();
542

    
543
        // display update
544
        if (frequent_updates || ((simulation.getEventNumber()&0x0f)==0 && elapsed(opt_updatefreq_fast, last_update)))
545
        {
546
            updateSimtimeDisplay();
547
            if (speedometer.getMillisSinceIntervalStart() > SPEEDOMETER_UPDATEMILLISECS)
548
            {
549
                speedometer.beginNewInterval();
550
                updatePerformanceDisplay(speedometer);
551
            }
552
            updateInspectors();
553
            Tcl_Eval(interp, "update");
554
            resetElapsedTime(last_update); // exclude UI update time [bug #52]
555
        }
556

    
557
        // exit conditions
558
        //if (untilmodule_reached) break;
559
        if (stopsimulation_flag) break;
560
        if (rununtil_time>0 && simulation.guessNextSimtime()>=rununtil_time) break;
561
        if (rununtil_eventnum>0 && simulation.getEventNumber()>=rununtil_eventnum) break;
562

    
563
        // delay loop for slow simulation
564
        if (runmode==RUNMODE_SLOW)
565
        {
566
            timeval start;
567
            gettimeofday(&start, NULL);
568
            while (!elapsed(opt_stepdelay, start) && !stopsimulation_flag)
569
                Tcl_Eval(interp, "update");
570
        }
571

    
572
        checkTimeLimits();
573
    }
574
    return false;
575
}
576

    
577
bool Tkenv::doRunSimulationExpress()
578
{
579
    //
580
    // IMPORTANT:
581
    // The following variables may change during execution (as a result of user interaction
582
    // during Tcl_Eval("update"):
583
    //  - runmode, rununtil_time, rununtil_eventnum, rununtil_msg, rununtil_module;
584
    //  - stopsimulation_flag
585
    //  - opt_expressmode_autoupdate
586
    //
587
    // EXPRESS does not support rununtil_module!
588
    //
589

    
590
    logBuffer.addInfo("{...running in Express mode...\n}");
591
    printLastLogLine();
592

    
593
    // update, just to get the above notice displayed
594
    Tcl_Eval(interp, "update");
595

    
596
    // OK, let's begin
597
    Speedometer speedometer;
598
    speedometer.start(simulation.getSimTime());
599
    disable_tracing = true;
600
    animating = false;
601

    
602
    struct timeval last_update;
603
    gettimeofday(&last_update, NULL);
604

    
605
    do
606
    {
607
        cMessage* msg = simulation.getScheduler()->getNextEvent();
608
        cSimpleModule* mod = (cSimpleModule*) simulation.getModule(msg->getArrivalModuleId());
609

    
610
        if (!mod) break;
611

    
612
        // "run until message": stop if desired event was reached
613
        if (rununtil_msg && simulation.msgQueue.peekFirst()==rununtil_msg) break;
614

    
615
        speedometer.addEvent(simulation.getSimTime());
616

    
617
        simulation.doOneEvent(msg);
618

    
619
        if ((simulation.getEventNumber()&0xff)==0 && elapsed(opt_updatefreq_express, last_update))
620
        {
621
            updateSimtimeDisplay();
622
            if (speedometer.getMillisSinceIntervalStart() > SPEEDOMETER_UPDATEMILLISECS)
623
            {
624
                speedometer.beginNewInterval();
625
                updatePerformanceDisplay(speedometer);
626
            }
627
            if (opt_expressmode_autoupdate)
628
                updateInspectors();
629
            Tcl_Eval(interp, "update");
630
            resetElapsedTime(last_update); // exclude UI update time [bug #52]
631
            if (runmode!=RUNMODE_EXPRESS)
632
                return true;  // should continue, but in a different mode
633
        }
634
        checkTimeLimits();
635
    }
636
    while( !stopsimulation_flag &&
637
           (rununtil_time<=0 || simulation.guessNextSimtime() < rununtil_time) &&
638
           (rununtil_eventnum<=0 || simulation.getEventNumber() < rununtil_eventnum)
639
         );
640
    return false;
641
}
642

    
643
void Tkenv::startAll()
644
{
645
    confirm("Not implemented.");
646
}
647

    
648
void Tkenv::finishSimulation()
649
{
650
    // strictly speaking, we shouldn't allow callFinish() after SIM_ERROR, but it comes handy in practice...
651
    ASSERT(simstate==SIM_NEW || simstate==SIM_READY || simstate==SIM_TERMINATED || simstate==SIM_ERROR);
652

    
653
    logBuffer.addInfo("{** Calling finish() methods of modules\n}");
654
    printLastLogLine();
655

    
656
    // now really call finish()
657
    try
658
    {
659
        simulation.callFinish();
660
        flushLastLine();
661

    
662
        checkFingerprint();
663
    }
664
    catch (std::exception& e)
665
    {
666
        displayException(e);
667
    }
668

    
669
    // then endrun
670
    try
671
    {
672
        endRun();
673
    }
674
    catch (std::exception& e)
675
    {
676
        displayException(e);
677
    }
678
    simstate = SIM_FINISHCALLED;
679

    
680
    updateSimtimeDisplay();
681
    updateNextModuleDisplay();
682
    updateInspectors();
683
}
684

    
685
void Tkenv::loadNedFile(const char *fname, const char *expectedPackage, bool isXML)
686
{
687
    try
688
    {
689
        simulation.loadNedFile(fname, expectedPackage, isXML);
690
    }
691
    catch (std::exception& e)
692
    {
693
        displayException(e);
694
    }
695
}
696

    
697
void Tkenv::newNetwork(const char *networkname)
698
{
699
    try
700
    {
701
        // finish & cleanup previous run if we haven't done so yet
702
        if (simstate!=SIM_NONET)
703
        {
704
            endRun();
705
            simulation.deleteNetwork();
706
            simstate = SIM_NONET;
707
        }
708

    
709
        cModuleType *network = resolveNetwork(networkname);
710
        ASSERT(network);
711

    
712
        CHK(Tcl_VarEval(interp, "clear_windows", NULL));
713

    
714
        // set up new network with config General.
715
        isconfigrun = false;
716
        getConfigEx()->activateConfig("General", 0);
717
        readPerRunOptions();
718
        opt_network_name = network->getName();  // override config setting
719
        answers.clear();
720
        simulation.setupNetwork(network);
721
        startRun();
722

    
723
        simstate = SIM_NEW;
724
    }
725
    catch (std::exception& e)
726
    {
727
        displayException(e);
728
        simstate = SIM_ERROR;
729
    }
730

    
731
    // update GUI
732
    animating = false; // affects how network graphics is drawn!
733
    updateNetworkRunDisplay();
734
    updateNextModuleDisplay();
735
    updateSimtimeDisplay();
736
    updateInspectors();
737
}
738

    
739
void Tkenv::newRun(const char *configname, int runnumber)
740
{
741
    try
742
    {
743
        // finish & cleanup previous run if we haven't done so yet
744
        if (simstate!=SIM_NONET)
745
        {
746
            endRun();
747
            simulation.deleteNetwork();
748
            simstate = SIM_NONET;
749
        }
750

    
751
        // set up new network
752
        isconfigrun = true;
753
        getConfigEx()->activateConfig(configname, runnumber);
754
        readPerRunOptions();
755

    
756
        if (opt_network_name.empty())
757
        {
758
            confirm("No network specified in the configuration.");
759
            return;
760
        }
761

    
762
        cModuleType *network = resolveNetwork(opt_network_name.c_str());
763
        ASSERT(network);
764

    
765
        CHK(Tcl_VarEval(interp, "clear_windows", NULL));
766

    
767
        answers.clear();
768
        simulation.setupNetwork(network);
769
        startRun();
770

    
771
        simstate = SIM_NEW;
772
    }
773
    catch (std::exception& e)
774
    {
775
        displayException(e);
776
        simstate = SIM_ERROR;
777
    }
778

    
779
    // update GUI
780
    animating = false; // affects how network graphics is drawn!
781
    updateNetworkRunDisplay();
782
    updateNextModuleDisplay();
783
    updateSimtimeDisplay();
784
    updateInspectors();
785
}
786

    
787
TInspector *Tkenv::inspect(cObject *obj, int type, const char *geometry, void *dat)
788
{
789
    // create inspector object & window or display existing one
790
    TInspector *existing_insp = findInspector(obj, type);
791
    if (existing_insp)
792
    {
793
        existing_insp->showWindow();
794
        return existing_insp;
795
    }
796

    
797
    // create inspector
798
    cInspectorFactory *p = findInspectorFactoryFor(obj,type);
799
    if (!p)
800
    {
801
        confirm(opp_stringf("Class `%s' has no associated inspectors.", obj->getClassName()).c_str());
802
        return NULL;
803
    }
804

    
805
    int actualtype = p->inspectorType();
806
    existing_insp = findInspector(obj, actualtype);
807
    if (existing_insp)
808
    {
809
        existing_insp->showWindow();
810
        return existing_insp;
811
    }
812

    
813
    TInspector *insp = p->createInspectorFor(obj, actualtype, geometry, dat);
814
    if (!insp)
815
    {
816
        // message: object has no such inspector
817
        confirm(opp_stringf("Class `%s' has no `%s' inspector.",obj->getClassName(),insptypeNameFromCode(type)).c_str());
818
        return NULL;
819
    }
820

    
821
    // everything ok, finish inspector
822
    inspectors.insert(inspectors.end(), insp);
823
    insp->createWindow();
824
    insp->update();
825

    
826
    return insp;
827
}
828

    
829
TInspector *Tkenv::findInspector(cObject *obj, int type)
830
{
831
    for (TInspectorList::iterator it = inspectors.begin(); it!=inspectors.end(); ++it)
832
    {
833
        TInspector *insp = *it;
834
        if (insp->getObject()==obj && insp->getType()==type)
835
            return insp;
836
    }
837
    return NULL;
838
}
839

    
840
void Tkenv::deleteInspector(TInspector *insp)
841
{
842
    inspectors.remove(insp);
843
    delete insp;
844
}
845

    
846
void Tkenv::updateInspectors()
847
{
848
    // update inspectors
849
    for (TInspectorList::iterator it = inspectors.begin(); it!=inspectors.end();)
850
    {
851
        TInspector *insp = *it;
852
        TInspectorList::iterator next = ++it;
853
        if (insp->isMarkedForDeletion())
854
            deleteInspector(insp);
855
        else
856
            insp->update();
857
        it = next;
858
    }
859

    
860
    // update object tree
861
    CHK(Tcl_VarEval(interp, "updateTreeManager",NULL));
862

    
863
    // trim log in main window
864
    CHK(Tcl_VarEval(interp, "mainlogwindow_trimlines",NULL));
865

    
866
    // try opening "pending" inspectors
867
    CHK(Tcl_VarEval(interp, "inspectorupdate_callback",NULL));
868
}
869

    
870
void Tkenv::createSnapshot( const char *label )
871
{
872
    simulation.snapshot(&simulation, label );
873
}
874

    
875
void Tkenv::updateGraphicalInspectorsBeforeAnimation()
876
{
877
    for (TInspectorList::iterator it = inspectors.begin(); it!=inspectors.end(); ++it)
878
    {
879
        TInspector *insp = *it;
880
        if (dynamic_cast<TGraphicalModWindow *>(insp) && static_cast<TGraphicalModWindow *>(insp)->needsRedraw())
881
        {
882
            insp->update();
883
        }
884
    }
885
}
886

    
887
void Tkenv::updateNetworkRunDisplay()
888
{
889
    char runnr[10];
890
    const char *networkname;
891

    
892
    if (getConfigEx()->getActiveRunNumber())
893
        sprintf(runnr, "?");
894
    else
895
        sprintf(runnr, "%d", getConfigEx()->getActiveRunNumber());
896

    
897
    if (simulation.getNetworkType()==NULL)
898
        networkname = "(no network)";
899
    else
900
        networkname = simulation.getNetworkType()->getName();
901

    
902
    CHK(Tcl_VarEval(interp, NETWORK_LABEL " config -text {",
903
                        "Run #",runnr,": ",networkname,
904
                        "}", NULL ));
905
    CHK(Tcl_VarEval(interp, "wm title . {OMNeT++/Tkenv - ", getWindowTitlePrefix(), networkname, "}",NULL));
906
}
907

    
908
void Tkenv::updateSimtimeDisplay()
909
{
910
    // event and time display
911
    char buf[32];
912
    sprintf(buf, "%"LL"d", simulation.getEventNumber());
913
    CHK(Tcl_VarEval(interp, EVENT_LABEL " config -text {"
914
                        "Event #", buf,
915
                        "}", NULL ));
916
    CHK(Tcl_VarEval(interp, TIME_LABEL " config -text {"
917
                        "T=", SIMTIME_STR(simulation.guessNextSimtime()),
918
                        "}", NULL ));
919

    
920
    // statistics
921
    sprintf(buf, "%u", simulation.msgQueue.getLength());
922
    CHK(Tcl_VarEval(interp, FESLENGTH_LABEL " config -text {"
923
                        "Msgs scheduled: ", buf,
924
                        "}", NULL ));
925
    sprintf(buf, "%lu", cMessage::getTotalMessageCount());
926
    CHK(Tcl_VarEval(interp, TOTALMSGS_LABEL " config -text {"
927
                        "Msgs created: ", buf,
928
                        "}", NULL ));
929
    sprintf(buf, "%lu", cMessage::getLiveMessageCount());
930
    CHK(Tcl_VarEval(interp, LIVEMSGS_LABEL " config -text {"
931
                        "Msgs present: ", buf,
932
                        "}", NULL ));
933

    
934
    // time axis
935
    CHK(Tcl_Eval(interp, "redraw_timeline"));
936
}
937

    
938
void Tkenv::updateNextModuleDisplay()
939
{
940
    cSimpleModule *mod = NULL;
941

    
942
    if (simstate==SIM_NEW || simstate==SIM_READY || simstate==SIM_RUNNING)
943
        mod = simulation.guessNextModule();
944

    
945
    char id[16];
946
    std::string modname;
947
    if (mod)
948
    {
949
        modname = mod->getFullPath();
950
        sprintf(id," (id=%u)", mod->getId());
951
    }
952
    else
953
    {
954
        modname = "n/a";
955
        id[0]=0;
956
    }
957
    CHK(Tcl_VarEval(interp, NEXT_LABEL " config -text {Next: ",modname.c_str(),id,"}",NULL));
958
}
959

    
960
void Tkenv::clearNextModuleDisplay()
961
{
962
    CHK(Tcl_VarEval(interp, NEXT_LABEL " config -text {Running...}", NULL ));
963
}
964

    
965
void Tkenv::updatePerformanceDisplay(Speedometer& speedometer)
966
{
967
    char buf[16];
968
    sprintf(buf, "%g", speedometer.getSimSecPerSec());
969
    CHK(Tcl_VarEval(interp, SIMSECPERSEC_LABEL " config -text {Simsec/sec: ", buf, "}", NULL));
970
    sprintf(buf, "%g", speedometer.getEventsPerSec());
971
    CHK(Tcl_VarEval(interp, EVENTSPERSEC_LABEL " config -text {Ev/sec: ", buf, "}", NULL));
972
    sprintf(buf, "%g", speedometer.getEventsPerSimSec());
973
    CHK(Tcl_VarEval(interp, EVENTSPERSIMSEC_LABEL " config -text {Ev/simsec: ", buf, "}", NULL));
974
}
975

    
976
void Tkenv::clearPerformanceDisplay()
977
{
978
    CHK(Tcl_VarEval(interp, SIMSECPERSEC_LABEL " config -text {Simsec/sec: n/a}", NULL));
979
    CHK(Tcl_VarEval(interp, EVENTSPERSEC_LABEL " config -text {Ev/sec: n/a}", NULL));
980
    CHK(Tcl_VarEval(interp, EVENTSPERSIMSEC_LABEL " config -text {Ev/simsec: n/a}", NULL));
981
}
982

    
983
void Tkenv::printEventBanner(cMessage *msg, cSimpleModule *module)
984
{
985
    // produce banner text
986
    char banner[2*MAX_OBJECTFULLPATH+2*MAX_CLASSNAME+60];
987
    if (opt_short_banners)
988
        sprintf(banner,"{** Event #%"LL"d  T=%s  %s, on `%s'\n}",
989
                simulation.getEventNumber(),
990
                SIMTIME_STR(simulation.getSimTime()),
991
                module->getFullPath().c_str(),
992
                TclQuotedString(msg->getFullName()).get()
993
              );
994
    else
995
        sprintf(banner,"{** Event #%"LL"d  T=%s  %s (%s, id=%d), on %s`%s' (%s, id=%ld)\n}",
996
                simulation.getEventNumber(),
997
                SIMTIME_STR(simulation.getSimTime()),
998
                module->getFullPath().c_str(),
999
                module->getComponentType()->getName(),
1000
                module->getId(),
1001
                (msg->isSelfMessage() ? "selfmsg " : ""),
1002
                TclQuotedString(msg->getFullName()).get(),
1003
                msg->getClassName(),
1004
                msg->getId()
1005
              );
1006

    
1007
    // insert into log buffer
1008
    logBuffer.addEvent(simulation.getEventNumber(), simulation.getSimTime(), module, banner);
1009

    
1010
    // print into module log windows
1011
    printLastLogLine();
1012

    
1013
    // and into the message window
1014
    if (hasmessagewindow)
1015
        CHK(Tcl_VarEval(interp,
1016
              "catch {\n"
1017
              " .messagewindow.main.text insert end ",banner,"\n"
1018
              " .messagewindow.main.text see end\n"
1019
              "}\n", NULL));
1020

    
1021
}
1022

    
1023
void Tkenv::printLastLogLine()
1024
{
1025
    const LogBuffer::Entry& entry = logBuffer.getEntries().back();
1026

    
1027
    // print into main window
1028
    if (opt_use_mainwindow)
1029
        TModuleWindow::printLastLineOf(interp, ".main.text", logBuffer, mainWindowExcludedModuleIds);
1030

    
1031
    // print into module window and all parent module windows if they exist
1032
    if (!entry.moduleIds)
1033
    {
1034
        // info message: insert into all log windows
1035
        for (TInspectorList::iterator it = inspectors.begin(); it!=inspectors.end(); ++it)
1036
        {
1037
            TModuleWindow *insp = dynamic_cast<TModuleWindow *>(*it);
1038
            if (insp)
1039
                insp->printLastLineOf(logBuffer);
1040
        }
1041
    }
1042
    else
1043
    {
1044
        // insert into the appropriate module windows
1045
        cModule *mod = simulation.getModule(entry.moduleIds[0]);
1046
        while (mod)
1047
        {
1048
            TModuleWindow *insp = static_cast<TModuleWindow *>(findInspector(mod,INSP_MODULEOUTPUT));
1049
            if (insp)
1050
                insp->printLastLineOf(logBuffer);
1051
            mod = mod->getParentModule();
1052
        }
1053
    }
1054
}
1055

    
1056
void Tkenv::displayException(std::exception& ex)
1057
{
1058
    // print exception text into main window
1059
    cException *e = dynamic_cast<cException *>(&ex);
1060
    if (e && e->getSimulationStage()!=CTX_NONE)
1061
    {
1062
        std::string txt = opp_stringf("<!> %s\n", e->getFormattedMessage().c_str());
1063
        logBuffer.addInfo(TclQuotedString(txt.c_str()).get());
1064
        printLastLogLine();
1065
    }
1066

    
1067
    // dialog via our printfmsg()
1068
    EnvirBase::displayException(ex);
1069
}
1070

    
1071
void Tkenv::componentInitBegin(cComponent *component, int stage)
1072
{
1073
    if (!opt_init_banners || runmode == RUNMODE_EXPRESS)
1074
        return;
1075

    
1076
    // produce banner text
1077
    char banner[MAX_OBJECTFULLPATH+60];
1078
    sprintf(banner, "{Initializing %s %s, stage %d\n}",
1079
        component->isModule() ? "module" : "channel", component->getFullPath().c_str(), stage);
1080

    
1081
    // insert into log buffer
1082
    logBuffer.addLogLine(banner);
1083

    
1084
    // print into module log windows
1085
    printLastLogLine();
1086

    
1087
    // and into the message window
1088
    if (hasmessagewindow)
1089
        CHK(Tcl_VarEval(interp,
1090
              "catch {\n"
1091
              " .messagewindow.main.text insert end ",banner,"\n"
1092
              " .messagewindow.main.text see end\n"
1093
              "}\n", NULL));
1094
}
1095

    
1096
void Tkenv::setMainWindowExcludedModuleIds(const std::set<int>& ids)
1097
{
1098
    mainWindowExcludedModuleIds = ids;
1099
    TModuleWindow::redisplay(interp, ".main.text", logBuffer, simulation.getSystemModule(), mainWindowExcludedModuleIds);
1100
}
1101

    
1102
//=========================================================================
1103

    
1104
void Tkenv::readOptions()
1105
{
1106
    EnvirBase::readOptions();
1107

    
1108
    cConfiguration *cfg = getConfig();
1109

    
1110
    opt_extrastack = (size_t) cfg->getAsDouble(CFGID_TKENV_EXTRA_STACK);
1111

    
1112
    const char *s = args->optionValue('c');
1113
    opt_default_config = s ? s : cfg->getAsString(CFGID_TKENV_DEFAULT_CONFIG);
1114

    
1115
    const char *r = args->optionValue('r');
1116
    opt_default_run = r ? atoi(r) : cfg->getAsInt(CFGID_TKENV_DEFAULT_RUN);
1117

    
1118
    opt_image_path = cfg->getAsPath(CFGID_TKENV_IMAGE_PATH).c_str();
1119
    opt_plugin_path = cfg->getAsPath(CFGID_TKENV_PLUGIN_PATH).c_str();
1120
}
1121

    
1122
void Tkenv::readPerRunOptions()
1123
{
1124
    EnvirBase::readPerRunOptions();
1125
}
1126

    
1127
void Tkenv::askParameter(cPar *par, bool unassigned)
1128
{
1129
    // use a value entered by the user earlier ("[x] use this value for similar parameters")
1130
    std::string key = std::string(((cComponent*)par->getOwner())->getNedTypeName()) + ":" + par->getName();
1131
    if (answers.find(key) != answers.end())
1132
    {
1133
        std::string answer = answers[key];
1134
        par->parse(answer.c_str());
1135
        return;
1136
    }
1137

    
1138
    // really ask
1139
    bool success = false;
1140
    bool useForAll = false;
1141
    while (!success)
1142
    {
1143
        cProperties *props = par->getProperties();
1144
        cProperty *prop = props->get("prompt");
1145
        std::string prompt = prop ? prop->getValue(cProperty::DEFAULTKEY) : "";
1146
        if (prompt.empty())
1147
            prompt = std::string("Enter parameter `") + par->getFullPath() + "':";
1148

    
1149
        std::string reply;
1150
        std::string title = unassigned ? "Unassigned Parameter" : "Requested to Ask Parameter";
1151
        bool ok = inputDialog(title.c_str(), prompt.c_str(),
1152
                              "Use this value for all similar parameters",
1153
                              par->str().c_str(), reply, useForAll);
1154
        if (!ok)
1155
            throw cRuntimeError(eCANCEL);
1156

    
1157
        try
1158
        {
1159
            par->parse(reply.c_str());
1160
            success = true;
1161
            if (useForAll)
1162
                answers[key] = reply;
1163
        }
1164
        catch (std::exception& e)
1165
        {
1166
            ev.printfmsg("%s -- please try again.", e.what());
1167
        }
1168
    }
1169
}
1170

    
1171
bool Tkenv::idle()
1172
{
1173
    // bug #56: refresh inspectors so that there aren't dead objects on the UI
1174
    // while running Tk "update" (below). This only needs to be done in Fast
1175
    // mode, because in normal Run mode inspectors are already up to date here
1176
    // (they are refreshed after every event), and in Express mode all user
1177
    // interactions are disabled except for the STOP button.
1178
    if (runmode == RUNMODE_FAST)
1179
    {
1180
        // updateInspectors() may be costly, so do not check the UI too often
1181
        timeval now;
1182
        gettimeofday(&now, NULL);
1183
        if (timeval_msec(now - idleLastUICheck) < 500)
1184
            return false;
1185

    
1186
        // refresh inspectors
1187
        updateSimtimeDisplay();
1188
        updateInspectors();
1189
    }
1190

    
1191
    // process UI events
1192
    eState origsimstate = simstate;
1193
    simstate = SIM_BUSY;
1194
    Tcl_Eval(interp, "update");
1195
    simstate = origsimstate;
1196

    
1197
    bool stop = stopsimulation_flag;
1198
    stopsimulation_flag = false;
1199

    
1200
    if (runmode == RUNMODE_FAST)
1201
        gettimeofday(&idleLastUICheck, NULL);
1202
    return stop;
1203
}
1204

    
1205
void Tkenv::objectDeleted(cObject *object)
1206
{
1207
    if (object==rununtil_msg)
1208
    {
1209
        // message to "run until" deleted -- stop the simulation by other means
1210
        rununtil_msg = NULL;
1211
        rununtil_eventnum = simulation.getEventNumber();
1212
        if (simstate==SIM_RUNNING || simstate==SIM_BUSY)
1213
            confirm("Message to run until has just been deleted.");
1214
    }
1215

    
1216
    for (TInspectorList::iterator it = inspectors.begin(); it!=inspectors.end(); )
1217
    {
1218
        TInspectorList::iterator next = it;
1219
        ++next;
1220
        TInspector *insp = *it;
1221
        if (insp->getObject()==object)
1222
        {
1223
            inspectors.erase(it); // with std::list, "next" remains valid
1224
            insp->hostObjectDeleted();
1225
            delete insp;
1226
        }
1227
        else
1228
        {
1229
            // notify the inspector, maybe it's interested in learning that
1230
            insp->objectDeleted(object);
1231
        }
1232
        it = next;
1233
    }
1234
}
1235

    
1236
void Tkenv::simulationEvent(cMessage *msg)
1237
{
1238
    EnvirBase::simulationEvent(msg);
1239

    
1240
    // display in message window
1241
    if (hasmessagewindow)
1242
        CHK(Tcl_VarEval(interp,
1243
            "catch {\n"
1244
            " .messagewindow.main.text insert end {DELIVD:\t (",msg->getClassName(),")",msg->getFullName(),"}\n"
1245
            " .messagewindow.main.text insert end ",TclQuotedString(msg->info().c_str()).get(),"\n"
1246
            " .messagewindow.main.text insert end {\n}\n"
1247
            " .messagewindow.main.text see end\n"
1248
            "}\n",
1249
            NULL));
1250

    
1251
    if (animating && opt_animation_enabled)
1252
    {
1253
        cGate *arrivalGate = msg->getArrivalGate();
1254
        if (!arrivalGate)
1255
            return;
1256

    
1257
        // if arrivalgate is connected, msg arrived on a connection, otherwise via sendDirect()
1258
        updateGraphicalInspectorsBeforeAnimation();
1259
        if (arrivalGate->getPreviousGate())
1260
        {
1261
            animateDelivery(msg);
1262
        }
1263
        else
1264
        {
1265
            animateDeliveryDirect(msg);
1266
        }
1267
    }
1268
}
1269

    
1270
void Tkenv::messageSent_OBSOLETE(cMessage *msg, cGate *directToGate) //FIXME needed?
1271
{
1272
    // display in message window
1273
    if (hasmessagewindow)
1274
        CHK(Tcl_VarEval(interp,
1275
            "catch {\n"
1276
            " .messagewindow.main.text insert end {SENT:\t (", msg->getClassName(),")", msg->getFullName(), "}\n"
1277
            " .messagewindow.main.text insert end ", TclQuotedString(msg->info().c_str()).get(), "\n"
1278
            " .messagewindow.main.text insert end {\n}\n"
1279
            " .messagewindow.main.text see end\n"
1280
            "}\n",
1281
            NULL));
1282

    
1283
    if (animating && opt_animation_enabled)
1284
    {
1285
        // find suitable inspectors and do animate the message...
1286
        updateGraphicalInspectorsBeforeAnimation(); // actually this will draw `msg' too (which would cause "phantom message"),
1287
                                                    // but we'll manually remove it before animation
1288
        if (!directToGate)
1289
        {
1290
            // message was sent via a gate (send())
1291
            animateSend(msg, msg->getSenderGate(), msg->getArrivalGate());
1292
        }
1293
        else
1294
        {
1295
            // sendDirect() was used
1296
            animateSendDirect(msg, simulation.getModule(msg->getSenderModuleId()), directToGate);
1297
            animateSend(msg, directToGate, msg->getArrivalGate());
1298
        }
1299
    }
1300
}
1301

    
1302
void Tkenv::messageScheduled(cMessage *msg)
1303
{
1304
    EnvirBase::messageScheduled(msg);
1305
}
1306

    
1307
void Tkenv::messageCancelled(cMessage *msg)
1308
{
1309
    if (msg==rununtil_msg && opt_stoponmsgcancel)
1310
    {
1311
        if (simstate==SIM_RUNNING || simstate==SIM_BUSY)
1312
            confirm(opp_stringf("Run-until message `%s' got cancelled.", msg->getName()).c_str());
1313
        rununtil_msg = NULL;
1314
        rununtil_eventnum = simulation.getEventNumber(); // stop the simulation using the eventnumber limit
1315
    }
1316
    EnvirBase::messageCancelled(msg);
1317
}
1318

    
1319
void Tkenv::beginSend(cMessage *msg)
1320
{
1321
    EnvirBase::beginSend(msg);
1322
}
1323

    
1324
void Tkenv::messageSendDirect(cMessage *msg, cGate *toGate, simtime_t propagationDelay, simtime_t transmissionDelay)
1325
{
1326
    EnvirBase::messageSendDirect(msg, toGate, propagationDelay, transmissionDelay);
1327
}
1328

    
1329
void Tkenv::messageSendHop(cMessage *msg, cGate *srcGate)
1330
{
1331
    EnvirBase::messageSendHop(msg, srcGate);
1332
}
1333

    
1334
void Tkenv::messageSendHop(cMessage *msg, cGate *srcGate, simtime_t propagationDelay, simtime_t transmissionDelay)
1335
{
1336
    EnvirBase::messageSendHop(msg, srcGate, propagationDelay, transmissionDelay);
1337
}
1338

    
1339
void Tkenv::endSend(cMessage *msg)
1340
{
1341
    EnvirBase::endSend(msg);
1342
}
1343

    
1344
void Tkenv::messageDeleted(cMessage *msg)
1345
{
1346
    EnvirBase::messageDeleted(msg);
1347
}
1348

    
1349
void Tkenv::componentMethodBegin(cComponent *fromComp, cComponent *toComp, const char *methodFmt, va_list va, bool silent)
1350
{
1351
    va_list va2;
1352
    va_copy(va2, va); // see bug #107
1353
    EnvirBase::componentMethodBegin(fromComp, toComp, methodFmt, va2, silent);
1354
    va_end(va2);
1355

    
1356
    if (silent || !animating || !opt_anim_methodcalls)
1357
        return;
1358

    
1359
    if (!methodFmt)
1360
       return;  // Enter_Method_Silent
1361

    
1362
    if (!fromComp->isModule() || !toComp->isModule())
1363
        return;  // calls to/from channels are not yet animated
1364

    
1365
    updateGraphicalInspectorsBeforeAnimation();
1366

    
1367
    static char methodText[MAX_METHODCALL];
1368
    vsnprintf(methodText, MAX_METHODCALL, methodFmt, va);
1369
    methodText[MAX_METHODCALL-1] = '\0';
1370

    
1371
    cModule *from = (cModule *)fromComp;
1372
    cModule *to = (cModule *)toComp;
1373

    
1374
    // find modules along the way
1375
    PathVec pathvec;
1376
    findDirectPath(from, to, pathvec);
1377

    
1378
    PathVec::iterator i;
1379
    int numinsp = 0;
1380
    for (i=pathvec.begin(); i!=pathvec.end(); i++)
1381
    {
1382
        if (i->to==NULL)
1383
        {
1384
            // ascent
1385
            cModule *mod = i->from;
1386
            cModule *enclosingmod = mod->getParentModule();
1387
            //ev << "DBG: animate ascent inside " << enclosingmod->getFullPath()
1388
            //   << " from " << mod->getFullPath() << endl;
1389
            TInspector *insp = findInspector(enclosingmod,INSP_GRAPHICAL);
1390
            if (insp)
1391
            {
1392
                numinsp++;
1393
                char parentptr[30], modptr[30];
1394
                strcpy(parentptr,ptrToStr(enclosingmod));
1395
                strcpy(modptr,ptrToStr(mod));
1396
                CHK(Tcl_VarEval(interp, "graphmodwin_animate_methodcall_ascent ",
1397
                                        insp->windowName(), " ",
1398
                                        parentptr," ",
1399
                                        modptr," ",
1400
                                        " {",methodText,"} ",
1401
                                        NULL));
1402
            }
1403
        }
1404
        else if (i->from==NULL)
1405
        {
1406
            // animate descent towards destmod
1407
            cModule *mod = i->to;
1408
            cModule *enclosingmod = mod->getParentModule();
1409
            //ev << "DBG: animate descent in " << enclosingmod->getFullPath() <<
1410
            //   " to " << mod->getFullPath() << endl;
1411

    
1412
            TInspector *insp = findInspector(enclosingmod,INSP_GRAPHICAL);
1413
            if (insp)
1414
            {
1415
                numinsp++;
1416
                char parentptr[30], modptr[30];
1417
                strcpy(parentptr,ptrToStr(enclosingmod));
1418
                strcpy(modptr,ptrToStr(mod));
1419
                CHK(Tcl_VarEval(interp, "graphmodwin_animate_methodcall_descent ",
1420
                                        insp->windowName(), " ",
1421
                                        parentptr," ",
1422
                                        modptr," ",
1423
                                        " {",methodText,"} ",
1424
                                        NULL));
1425
            }
1426
        }
1427
        else
1428
        {
1429
            cModule *enclosingmod = i->from->getParentModule();
1430
            TInspector *insp = findInspector(enclosingmod,INSP_GRAPHICAL);
1431
            if (insp)
1432
            {
1433
                numinsp++;
1434
                char fromptr[30], toptr[30];
1435
                strcpy(fromptr,ptrToStr(i->from));
1436
                strcpy(toptr,ptrToStr(i->to));
1437
                CHK(Tcl_VarEval(interp, "graphmodwin_animate_methodcall_horiz ",
1438
                                        insp->windowName(), " ",
1439
                                        fromptr," ",
1440
                                        toptr," ",
1441
                                        " {",methodText,"} ",
1442
                                        NULL));
1443
            }
1444
        }
1445
    }
1446

    
1447
    if (numinsp>0)
1448
    {
1449
        // leave it there for a while
1450
        CHK(Tcl_Eval(interp, "graphmodwin_animate_methodcall_wait"));
1451

    
1452
        // then remove all arrows
1453
        for (i=pathvec.begin(); i!=pathvec.end(); i++)
1454
        {
1455
            cModule *mod= i->from ? i->from : i->to;
1456
            cModule *enclosingmod = mod->getParentModule();
1457
            TInspector *insp = findInspector(enclosingmod,INSP_GRAPHICAL);
1458
            if (insp)
1459
            {
1460
                CHK(Tcl_VarEval(interp, "graphmodwin_animate_methodcall_cleanup ",
1461
                                        insp->windowName(),
1462
                                        NULL));
1463
            }
1464
        }
1465
    }
1466
}
1467

    
1468
void Tkenv::componentMethodEnd()
1469
{
1470
    EnvirBase::componentMethodEnd();
1471
}
1472

    
1473
void Tkenv::moduleCreated(cModule *newmodule)
1474
{
1475
    EnvirBase::moduleCreated(newmodule);
1476

    
1477
    cModule *mod = newmodule->getParentModule();
1478
    TInspector *insp = findInspector(mod,INSP_GRAPHICAL);
1479
    if (!insp) return;
1480
    TGraphicalModWindow *modinsp = dynamic_cast<TGraphicalModWindow *>(insp);
1481
    assert(modinsp);
1482
    modinsp->submoduleCreated(newmodule);
1483
}
1484

    
1485
void Tkenv::moduleDeleted(cModule *module)
1486
{
1487
    EnvirBase::moduleDeleted(module);
1488

    
1489
    cModule *mod = module->getParentModule();
1490
    TInspector *insp = findInspector(mod,INSP_GRAPHICAL);
1491
    if (!insp) return;
1492
    TGraphicalModWindow *modinsp = dynamic_cast<TGraphicalModWindow *>(insp);
1493
    assert(modinsp);
1494
    modinsp->submoduleDeleted(module);
1495
}
1496

    
1497
void Tkenv::moduleReparented(cModule *module, cModule *oldparent)
1498
{
1499
    EnvirBase::moduleReparented(module, oldparent);
1500

    
1501
    // pretend it got deleted from under the 1st module, and got created under the 2nd
1502
    TInspector *insp = findInspector(oldparent,INSP_GRAPHICAL);
1503
    TGraphicalModWindow *modinsp = dynamic_cast<TGraphicalModWindow *>(insp);
1504
    if (modinsp) modinsp->submoduleDeleted(module);
1505

    
1506
    cModule *mod = module->getParentModule();
1507
    TInspector *insp2 = findInspector(mod,INSP_GRAPHICAL);
1508
    TGraphicalModWindow *modinsp2 = dynamic_cast<TGraphicalModWindow *>(insp2);
1509
    if (modinsp2) modinsp2->submoduleCreated(module);
1510
}
1511

    
1512
void Tkenv::connectionCreated(cGate *srcgate)
1513
{
1514
    EnvirBase::connectionCreated(srcgate);
1515

    
1516
    // notify compound module where the connection (whose source is this gate) is displayed
1517
    cModule *notifymodule = NULL;
1518
    if (srcgate->getType()==cGate::OUTPUT)
1519
        notifymodule = srcgate->getOwnerModule()->getParentModule();
1520
    else
1521
        notifymodule = srcgate->getOwnerModule();
1522
    TInspector *insp = findInspector(notifymodule,INSP_GRAPHICAL);
1523
    if (!insp) return;
1524
    TGraphicalModWindow *modinsp = dynamic_cast<TGraphicalModWindow *>(insp);
1525
    assert(modinsp);
1526
    modinsp->connectionCreated(srcgate);
1527
}
1528

    
1529
void Tkenv::connectionDeleted(cGate *srcgate)
1530
{
1531
    EnvirBase::connectionDeleted(srcgate);
1532

    
1533
    // notify compound module where the connection (whose source is this gate) is displayed
1534
    // note: almost the same code as above
1535
    cModule *notifymodule;
1536
    if (srcgate->getType()==cGate::OUTPUT)
1537
        notifymodule = srcgate->getOwnerModule()->getParentModule();
1538
    else
1539
        notifymodule = srcgate->getOwnerModule();
1540
    TInspector *insp = findInspector(notifymodule,INSP_GRAPHICAL);
1541
    if (!insp) return;
1542
    TGraphicalModWindow *modinsp = dynamic_cast<TGraphicalModWindow *>(insp);
1543
    assert(modinsp);
1544
    modinsp->connectionDeleted(srcgate);
1545
}
1546

    
1547
void Tkenv::displayStringChanged(cComponent *component)
1548
{
1549
    EnvirBase::displayStringChanged(component);
1550

    
1551
    if (dynamic_cast<cModule *>(component))
1552
        moduleDisplayStringChanged((cModule *)component);
1553
    else if (dynamic_cast<cChannel *>(component))
1554
        channelDisplayStringChanged((cChannel *)component);
1555
}
1556

    
1557
void Tkenv::channelDisplayStringChanged(cChannel *channel)
1558
{
1559
    cGate *gate = channel->getSourceGate();
1560

    
1561
    // notify module inspector which displays connection
1562
    cModule *notifymodule;
1563
    if (gate->getType()==cGate::OUTPUT)
1564
        notifymodule = gate->getOwnerModule()->getParentModule();
1565
    else
1566
        notifymodule = gate->getOwnerModule();
1567

    
1568
    TInspector *insp = findInspector(notifymodule,INSP_GRAPHICAL);
1569
    if (insp)
1570
    {
1571
        TGraphicalModWindow *modinsp = dynamic_cast<TGraphicalModWindow *>(insp);
1572
        assert(modinsp);
1573
        modinsp->displayStringChanged(gate);
1574
    }
1575

    
1576
    // graphical gate inspector windows: normally a user doesn't have many such windows open
1577
    // (typically, none at all), so we can afford simply refreshing all of them
1578
    for (TInspectorList::iterator it = inspectors.begin(); it!=inspectors.end(); ++it)
1579
    {
1580
        TInspector *insp = *it;
1581
        TGraphicalGateWindow *gateinsp = dynamic_cast<TGraphicalGateWindow *>(insp);
1582
        if (gateinsp)
1583
            gateinsp->displayStringChanged(gate);
1584
    }
1585
}
1586

    
1587
void Tkenv::moduleDisplayStringChanged(cModule *module)
1588
{
1589
    // refresh inspector where this module is a submodule
1590
    cModule *parentmodule = module->getParentModule();
1591
    TInspector *insp = findInspector(parentmodule,INSP_GRAPHICAL);
1592
    if (insp)
1593
    {
1594
        TGraphicalModWindow *parentmodinsp = dynamic_cast<TGraphicalModWindow *>(insp);
1595
        assert(parentmodinsp);
1596
        parentmodinsp->displayStringChanged(module);
1597
    }
1598

    
1599
    // refresh inspector where this module is the parent (i.e. this is a
1600
    // background display string change)
1601
    insp = findInspector(module,INSP_GRAPHICAL);
1602
    if (insp)
1603
    {
1604
        TGraphicalModWindow *modinsp = dynamic_cast<TGraphicalModWindow *>(insp);
1605
        assert(modinsp);
1606
        modinsp->displayStringChanged();
1607
    }
1608
}
1609

    
1610
void Tkenv::animateSend(cMessage *msg, cGate *fromgate, cGate *togate)
1611
{
1612
    char msgptr[32];
1613
    ptrToStr(msg,msgptr);
1614

    
1615
    cGate *g = fromgate;
1616
    cGate *arrivalgate = togate;
1617

    
1618
    while (g && g->getNextGate())
1619
    {
1620
        cModule *mod = g->getOwnerModule();
1621
        if (g->getType()==cGate::OUTPUT) mod = mod->getParentModule();
1622

    
1623
        TInspector *insp = findInspector(mod,INSP_GRAPHICAL);
1624
        if (insp)
1625
        {
1626
            int lastgate = (g->getNextGate()==arrivalgate);
1627
            CHK(Tcl_VarEval(interp, "graphmodwin_animate_on_conn ",
1628
                                    insp->windowName(), " ",
1629
                                    msgptr, " ",
1630
                                    ptrToStr(g)," ",
1631
                                    (lastgate?"beg":"thru"),
1632
                                    NULL));
1633
        }
1634
        g = g->getNextGate();
1635
    }
1636
}
1637

    
1638
// helper for animateSendDirect() functions
1639
static cModule *findSubmoduleTowards(cModule *parentmod, cModule *towardsgrandchild)
1640
{
1641
    if (parentmod==towardsgrandchild)
1642
       return NULL; // shortcut -- we don't have to go up to the top to see we missed
1643

    
1644
    // search upwards from 'towardsgrandchild'
1645
    cModule *m = towardsgrandchild;
1646
    while (m && m->getParentModule()!=parentmod)
1647
       m = m->getParentModule();
1648
    return m;
1649
}
1650

    
1651

    
1652
void Tkenv::findDirectPath(cModule *srcmod, cModule *destmod, PathVec& pathvec)
1653
{
1654
    // for animation purposes, we assume that the message travels up
1655
    // in the module hierarchy until it finds the first compound module
1656
    // that also contains the destination module (possibly somewhere deep),
1657
    // and then it descends to the destination module. We have to find the
1658
    // list of modules visited during the travel.
1659

    
1660
    // first, find "lowest common ancestor" module
1661
    cModule *commonparent = srcmod;
1662
    while (commonparent)
1663
    {
1664
        // try to find commonparent among ancestors of destmod
1665
        cModule *m = destmod;
1666
        while (m && commonparent!=m)
1667
            m = m->getParentModule();
1668
        if (commonparent==m)
1669
            break;
1670
        commonparent = commonparent->getParentModule();
1671
    }
1672

    
1673
    // commonparent should exist, worst case it's the system module,
1674
    // but let's have the following "if" anyway...
1675
    if (!commonparent)
1676
        return;
1677

    
1678
    // animate the ascent of the message until commonparent (excluding).
1679
    // The second condition, destmod==commonparent covers case when we're sending
1680
    // to an output gate of the parent (grandparent, etc) gate.
1681
    cModule *mod = srcmod;
1682
    while (mod!=commonparent && (mod->getParentModule()!=commonparent || destmod==commonparent))
1683
    {
1684
        pathvec.push_back(sPathEntry(mod,NULL));
1685
        mod = mod->getParentModule();
1686
    }
1687

    
1688
    // animate within commonparent
1689
    if (commonparent!=srcmod && commonparent!=destmod)
1690
    {
1691
        cModule *from = findSubmoduleTowards(commonparent, srcmod);
1692
        cModule *to = findSubmoduleTowards(commonparent, destmod);
1693
        pathvec.push_back(sPathEntry(from,to));
1694
    }
1695

    
1696
    // descend from commonparent to destmod
1697
    mod = findSubmoduleTowards(commonparent, destmod);
1698
    if (mod && srcmod!=commonparent)
1699
        mod = findSubmoduleTowards(mod, destmod);
1700
    while (mod)
1701
    {
1702
        // animate descent towards destmod
1703
        pathvec.push_back(sPathEntry(NULL,mod));
1704
        // find module 'under' mod, towards destmod (this will return NULL if mod==destmod)
1705
        mod = findSubmoduleTowards(mod, destmod);
1706
    }
1707
}
1708

    
1709
void Tkenv::animateSendDirect(cMessage *msg, cModule *frommodule, cGate *togate)
1710
{
1711
    char msgptr[32];
1712
    ptrToStr(msg,msgptr);
1713

    
1714
    PathVec pathvec;
1715
    findDirectPath(frommodule, togate->getOwnerModule(), pathvec);
1716

    
1717
    cModule *arrivalmod = msg->getArrivalGate()->getOwnerModule();
1718

    
1719
    PathVec::iterator i;
1720
    for (i=pathvec.begin(); i!=pathvec.end(); i++)
1721
    {
1722
        if (i->to==NULL)
1723
        {
1724
            // ascent
1725
            cModule *mod = i->from;
1726
            cModule *enclosingmod = mod->getParentModule();
1727
            TInspector *insp = findInspector(enclosingmod,INSP_GRAPHICAL);
1728
            if (insp)
1729
            {
1730
                char parentptr[30], modptr[30];
1731
                strcpy(parentptr,ptrToStr(enclosingmod));
1732
                strcpy(modptr,ptrToStr(mod));
1733
                CHK(Tcl_VarEval(interp, "graphmodwin_animate_senddirect_ascent ",
1734
                                        insp->windowName(), " ",
1735
                                        msgptr, " ",
1736
                                        parentptr," ",
1737
                                        modptr," ",
1738
                                        "thru", // cannot be "beg" (msg ball cannot stay on encl.module rect)
1739
                                        NULL));
1740
            }
1741
        }
1742
        else if (i->from==NULL)
1743
        {
1744
            // animate descent towards destmod
1745
            cModule *mod = i->to;
1746
            cModule *enclosingmod = mod->getParentModule();
1747
            TInspector *insp = findInspector(enclosingmod,INSP_GRAPHICAL);
1748
            if (insp)
1749
            {
1750
                char parentptr[30], modptr[30];
1751
                strcpy(parentptr,ptrToStr(enclosingmod));
1752
                strcpy(modptr,ptrToStr(mod));
1753
                CHK(Tcl_VarEval(interp, "graphmodwin_animate_senddirect_descent ",
1754
                                        insp->windowName(), " ",
1755
                                        msgptr, " ",
1756
                                        parentptr," ",
1757
                                        modptr," ",
1758
                                        (mod==arrivalmod?"beg":"thru"),
1759
                                        NULL));
1760
            }
1761
        }
1762
        else
1763
        {
1764
            cModule *enclosingmod = i->from->getParentModule();
1765
            TInspector *insp = findInspector(enclosingmod,INSP_GRAPHICAL);
1766
            if (insp)
1767
            {
1768
                char fromptr[30], toptr[30];
1769
                strcpy(fromptr,ptrToStr(i->from));
1770
                strcpy(toptr,ptrToStr(i->to));
1771
                CHK(Tcl_VarEval(interp, "graphmodwin_animate_senddirect_horiz ",
1772
                                        insp->windowName(), " ",
1773
                                        msgptr, " ",
1774
                                        fromptr," ",
1775
                                        toptr," ",
1776
                                        (i->to==arrivalmod?"beg":"thru"),
1777
                                        NULL));
1778
            }
1779
        }
1780
    }
1781

    
1782
    // then remove all arrows
1783
    for (i=pathvec.begin(); i!=pathvec.end(); i++)
1784
    {
1785
        cModule *mod= i->from ? i->from : i->to;
1786
        cModule *enclosingmod = mod->getParentModule();
1787
        TInspector *insp = findInspector(enclosingmod,INSP_GRAPHICAL);
1788
        if (insp)
1789
        {
1790
            CHK(Tcl_VarEval(interp, "graphmodwin_animate_senddirect_cleanup ",
1791
                                    insp->windowName(),
1792
                                    NULL));
1793
        }
1794
    }
1795
}
1796

    
1797

    
1798
void Tkenv::animateDelivery(cMessage *msg)
1799
{
1800
    char msgptr[32];
1801
    ptrToStr(msg,msgptr);
1802

    
1803
    // find suitable inspectors and do animate the message...
1804
    cGate *g = msg->getArrivalGate();
1805
    ASSERT(g);
1806
    g = g->getPreviousGate();
1807
    ASSERT(g);
1808

    
1809
    cModule *mod = g->getOwnerModule();
1810
    if (g->getType()==cGate::OUTPUT) mod = mod->getParentModule();
1811

    
1812
    TInspector *insp = findInspector(mod,INSP_GRAPHICAL);
1813
    if (insp)
1814
    {
1815
        CHK(Tcl_VarEval(interp, "graphmodwin_animate_on_conn ",
1816
                                insp->windowName(), " ",
1817
                                msgptr, " ",
1818
                                ptrToStr(g)," ",
1819
                                "end",
1820
                                NULL));
1821
    }
1822
}
1823

    
1824
void Tkenv::animateDeliveryDirect(cMessage *msg)
1825
{
1826
    char msgptr[32];
1827
    ptrToStr(msg,msgptr);
1828

    
1829
    // find suitable inspectors and do animate the message...
1830
    cGate *g = msg->getArrivalGate();
1831
    ASSERT(g);
1832
    cModule *destmod = g->getOwnerModule();
1833
    cModule *mod = destmod->getParentModule();
1834

    
1835
    TInspector *insp = findInspector(mod,INSP_GRAPHICAL);
1836
    if (insp)
1837
    {
1838
        CHK(Tcl_VarEval(interp, "graphmodwin_animate_senddirect_delivery ",
1839
                                insp->windowName(), " ",
1840
                                msgptr, " ",
1841
                                ptrToStr(destmod),
1842
                                NULL));
1843
    }
1844
}
1845

    
1846
void Tkenv::performAnimations()
1847
{
1848
    CHK(Tcl_VarEval(interp, "perform_animations", NULL));
1849
}
1850

    
1851
void Tkenv::bubble(cComponent *component, const char *text)
1852
{
1853
    EnvirBase::bubble(component, text);
1854

    
1855
    if (disable_tracing)
1856
        return;
1857

    
1858
    if (!opt_bubbles)
1859
        return;
1860

    
1861
    if (dynamic_cast<cModule *>(component))
1862
    {
1863
        // module bubble
1864
        cModule *mod = (cModule *)component;
1865
        cModule *parentmod = mod->getParentModule();
1866
        TInspector *insp = findInspector(parentmod,INSP_GRAPHICAL);
1867
        if (!insp) return;
1868
        TGraphicalModWindow *modinsp = dynamic_cast<TGraphicalModWindow *>(insp);
1869
        assert(modinsp);
1870
        modinsp->bubble(mod, text);
1871
    }
1872
    else if (dynamic_cast<cChannel *>(component))
1873
    {
1874
        //TODO channel bubble
1875
    }
1876
}
1877

    
1878
void Tkenv::confirm(const char *msg)
1879
{
1880
    if (!interp)
1881
        ::printf("\n<!> %s\n\n", msg); // fallback in case Tkenv didn't fire up correctly
1882
    else
1883
        CHK(Tcl_VarEval(interp, "messagebox {Confirm} ",TclQuotedString(msg).get()," info ok", NULL));
1884
}
1885

    
1886
void Tkenv::putsmsg(const char *msg)
1887
{
1888
    confirm(msg);
1889
}
1890

    
1891
void Tkenv::sputn(const char *s, int n)
1892
{
1893
    EnvirBase::sputn(s, n);
1894

    
1895
    if (disable_tracing)
1896
        return;
1897

    
1898
    if (!interp)
1899
    {
1900
        (void) ::fwrite(s,1,n,stdout); // fallback in case Tkenv didn't fire up correctly
1901
        return;
1902
    }
1903

    
1904
    // rough guard against forgotten "\n"'s in the code
1905
    if (n>5000)
1906
    {
1907
        const char *s2 = "... [line too long, truncated]\n";
1908
        this->sputn(s, 5000);
1909
        this->sputn(s2, strlen(s2));
1910
        return;
1911
    }
1912

    
1913
    // insert into log buffer
1914
    cModule *module = simulation.getContextModule();
1915
    if (module)
1916
        logBuffer.addLogLine(TclQuotedString(s,n).get()); //FIXME too much copying! reuse original string if no quoting needed
1917
    else
1918
        logBuffer.addInfo(TclQuotedString(s,n).get()); //FIXME too much copying! reuse original string if no quoting needed
1919

    
1920
    // print string into log windows
1921
    printLastLogLine();
1922
}
1923

    
1924
cEnvir& Tkenv::flush()
1925
{
1926
    // Tk doesn't need flush(), it displays everything ASAP anyway
1927
    return *this;
1928
}
1929

    
1930
bool Tkenv::inputDialog(const char *title, const char *prompt,
1931
                        const char *checkboxLabel, const char *defaultValue,
1932
                        std::string& outResult, bool& inoutCheckState)
1933
{
1934
    CHK(Tcl_Eval(interp, "global opp"));
1935
    Tcl_SetVar2(interp, "opp", "result", (char *)defaultValue, TCL_GLOBAL_ONLY);
1936
    Tcl_SetVar2(interp, "opp", "check", (char *)(inoutCheckState ? "1" : "0"), TCL_GLOBAL_ONLY);
1937
    if (checkboxLabel==NULL)
1938
        CHK(Tcl_VarEval(interp, "inputbox ",
1939
                        TclQuotedString(title).get()," ",
1940
                        TclQuotedString(prompt).get()," opp(result) ", NULL));
1941
    else
1942
        CHK(Tcl_VarEval(interp, "inputbox ",
1943
                        TclQuotedString(title).get()," ",
1944
                        TclQuotedString(prompt).get()," opp(result) ",
1945
                        TclQuotedString(checkboxLabel).get(), " opp(check)", NULL));
1946

    
1947
    if (Tcl_GetStringResult(interp)[0]=='0') {
1948
        return false;  // cancel
1949
    }
1950
    else {
1951
        outResult = Tcl_GetVar2(interp, "opp", "result", TCL_GLOBAL_ONLY);
1952
        inoutCheckState = Tcl_GetVar2(interp, "opp", "check", TCL_GLOBAL_ONLY)[0]=='1';
1953
        return true; // OK
1954
    }
1955
}
1956

    
1957
std::string Tkenv::gets(const char *promt, const char *defaultReply)
1958
{
1959
    cModule *mod = simulation.getContextModule();
1960
    std::string title = mod ? mod->getFullPath() : simulation.getNetworkType()->getName();
1961
    std::string result;
1962
    bool dummy;
1963
    bool ok = inputDialog(title.c_str(), promt, NULL, defaultReply, result, dummy);
1964
    if (!ok)
1965
        throw cRuntimeError(eCANCEL);
1966
    return result;
1967
}
1968

    
1969
bool Tkenv::askyesno(const char *question)
1970
{
1971
    // should return -1 when CANCEL is pressed
1972
    CHK(Tcl_VarEval(interp, "messagebox {Tkenv} ",TclQuotedString(question).get()," question yesno", NULL));
1973
    return Tcl_GetStringResult(interp)[0]=='y';
1974
}
1975

    
1976
unsigned Tkenv::getExtraStackForEnvir() const
1977
{
1978
     return opt_extrastack;
1979
}
1980

    
1981
//======================================================================
1982
// dummy function to force Unix linkers collect all symbols needed
1983

    
1984
void _dummy_for_objinsp();
1985
void _dummy_for_modinsp();
1986
void _dummy_for_statinsp();
1987

    
1988
void _dummy_func() {
1989
  _dummy_for_objinsp();
1990
  _dummy_for_modinsp();
1991
  _dummy_for_statinsp();
1992
}
1993

    
1994