root/trunk/thune/debugger/ThuneDebugger.cpp

Revision 458, 18.1 kB (checked in by krobillard, 17 months ago)

Merged Thune thread_safe branch into trunk (r386:457)

Line 
1/*==========================================================================*
2
3 *==========================================================================*/
4
5
6#include <QtGui>
7#include "ThuneDebugger.h"
8#include "ThuneHighlight.h"
9#include "StackDisplay.h"
10#include "internal.h"
11
12
13#define APP_NAME          "Thune"
14#define APP_SETTINGS      "Thune", "Debugger"
15
16
17static QTextEdit*       gConsole;
18static ThuneDebugger*   gDB;
19
20
21ThuneDebugger::ThuneDebugger()
22{
23    gDB = this;
24
25    _mode = kModeVoid;
26    _env.threads = 0;
27
28    _loop = new QEventLoop( this );
29
30
31    // Editor
32
33    _editor = new QTextEdit;
34    _editor->setTabStopWidth( 8 * 4 /*_editor->fontMetrics().height() * 3*/ );
35    new ThuneHighlight(_editor->document());
36    setCentralWidget(_editor);
37
38
39    // Console
40
41    _console = new QTextEdit;
42    _console->setReadOnly( true );
43
44    QDockWidget* dock;
45    dock = new QDockWidget( this );
46    dock->setWindowTitle( "Output" );
47    dock->setWidget( _console );
48    dock->setAllowedAreas( Qt::TopDockWidgetArea |
49                           Qt::BottomDockWidgetArea );
50    dock->setFeatures( QDockWidget::DockWidgetMovable |
51                       QDockWidget::DockWidgetFloatable );
52    addDockWidget( Qt::BottomDockWidgetArea, dock );
53
54
55    // Stack Display
56
57    _stack = new StackDisplay;
58
59    dock = new QDockWidget( this );
60    dock->setWindowTitle( "Data Stack" );
61    dock->setWidget( _stack );
62    addDockWidget( Qt::RightDockWidgetArea, dock );
63
64
65    createActions();
66    createMenus();
67    createToolBars();
68    createStatusBar();
69
70    readSettings();
71
72    connect(_editor->document(), SIGNAL(contentsChanged()),
73            this, SLOT(documentWasModified()));
74
75    setCurrentFile("");
76}
77
78
79ThuneDebugger::~ThuneDebugger()
80{
81    destroyEnv();
82}
83
84
85void ThuneDebugger::closeEvent(QCloseEvent *event)
86{
87    if (maybeSave())
88    {
89        writeSettings();
90        event->accept();
91    }
92    else
93    {
94        event->ignore();
95    }
96}
97
98
99void ThuneDebugger::newFile()
100{
101    if (maybeSave())
102    {
103        _editor->clear();
104        setCurrentFile("");
105    }
106}
107
108
109void ThuneDebugger::open()
110{
111    if (maybeSave())
112    {
113        QString fileName = QFileDialog::getOpenFileName(this);
114        if (!fileName.isEmpty())
115            loadFile(fileName);
116    }
117}
118
119
120bool ThuneDebugger::save()
121{
122    if (curFile.isEmpty())
123    {
124        return saveAs();
125    }
126    else
127    {
128        return saveFile(curFile);
129    }
130}
131
132
133bool ThuneDebugger::saveAs()
134{
135    QString fileName = QFileDialog::getSaveFileName(this);
136    if (fileName.isEmpty())
137        return false;
138
139    return saveFile(fileName);
140}
141
142
143void ThuneDebugger::openRecentFile()
144{
145    QAction* action = qobject_cast<QAction*>(sender());
146    if( action )
147        loadFile( action->data().toString() );
148}
149
150
151void ThuneDebugger::about()
152{
153    QMessageBox* dlg;
154    QString msg = QString( "<h2>Thune Debugger</h2>\n"
155                           "<p>Version %1 - %2</p><br/>"
156                           "Written by Karl Robillard</p>"
157                         ).arg( UR_VERSION_STR )
158                          .arg( __DATE__ );
159
160    dlg = new QMessageBox( this );
161    dlg->setAttribute( Qt::WA_DeleteOnClose );
162    dlg->setWindowTitle( tr("About Thune") );
163    dlg->setTextFormat( Qt::RichText );
164    dlg->setText( msg );
165    dlg->setIconPixmap( QPixmap(":/images/thune.png") );
166    dlg->show();
167}
168
169
170void ThuneDebugger::documentWasModified()
171{
172    setWindowModified(_editor->document()->isModified());
173}
174
175
176void ThuneDebugger::createActions()
177{
178    newAct = new QAction(QIcon(":/images/new.png"), tr("&New"), this);
179    newAct->setShortcut(tr("Ctrl+N"));
180    newAct->setStatusTip(tr("Create a new file"));
181    connect(newAct, SIGNAL(triggered()), this, SLOT(newFile()));
182
183    openAct = new QAction(QIcon(":/images/open.png"), tr("&Open..."), this);
184    openAct->setShortcut(tr("Ctrl+O"));
185    openAct->setStatusTip(tr("Open an existing file"));
186    connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
187
188    saveAct = new QAction(QIcon(":/images/save.png"), tr("&Save"), this);
189    saveAct->setShortcut(tr("Ctrl+S"));
190    saveAct->setStatusTip(tr("Save the document to disk"));
191    connect(saveAct, SIGNAL(triggered()), this, SLOT(save()));
192
193    saveAsAct = new QAction(tr("Save &As..."), this);
194    saveAsAct->setStatusTip(tr("Save the document under a new name"));
195    connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs()));
196
197    exitAct = new QAction(tr("E&xit"), this);
198    exitAct->setShortcut(tr("Ctrl+Q"));
199    exitAct->setStatusTip(tr("Exit the application"));
200    connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
201
202    cutAct = new QAction(QIcon(":/images/cut.png"), tr("Cu&t"), this);
203    cutAct->setShortcut(tr("Ctrl+X"));
204    cutAct->setStatusTip(tr("Cut the current selection's contents to the "
205                            "clipboard"));
206    connect(cutAct, SIGNAL(triggered()), _editor, SLOT(cut()));
207
208    copyAct = new QAction(QIcon(":/images/copy.png"), tr("&Copy"), this);
209    copyAct->setShortcut(tr("Ctrl+C"));
210    copyAct->setStatusTip(tr("Copy the current selection's contents to the "
211                             "clipboard"));
212    connect(copyAct, SIGNAL(triggered()), _editor, SLOT(copy()));
213
214    pasteAct = new QAction(QIcon(":/images/paste.png"), tr("&Paste"), this);
215    pasteAct->setShortcut(tr("Ctrl+V"));
216    pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current "
217                              "selection"));
218    connect(pasteAct, SIGNAL(triggered()), _editor, SLOT(paste()));
219
220    aboutAct = new QAction(tr("&About"), this);
221    aboutAct->setStatusTip(tr("Show the application's About box"));
222    connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
223
224    /*
225    aboutQtAct = new QAction(tr("About &Qt"), this);
226    aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
227    connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
228    */
229
230    cutAct->setEnabled(false);
231    copyAct->setEnabled(false);
232    connect(_editor, SIGNAL(copyAvailable(bool)),
233            cutAct, SLOT(setEnabled(bool)));
234    connect(_editor, SIGNAL(copyAvailable(bool)),
235            copyAct, SLOT(setEnabled(bool)));
236
237
238    // Recent Files
239
240    for( int i = 0; i < MaxRecentFiles; ++i )
241    {
242        recentFileActs[i] = new QAction(this);
243        recentFileActs[i]->setVisible(false);
244        connect(recentFileActs[i], SIGNAL(triggered()),
245                this, SLOT(openRecentFile()));
246    }
247
248
249    // Debug actions
250
251    _actRun = new QAction(QIcon(":/images/run.png"), tr("&Run"), this);
252    _actRun->setShortcut(tr("F6"));
253    _actRun->setStatusTip(tr("Run program"));
254    connect(_actRun, SIGNAL(triggered()), this, SLOT(dbRun()));
255
256    _actStep = new QAction(QIcon(":/images/step.png"), tr("&Step"), this);
257    _actStep->setShortcut(tr("F5"));
258    _actStep->setStatusTip(tr("Evaluate one value"));
259    connect(_actStep, SIGNAL(triggered()), this, SLOT(dbStep()));
260
261    _actHalt = new QAction(QIcon(":/images/stop.png"), tr("&Halt"), this);
262    _actHalt->setShortcut(tr("Esc"));
263    _actHalt->setStatusTip(tr("Halt program"));
264    connect(_actHalt, SIGNAL(triggered()), this, SLOT(dbHalt()));
265}
266
267
268void ThuneDebugger::createMenus()
269{
270    QMenu* fileMenu;
271    QMenu* editMenu;
272    QMenu* helpMenu;
273    QMenu* progMenu;
274
275    fileMenu = menuBar()->addMenu(tr("&File"));
276    fileMenu->addAction(newAct);
277    fileMenu->addAction(openAct);
278    fileMenu->addAction(saveAct);
279    fileMenu->addAction(saveAsAct);
280    separatorAct = fileMenu->addSeparator();
281    for( int i = 0; i < MaxRecentFiles; ++i )
282        fileMenu->addAction(recentFileActs[i]);
283    fileMenu->addSeparator();
284    fileMenu->addAction(exitAct);
285
286    updateRecentFileActions();
287
288    editMenu = menuBar()->addMenu(tr("&Edit"));
289    editMenu->addAction(cutAct);
290    editMenu->addAction(copyAct);
291    editMenu->addAction(pasteAct);
292
293    progMenu = menuBar()->addMenu(tr("&Program"));
294    progMenu->addAction(_actRun);
295    progMenu->addAction(_actStep);
296    progMenu->addAction(_actHalt);
297
298    menuBar()->addSeparator();
299
300    helpMenu = menuBar()->addMenu(tr("&Help"));
301    helpMenu->addAction(aboutAct);
302}
303
304
305void ThuneDebugger::createToolBars()
306{
307    _fileToolBar = addToolBar(tr("File"));
308    _fileToolBar->addAction(newAct);
309    _fileToolBar->addAction(openAct);
310    _fileToolBar->addAction(saveAct);
311
312    _editToolBar = addToolBar(tr("Edit"));
313    _editToolBar->addAction(cutAct);
314    _editToolBar->addAction(copyAct);
315    _editToolBar->addAction(pasteAct);
316
317    _debugToolBar = addToolBar(tr("Debug"));
318    _debugToolBar->addAction(_actRun);
319    _debugToolBar->addAction(_actStep);
320    _debugToolBar->addAction(_actHalt);
321}
322
323
324void ThuneDebugger::createStatusBar()
325{
326    statusBar()->showMessage(tr("Ready"));
327}
328
329
330void ThuneDebugger::readSettings()
331{
332    QSettings settings( APP_SETTINGS );
333    QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
334    QSize size = settings.value("size", QSize(400, 400)).toSize();
335    resize(size);
336    move(pos);
337}
338
339
340void ThuneDebugger::writeSettings()
341{
342    QSettings settings( APP_SETTINGS );
343    settings.setValue("pos", pos());
344    settings.setValue("size", size());
345}
346
347
348bool ThuneDebugger::maybeSave()
349{
350    if (_editor->document()->isModified()) {
351        int ret = QMessageBox::warning(this, APP_NAME,
352                     tr("The document has been modified.\n"
353                        "Do you want to save your changes?"),
354                     QMessageBox::Yes | QMessageBox::Default,
355                     QMessageBox::No,
356                     QMessageBox::Cancel | QMessageBox::Escape);
357        if (ret == QMessageBox::Yes)
358            return save();
359        else if (ret == QMessageBox::Cancel)
360            return false;
361    }
362    return true;
363}
364
365
366void ThuneDebugger::loadFile(const QString &fileName)
367{
368    QFile file(fileName);
369    if (!file.open(QFile::ReadOnly | QFile::Text))
370    {
371        QMessageBox::warning(this, APP_NAME,
372                             tr("Cannot read file %1:\n%2.")
373                             .arg(fileName)
374                             .arg(file.errorString()));
375        return;
376    }
377
378    QTextStream in(&file);
379    QApplication::setOverrideCursor(Qt::WaitCursor);
380    _editor->setPlainText(in.readAll());
381    QApplication::restoreOverrideCursor();
382
383    setCurrentFile(fileName);
384    statusBar()->showMessage(tr("File loaded"), 2000);
385}
386
387
388bool ThuneDebugger::saveFile(const QString &fileName)
389{
390    QFile file(fileName);
391    if (!file.open(QFile::WriteOnly | QFile::Text)) {
392        QMessageBox::warning(this, APP_NAME,
393                             tr("Cannot write file %1:\n%2.")
394                             .arg(fileName)
395                             .arg(file.errorString()));
396        return false;
397    }
398
399    QTextStream out(&file);
400    QApplication::setOverrideCursor(Qt::WaitCursor);
401    out << _editor->toPlainText();
402    QApplication::restoreOverrideCursor();
403
404    setCurrentFile(fileName);
405    statusBar()->showMessage(tr("File saved"), 2000);
406    return true;
407}
408
409
410void ThuneDebugger::setCurrentFile(const QString &fileName)
411{
412    curFile = fileName;
413    _editor->document()->setModified(false);
414    setWindowModified(false);
415
416    QString shownName;
417    if( fileName.isEmpty() )
418    {
419        shownName = "untitled.t";
420    }
421    else
422    {
423        addRecentFile( fileName );
424        shownName = strippedName(fileName);
425    }
426
427    setWindowTitle(tr("%1[*] - %2").arg(shownName).arg( APP_NAME ));
428}
429
430
431void ThuneDebugger::addRecentFile( const QString &fileName )
432{
433    QSettings settings( APP_SETTINGS );
434
435    QStringList files = settings.value("recentFileList").toStringList();
436    files.removeAll(fileName);
437    files.prepend(fileName);
438
439    while( files.size() > MaxRecentFiles )
440        files.removeLast();
441
442    settings.setValue("recentFileList", files);
443
444#if 0
445    foreach( QWidget *widget, QApplication::topLevelWidgets() )
446    {
447        ThuneDebugger* mainWin = qobject_cast<ThuneDebugger*>(widget);
448        if( mainWin )
449            mainWin->updateRecentFileActions();
450    }
451#else
452    updateRecentFileActions();
453#endif
454}
455
456
457void ThuneDebugger::updateRecentFileActions()
458{
459    QSettings settings( APP_SETTINGS );
460    QStringList files = settings.value("recentFileList").toStringList();
461
462    int numRecentFiles = qMin(files.size(), (int)MaxRecentFiles);
463
464    for( int i = 0; i < numRecentFiles; ++i )
465    {
466        QString text = tr("&%1 %2").arg(i + 1).arg(strippedName(files[i]));
467        recentFileActs[i]->setText(text);
468        recentFileActs[i]->setData(files[i]);
469        recentFileActs[i]->setVisible(true);
470    }
471    for( int j = numRecentFiles; j < MaxRecentFiles; ++j )
472        recentFileActs[j]->setVisible(false);
473
474    separatorAct->setVisible(numRecentFiles > 0);
475}
476
477
478QString ThuneDebugger::strippedName(const QString &fullFileName)
479{
480    return QFileInfo(fullFileName).fileName();
481}
482
483
484//----------------------------------------------------------------------------
485
486
487void ThuneDebugger::showPC( UThread* ut, UCell* pc, UCell* end )
488{
489    UString* str = ur_threadTmp( ut );
490
491    ur_arrayReserve( str, 1, 4 );
492    str->ptr.c[0] = '.';
493    str->ptr.c[1] = '.';
494    str->ptr.c[2] = ' ';
495    str->used = 3;
496
497    ur_toStr( pc, str, 0 );
498
499    ++pc;
500    while( pc != end )
501    {
502        if( pc->id.flags & UR_FLAG_EOL )
503            break;
504
505        ur_arrayReserve( str, 1, str->used + 1 );
506        str->ptr.c[ str->used ] = ' ';
507        ++str->used;
508
509        ur_toStr( pc, str, 0 );
510        ++pc;
511    }
512
513    ur_termCStr( str );
514
515    //statusBar()->showMessage( str->ptr.c );
516    _console->append( str->ptr.c );
517
518    _stack->update( ut, str );
519}
520
521
522int ThuneDebugger::monitorIgnore( UThread*, int, UCell*, UCell* )
523{
524    return 0;
525}
526
527
528int ThuneDebugger::monitorRun( UThread* ut, int cmd, UCell* pc, UCell* end )
529{
530    switch( cmd )
531    {
532        case UR_EMH_STEP:
533            gDB->_runCycle++;
534            if( (gDB->_runCycle & 127) == 0 )
535            {
536                qApp->processEvents();
537                if( gDB->_interrupt )
538                {
539                    UR_CALL_OP = OP_QUIT;
540                    return 1;
541                }
542            }
543            break;
544
545        case UR_EMH_HALT:
546            gDB->setMode( kModeStep );
547
548            gDB->showPC( ut, pc, end );
549            int ret = gDB->_loop->exec();
550            if( ret < 0 )
551            {
552                UR_CALL_OP = OP_QUIT;
553                return 1;
554            }
555            break;
556    }
557    return 0;
558}
559
560
561int ThuneDebugger::monitorStep( UThread* ut, int cmd, UCell* pc, UCell* end )
562{
563    switch( cmd )
564    {
565        case UR_EMH_STEP:
566            gDB->showPC( ut, pc, end );
567            int ret = gDB->_loop->exec();
568            //printf( "KR ret %d\n", ret );
569            if( ret < 0 )
570            {
571                UR_CALL_OP = OP_QUIT;
572                return 1;
573            }
574            break;
575    }
576    return 0;
577}
578
579
580extern "C" void ur_dprint( const char* fmt, ... )
581{
582#define DBUF_LEN 256
583    va_list arg;
584    char buf[ DBUF_LEN ];
585
586    va_start( arg, fmt );
587    vsnprintf( buf, DBUF_LEN, fmt, arg );
588    va_end( arg );
589
590    buf[ DBUF_LEN - 1 ] = '\0';
591    gConsole->append( buf );
592}
593
594
595void ThuneDebugger::setMode( int mode )
596{
597    switch( mode )
598    {
599        case kModeDone:
600            _mode = kModeDone;
601            _env.monitor = monitorIgnore;
602            _actRun->setEnabled( true );
603            _actStep->setEnabled( true );
604            _actHalt->setEnabled( false );
605            break;
606
607        case kModeRun:
608            _mode = kModeRun;
609            _env.monitor = monitorRun;
610            _actRun->setEnabled( false );
611            _actStep->setEnabled( false );
612            _actHalt->setEnabled( true );
613            break;
614
615        case kModeStep:
616            _mode = kModeStep;
617            _env.monitor = monitorStep;
618            _actRun->setEnabled( true );
619            _actStep->setEnabled( true );
620            _actHalt->setEnabled( false );
621            break;
622    }
623}
624
625
626bool ThuneDebugger::createEnv()
627{
628    destroyEnv();
629
630    _runCycle = 0;
631    _interrupt = false;
632
633    gConsole = _console;
634    _console->clear();
635
636    _stack->clear();
637
638    _env.monitor = monitorIgnore;
639
640    if( ur_startup( &_env, 0, 0 ) )
641        return false;
642
643    return true;
644}
645
646
647void ThuneDebugger::destroyEnv()
648{
649    if( _env.threads )
650        ur_shutdown( &_env );
651}
652
653
654void ThuneDebugger::eval()
655{
656    UCell* res;
657    QByteArray cmd;
658    UThread* ut = _env.threads;
659
660    cmd = _editor->toPlainText().toAscii();
661
662    switch( ur_evalCStr( ut, cmd, cmd.size() ) )
663    {
664        case UR_EVAL_OK:
665        {
666            res = ur_result( ut, 0 );
667
668            if( ! ur_is(res, UT_UNSET) &&
669                ! ur_is(res, UT_CONTEXT) &&
670                ! ur_is(res, UT_FUNCTION) )
671            {
672                UString str;
673
674                ur_arrayInit( &str, 1, 0 );
675                ur_toStr( res, &str, 0 );
676                if( str.ptr.c )
677                {
678                    UString* sp = &str;
679                    ur_termCStr( sp );
680
681                    _console->append( str.ptr.c );
682                    //printf( "== %s\n", str.ptr.c );
683                }
684                ur_arrayFree( &str );
685            }
686
687            setMode( kModeDone );
688            statusBar()->showMessage(tr("Finished"), 2000);
689        }
690            break;
691
692        case UR_EVAL_HALT:
693            break;
694
695        case UR_EVAL_QUIT:
696            _mode = kModeDone;
697            break;
698
699        case UR_EVAL_ERROR:
700        {
701            UString* str;
702
703            res = ur_result( ut, 0 );
704            str = ur_binPtr( res->err.messageStr );
705
706            _console->append( "Error!" );
707            _console->append( str->ptr.c );
708        }
709            break;
710    }
711}
712
713
714void ThuneDebugger::dbRun()
715{
716    switch( _mode )
717    {
718        case kModeVoid:
719        case kModeDone:
720            if( createEnv() )
721            {
722                setMode( kModeRun );
723                statusBar()->showMessage(tr("Running..."), 2000);
724                eval();
725            }
726            break;
727
728        case kModeStep:
729            setMode( kModeRun );
730            gDB->_loop->exit( 0 );
731            break;
732    }
733}
734
735
736void ThuneDebugger::dbStep()
737{
738    switch( _mode )
739    {
740        case kModeVoid:
741        case kModeDone:
742            if( createEnv() )
743            {
744                setMode( kModeStep );
745                statusBar()->showMessage(tr("Stepping..."), 2000);
746                eval();
747            }
748            break;
749
750        case kModeStep:
751            gDB->_loop->exit( 0 );
752            break;
753    }
754}
755
756
757void ThuneDebugger::dbHalt()
758{
759    switch( _mode )
760    {
761        case kModeRun:
762            _interrupt = true;
763            break;
764    }
765}
766
767
768//EOF
Note: See TracBrowser for help on using the browser.