$search
00001 /* 00002 Aseba - an event-based framework for distributed robot control 00003 Copyright (C) 2007--2012: 00004 Stephane Magnenat <stephane at magnenat dot net> 00005 (http://stephane.magnenat.net) 00006 and other contributors, see authors.txt for details 00007 00008 This program is free software: you can redistribute it and/or modify 00009 it under the terms of the GNU Lesser General Public License as published 00010 by the Free Software Foundation, version 3 of the License. 00011 00012 This program is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 GNU Lesser General Public License for more details. 00016 00017 You should have received a copy of the GNU Lesser General Public License 00018 along with this program. If not, see <http://www.gnu.org/licenses/>. 00019 */ 00020 00021 #include "MainWindow.h" 00022 #include "ClickableLabel.h" 00023 #include "DashelTarget.h" 00024 #include "TargetModels.h" 00025 #include "NamedValuesVectorModel.h" 00026 #include "AeslEditor.h" 00027 #include "EventViewer.h" 00028 #include "FindDialog.h" 00029 #include "ModelAggregator.h" 00030 #include "translations/CompilerTranslator.h" 00031 #include "../common/consts.h" 00032 #include "../common/productids.h" 00033 #include "../utils/utils.h" 00034 #include <QtGui> 00035 #include <QtXml> 00036 #include <sstream> 00037 #include <iostream> 00038 #include <cassert> 00039 #include <QTabWidget> 00040 #include <QtConcurrentRun> 00041 00042 #include <MainWindow.moc> 00043 #include <version.h> 00044 00045 #include <iostream> 00046 00047 using std::copy; 00048 00049 00050 namespace Aseba 00051 { 00052 // /** \addtogroup studio */ 00055 CompilationLogDialog::CompilationLogDialog(QWidget *parent) : 00056 QTextEdit(parent) 00057 { 00058 QFont font; 00059 font.setFamily(""); 00060 font.setStyleHint(QFont::TypeWriter); 00061 font.setFixedPitch(true); 00062 font.setPointSize(10); 00063 00064 setFont(font); 00065 setTabStopWidth( QFontMetrics(font).width(' ') * 4); 00066 setReadOnly(true); 00067 00068 setWindowTitle(tr("Aseba Studio: Output of last compilation")); 00069 00070 resize(600, 560); 00071 } 00072 00073 void CompilationLogDialog::hideEvent( QHideEvent * event ) 00074 { 00075 if (!isVisible()) 00076 emit hidden(); 00077 } 00078 00080 00081 EditorsPlotsTabWidget::EditorsPlotsTabWidget() 00082 { 00083 vmMemorySize[0] = -1; // not yet initialized 00084 vmMemorySize[1] = -1; // not yet initialized 00085 readSettings(); // read user's preferences 00086 connect(this, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); 00087 } 00088 00089 EditorsPlotsTabWidget::~EditorsPlotsTabWidget() 00090 { 00091 // store user's preferences 00092 writeSettings(); 00093 } 00094 00095 void EditorsPlotsTabWidget::addTab(QWidget* widget, const QString& label, bool closable) 00096 { 00097 const int index = QTabWidget::addTab(widget, label); 00098 #if QT_VERSION >= 0x040500 00099 if (closable) 00100 { 00101 QPushButton* button = new QPushButton(QIcon(":/images/remove.png"), ""); 00102 button->setFlat(true); 00103 connect(button, SIGNAL(clicked(bool)), this, SLOT(removeAndDeleteTab())); 00104 tabBar()->setTabButton(index, QTabBar::RightSide, button); 00105 } 00106 #endif // QT_VERSION >= 0x040500 00107 00108 // manage the sections size for the vmMemoryView child widget 00109 NodeTab* tab = dynamic_cast<NodeTab*>(widget); 00110 if (tab) 00111 { 00112 vmMemoryViewResize(tab); 00113 connect(tab->vmMemoryView->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(vmMemoryResized(int,int,int))); 00114 } 00115 } 00116 00117 void EditorsPlotsTabWidget::highlightTab(int index, QColor color) 00118 { 00119 tabBar()->setTabTextColor(index, color); 00120 } 00121 00122 void EditorsPlotsTabWidget::setExecutionMode(int index, Target::ExecutionMode state) 00123 { 00124 if (state == Target::EXECUTION_UNKNOWN) 00125 { 00126 setTabIcon(index, QIcon()); 00127 } 00128 else if (state == Target::EXECUTION_RUN) 00129 { 00130 setTabIcon(index, QIcon(":/images/play.png")); 00131 } 00132 else if (state == Target::EXECUTION_STEP_BY_STEP) 00133 { 00134 setTabIcon(index, QIcon(":/images/pause.png")); 00135 } 00136 else if (state == Target::EXECUTION_STOP) 00137 { 00138 setTabIcon(index, QIcon(":/images/stop.png")); 00139 } 00140 } 00141 00142 void EditorsPlotsTabWidget::removeAndDeleteTab(int index) 00143 { 00144 #if QT_VERSION >= 0x040500 00145 if (index < 0) 00146 { 00147 QWidget* button(polymorphic_downcast<QWidget*>(sender())); 00148 for (int i = 0; i < count(); ++i) 00149 { 00150 if (tabBar()->tabButton(i, QTabBar::RightSide) == button) 00151 { 00152 index = i; 00153 break; 00154 } 00155 } 00156 } 00157 #endif // QT_VERSION >= 0x040500 00158 00159 if (index >= 0) 00160 { 00161 QWidget* w(widget(index)); 00162 QTabWidget::removeTab(index); 00163 w->deleteLater(); 00164 } 00165 } 00166 00167 void EditorsPlotsTabWidget::vmMemoryResized(int col, int oldSize, int newSize) 00168 { 00169 // keep track of the current value, to apply it on the other tabs 00170 vmMemorySize[col] = newSize; 00171 } 00172 00173 void EditorsPlotsTabWidget::tabChanged(int index) 00174 { 00175 // resize the vmMemoryView, to match the user choice 00176 NodeTab* tab = dynamic_cast<NodeTab*>(currentWidget()); 00177 if (!tab) 00178 return; 00179 00180 vmMemoryViewResize(tab); 00181 // reset the tab highlight 00182 resetHighlight(index); 00183 } 00184 00185 void EditorsPlotsTabWidget::resetHighlight(int index) 00186 { 00187 tabBar()->setTabTextColor(index, Qt::black); 00188 } 00189 00190 void EditorsPlotsTabWidget::vmMemoryViewResize(NodeTab *tab) 00191 { 00192 if (!tab) 00193 return; 00194 00195 // resize the vmMemoryView QTreeView, according to the global value 00196 for (int i = 0; i < 2; i++) 00197 if (vmMemorySize[i] != -1) 00198 tab->vmMemoryView->header()->resizeSection(i, vmMemorySize[i]); 00199 } 00200 00201 void EditorsPlotsTabWidget::readSettings() 00202 { 00203 QSettings settings; 00204 vmMemorySize[0] = settings.value("vmMemoryView/col0", -1).toInt(); 00205 vmMemorySize[1] = settings.value("vmMemoryView/col1", -1).toInt(); 00206 } 00207 00208 void EditorsPlotsTabWidget::writeSettings() 00209 { 00210 QSettings settings; 00211 settings.setValue("vmMemoryView/col0", vmMemorySize[0]); 00212 settings.setValue("vmMemoryView/col1", vmMemorySize[1]); 00213 } 00214 00216 00217 void ScriptTab::createEditor() 00218 { 00219 // editor widget 00220 editor = new AeslEditor(this); 00221 breakpoints = new AeslBreakpointSidebar(editor); 00222 linenumbers = new AeslLineNumberSidebar(editor); 00223 highlighter = new AeslHighlighter(editor, editor->document()); 00224 } 00225 00227 00228 AbsentNodeTab::AbsentNodeTab(const unsigned id, const QString& name, const QString& sourceCode, const SavedPlugins& savedPlugins) : 00229 ScriptTab(id), 00230 name(name), 00231 savedPlugins(savedPlugins) 00232 { 00233 createEditor(); 00234 editor->setReadOnly(true); 00235 editor->setPlainText(sourceCode); 00236 QVBoxLayout *layout = new QVBoxLayout; 00237 layout->addWidget(editor); 00238 setLayout(layout); 00239 } 00240 00242 00243 NodeTab::CompilationResult* compilationThread(const TargetDescription targetDescription, const CommonDefinitions commonDefinitions, QString source, bool dump); 00244 00245 NodeTab::NodeTab(MainWindow* mainWindow, Target *target, const CommonDefinitions *commonDefinitions, int id, QWidget *parent) : 00246 QSplitter(parent), 00247 ScriptTab(id), 00248 VariableListener(0), 00249 pid(ASEBA_PID_UNDEFINED), 00250 target(target), 00251 commonDefinitions(commonDefinitions), 00252 mainWindow(mainWindow), 00253 currentPC(0), 00254 previousMode(Target::EXECUTION_UNKNOWN), 00255 firstCompilation(true), 00256 showHidden(mainWindow->showHiddenAct->isChecked()), 00257 compilationDirty(false), 00258 isSynchronized(true) 00259 { 00260 // setup some variables 00261 rehighlighting = false; 00262 errorPos = -1; 00263 allocatedVariablesCount = 0; 00264 00265 // create models 00266 vmFunctionsModel = new TargetFunctionsModel(target->getDescription(id)); 00267 vmMemoryModel = new TargetVariablesModel(); 00268 variablesModel = vmMemoryModel; 00269 subscribeToVariableOfInterest(ASEBA_PID_VAR_NAME); 00270 00271 // create gui 00272 setupWidgets(); 00273 setupConnections(); 00274 00275 // create aggregated models 00276 // local and global events 00277 ModelAggregator* aggregator = new ModelAggregator(this); 00278 aggregator->addModel(vmLocalEvents->model()); 00279 aggregator->addModel(mainWindow->eventsDescriptionsModel); 00280 eventAggregator = aggregator; 00281 // variables and constants 00282 aggregator = new ModelAggregator(this); 00283 aggregator->addModel(vmMemoryModel); 00284 aggregator->addModel(mainWindow->constantsDefinitionsModel); 00285 variableAggregator = aggregator; 00286 00287 // create the sorting proxy 00288 sortingProxy = new QSortFilterProxyModel(this); 00289 sortingProxy->setDynamicSortFilter(true); 00290 sortingProxy->setSortCaseSensitivity(Qt::CaseInsensitive); 00291 sortingProxy->setSortRole(Qt::DisplayRole); 00292 00293 // create the chainsaw filter for native functions 00294 functionsFlatModel = new TreeChainsawFilter(this); 00295 functionsFlatModel->setSourceModel(vmFunctionsModel); 00296 00297 editor->setFocus(); 00298 setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); 00299 00300 // get the value of the variables 00301 // compile in this thread the first time 00302 NodeTab::CompilationResult* result = compilationThread(*target->getDescription(id), *commonDefinitions, editor->toPlainText(), false); 00303 processCompilationResult(result); 00304 } 00305 00306 NodeTab::~NodeTab() 00307 { 00308 // Note: this might memory leak, depending on when the signal/slots get disconnected 00309 // but this is a rare case and can be safely ignored 00310 compilationFuture.waitForFinished(); 00311 for (NodeToolInterfaces::const_iterator it(tools.begin()); it != tools.end(); ++it) 00312 { 00313 NodeToolInterface* tool(*it); 00314 if (!tool->surviveTabDestruction()) 00315 delete (*it); 00316 } 00317 delete vmFunctionsModel; 00318 delete vmMemoryModel; 00319 } 00320 00321 void NodeTab::timerEvent ( QTimerEvent * event ) 00322 { 00323 if (mainWindow->nodes->currentWidget() != this) 00324 return; 00325 00326 // only fetch what is visible 00327 const QList<TargetVariablesModel::Variable> variables(variablesModel->getVariables()); 00328 assert(variables.size() == variablesModel->rowCount()); 00329 00330 unsigned currentReqCount(0); 00331 unsigned currentReqPos(0); 00332 for (int i = 0; i < variables.size(); ++i) 00333 { 00334 const TargetVariablesModel::Variable& var(variables[i]); 00335 if (((var.value.size() == 1) || 00336 (vmMemoryView->isExpanded(variablesModel->index(i, 0))) 00337 ) && 00338 (!vmMemoryView->isRowHidden(i, QModelIndex())) 00339 ) 00340 { 00341 if (currentReqCount == 0) 00342 { 00343 // new request 00344 currentReqPos = var.pos; 00345 currentReqCount = var.value.size(); 00346 } 00347 else if (currentReqPos + currentReqCount == var.pos) 00348 { 00349 // continuous, append 00350 currentReqCount += var.value.size(); 00351 } 00352 else 00353 { 00354 // flush and reset 00355 target->getVariables(id, currentReqPos, currentReqCount); 00356 // new request 00357 currentReqPos = var.pos; 00358 currentReqCount = var.value.size(); 00359 } 00360 } 00361 } 00362 if (currentReqCount != 0) 00363 { 00364 // flush request 00365 target->getVariables(id, currentReqPos, currentReqCount); 00366 } 00367 } 00368 00369 void NodeTab::variableValueUpdated(const QString& name, const VariablesDataVector& values) 00370 { 00371 if ((name == ASEBA_PID_VAR_NAME) && (values.size() >= 1)) 00372 { 00373 // only regenerate if pid has changed 00374 if (values[0] != int(pid)) 00375 { 00376 pid = values[0]; 00377 nodeToolRegistrer.update(pid, this, tools); 00378 updateToolList(); 00379 00380 mainWindow->regenerateHelpMenu(); 00381 } 00382 } 00383 } 00384 00385 void NodeTab::setupWidgets() 00386 { 00387 createEditor(); 00388 00389 // editor related notification widgets 00390 cursorPosText = new QLabel; 00391 compilationResultImage = new ClickableLabel; 00392 compilationResultText = new ClickableLabel; 00393 compilationResultText->setWordWrap(true); 00394 compilationResultText->setAlignment(Qt::AlignRight|Qt::AlignVCenter); 00395 QHBoxLayout *compilationResultLayout = new QHBoxLayout; 00396 compilationResultLayout->addWidget(cursorPosText); 00397 compilationResultLayout->addWidget(compilationResultText, 1000); 00398 compilationResultLayout->addWidget(compilationResultImage); 00399 00400 // memory usage notification area 00401 memoryUsageText = new QLabel(); 00402 memoryUsageText->setAlignment(Qt::AlignLeft); 00403 00404 // editor area 00405 QHBoxLayout *editorAreaLayout = new QHBoxLayout; 00406 editorAreaLayout->setSpacing(0); 00407 editorAreaLayout->addWidget(breakpoints); 00408 editorAreaLayout->addWidget(linenumbers); 00409 editorAreaLayout->addWidget(editor); 00410 00411 // keywords 00412 keywordsToolbar = new QToolBar(); 00413 varButton = new QToolButton(); varButton->setText("var"); 00414 ifButton = new QToolButton(); ifButton->setText("if"); 00415 elseifButton = new QToolButton(); elseifButton->setText("elseif"); 00416 elseButton = new QToolButton(); elseButton->setText("else"); 00417 oneventButton = new QToolButton(); oneventButton->setText("onevent"); 00418 whileButton = new QToolButton(); whileButton->setText("while"); 00419 forButton = new QToolButton(); forButton->setText("for"); 00420 subroutineButton = new QToolButton(); subroutineButton->setText("sub"); 00421 callsubButton = new QToolButton(); callsubButton->setText("callsub"); 00422 keywordsToolbar->addWidget(new QLabel(tr("<b>Keywords</b>"))); 00423 keywordsToolbar->addSeparator(); 00424 keywordsToolbar->addWidget(varButton); 00425 keywordsToolbar->addWidget(ifButton); 00426 keywordsToolbar->addWidget(elseifButton); 00427 keywordsToolbar->addWidget(elseButton); 00428 keywordsToolbar->addWidget(oneventButton); 00429 keywordsToolbar->addWidget(whileButton); 00430 keywordsToolbar->addWidget(forButton); 00431 keywordsToolbar->addWidget(subroutineButton); 00432 keywordsToolbar->addWidget(callsubButton); 00433 00434 QVBoxLayout *editorLayout = new QVBoxLayout; 00435 editorLayout->addWidget(keywordsToolbar); 00436 editorLayout->addLayout(editorAreaLayout); 00437 editorLayout->addLayout(compilationResultLayout); 00438 editorLayout->addWidget(memoryUsageText); 00439 00440 // panel 00441 00442 // buttons 00443 executionModeLabel = new QLabel(tr("unknown")); 00444 00445 loadButton = new QPushButton(QIcon(":/images/upload.png"), tr("Load")); 00446 resetButton = new QPushButton(QIcon(":/images/reset.png"), tr("Reset")); 00447 resetButton->setEnabled(false); 00448 runInterruptButton = new QPushButton(QIcon(":/images/play.png"), tr("Run")); 00449 runInterruptButton->setEnabled(false); 00450 nextButton = new QPushButton(QIcon(":/images/step.png"), tr("Next")); 00451 nextButton->setEnabled(false); 00452 refreshMemoryButton = new QPushButton(QIcon(":/images/rescan.png"), tr("refresh")); 00453 autoRefreshMemoryCheck = new QCheckBox(tr("auto")); 00454 00455 QGridLayout* buttonsLayout = new QGridLayout; 00456 buttonsLayout->addWidget(new QLabel(tr("<b>Execution</b>")), 0, 0); 00457 buttonsLayout->addWidget(executionModeLabel, 0, 1); 00458 buttonsLayout->addWidget(loadButton, 1, 0); 00459 buttonsLayout->addWidget(runInterruptButton, 1, 1); 00460 buttonsLayout->addWidget(resetButton, 2, 0); 00461 buttonsLayout->addWidget(nextButton, 2, 1); 00462 00463 // memory 00464 vmMemoryView = new QTreeView; 00465 vmMemoryView->setModel(vmMemoryModel); 00466 vmMemoryView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); 00467 vmMemoryView->setItemDelegate(new SpinBoxDelegate(-32768, 32767, this)); 00468 vmMemoryView->setColumnWidth(0, 200-QFontMetrics(QFont()).width("-8888888##")); 00469 vmMemoryView->setColumnWidth(1, QFontMetrics(QFont()).width("-8888888##")); 00470 vmMemoryView->setSelectionMode(QAbstractItemView::SingleSelection); 00471 vmMemoryView->setSelectionBehavior(QAbstractItemView::SelectItems); 00472 vmMemoryView->setDragDropMode(QAbstractItemView::DragOnly); 00473 vmMemoryView->setDragEnabled(true); 00474 //vmMemoryView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 00475 //vmMemoryView->setHeaderHidden(true); 00476 00477 QVBoxLayout *memoryLayout = new QVBoxLayout; 00478 QHBoxLayout *memorySubLayout = new QHBoxLayout; 00479 memorySubLayout->addWidget(new QLabel(tr("<b>Variables</b>"))); 00480 memorySubLayout->addStretch(); 00481 memorySubLayout->addWidget(autoRefreshMemoryCheck); 00482 memorySubLayout->addWidget(refreshMemoryButton); 00483 memoryLayout->addLayout(memorySubLayout); 00484 memoryLayout->addWidget(vmMemoryView); 00485 memorySubLayout = new QHBoxLayout; 00486 QLabel* filterLabel(new QLabel(tr("F&ilter:"))); 00487 memorySubLayout->addWidget(filterLabel); 00488 vmMemoryFilter= new QLineEdit; 00489 filterLabel->setBuddy(vmMemoryFilter); 00490 memorySubLayout->addWidget(vmMemoryFilter); 00491 memoryLayout->addLayout(memorySubLayout); 00492 00493 // functions 00494 vmFunctionsView = new QTreeView; 00495 vmFunctionsView->setMinimumHeight(40); 00496 vmFunctionsView->setMinimumSize(QSize(50,40)); 00497 vmFunctionsView->setModel(vmFunctionsModel); 00498 vmFunctionsView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); 00499 vmFunctionsView->setSelectionMode(QAbstractItemView::SingleSelection); 00500 vmFunctionsView->setSelectionBehavior(QAbstractItemView::SelectItems); 00501 vmFunctionsView->setDragDropMode(QAbstractItemView::DragOnly); 00502 vmFunctionsView->setDragEnabled(true); 00503 vmFunctionsView->setEditTriggers(QAbstractItemView::NoEditTriggers); 00504 vmFunctionsView->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); 00505 #if QT_VERSION >= 0x040400 00506 vmFunctionsView->setHeaderHidden(true); 00507 #elif (!defined(_MSC_VER)) 00508 #warning "Some feature have been disabled because you are using Qt < 4.4.0" 00509 #endif 00510 00511 // local events 00512 vmLocalEvents = new DraggableListWidget; 00513 vmLocalEvents->setMinimumHeight(40); 00514 vmLocalEvents->setSelectionMode(QAbstractItemView::SingleSelection); 00515 vmLocalEvents->setDragDropMode(QAbstractItemView::DragOnly); 00516 vmLocalEvents->setDragEnabled(true); 00517 for (size_t i = 0; i < target->getDescription(id)->localEvents.size(); i++) 00518 { 00519 QListWidgetItem* item = new QListWidgetItem(QString::fromStdWString(target->getDescription(id)->localEvents[i].name)); 00520 item->setToolTip(QString::fromStdWString(target->getDescription(id)->localEvents[i].description)); 00521 vmLocalEvents->addItem(item); 00522 } 00523 00524 // toolbox 00525 toolBox = new QToolBox; 00526 toolBox->addItem(vmFunctionsView, tr("Native Functions")); 00527 toolBox->addItem(vmLocalEvents, tr("Local Events")); 00528 QWidget* toolListWidget(new QWidget); 00529 toolListLayout = new QVBoxLayout; 00530 toolListWidget->setLayout(toolListLayout); 00531 toolListIndex = toolBox->addItem(toolListWidget, tr("Local Tools")); 00532 QVBoxLayout* toolBoxLayout = new QVBoxLayout; 00533 toolBoxLayout->addWidget(toolBox); 00534 QWidget* toolBoxWidget = new QWidget; 00535 toolBoxWidget->setLayout(toolBoxLayout); 00536 00537 // panel 00538 QSplitter *panelSplitter = new QSplitter(Qt::Vertical); 00539 00540 QWidget* buttonsWidget = new QWidget; 00541 buttonsWidget->setLayout(buttonsLayout); 00542 panelSplitter->addWidget(buttonsWidget); 00543 panelSplitter->setCollapsible(0, false); 00544 00545 QWidget* memoryWidget = new QWidget; 00546 memoryWidget->setLayout(memoryLayout); 00547 panelSplitter->addWidget(memoryWidget); 00548 panelSplitter->setStretchFactor(1, 3); 00549 00550 panelSplitter->addWidget(toolBoxWidget); 00551 panelSplitter->setStretchFactor(2, 1); 00552 00553 addWidget(panelSplitter); 00554 QWidget *editorWidget = new QWidget; 00555 editorWidget->setLayout(editorLayout); 00556 addWidget(editorWidget); 00557 setSizes(QList<int>() << 250 << 550); 00558 } 00559 00560 void NodeTab::setupConnections() 00561 { 00562 // compiler 00563 connect(&compilationWatcher, SIGNAL(finished()), SLOT(compilationCompleted())); 00564 00565 // execution 00566 connect(loadButton, SIGNAL(clicked()), SLOT(loadClicked())); 00567 connect(resetButton, SIGNAL(clicked()), SLOT(resetClicked())); 00568 connect(runInterruptButton, SIGNAL(clicked()), SLOT(runInterruptClicked())); 00569 connect(nextButton, SIGNAL(clicked()), SLOT(nextClicked())); 00570 connect(refreshMemoryButton, SIGNAL(clicked()), SLOT(refreshMemoryClicked())); 00571 connect(autoRefreshMemoryCheck, SIGNAL(stateChanged(int)), SLOT(autoRefreshMemoryClicked(int))); 00572 00573 // memory 00574 connect(vmMemoryModel, SIGNAL(variableValuesChanged(unsigned, const VariablesDataVector &)), SLOT(setVariableValues(unsigned, const VariablesDataVector &))); 00575 connect(vmMemoryFilter, SIGNAL(textChanged(const QString &)), SLOT(updateHidden())); 00576 00577 // editor 00578 connect(editor, SIGNAL(textChanged()), SLOT(editorContentChanged())); 00579 connect(editor, SIGNAL(cursorPositionChanged() ), SLOT(cursorMoved())); 00580 connect(editor, SIGNAL(breakpointSet(unsigned)), SLOT(setBreakpoint(unsigned))); 00581 connect(editor, SIGNAL(breakpointCleared(unsigned)), SLOT(clearBreakpoint(unsigned))); 00582 connect(editor, SIGNAL(breakpointClearedAll()), SLOT(breakpointClearedAll())); 00583 connect(editor, SIGNAL(refreshModelRequest(LocalContext)), SLOT(refreshCompleterModel(LocalContext))); 00584 00585 connect(compilationResultImage, SIGNAL(clicked()), SLOT(goToError())); 00586 connect(compilationResultText, SIGNAL(clicked()), SLOT(goToError())); 00587 00588 // tools plugins 00589 connect(mainWindow, SIGNAL(MainWindowClosed()), SLOT(closePlugins())); 00590 00591 // keywords 00592 signalMapper = new QSignalMapper(this); 00593 signalMapper->setMapping(varButton, QString("var ")); 00594 signalMapper->setMapping(ifButton, QString("if")); 00595 signalMapper->setMapping(elseifButton, QString("elseif")); 00596 signalMapper->setMapping(elseButton, QString("else\n\t")); 00597 signalMapper->setMapping(oneventButton, QString("onevent ")); 00598 signalMapper->setMapping(whileButton, QString("while")); 00599 signalMapper->setMapping(forButton, QString("for")); 00600 signalMapper->setMapping(subroutineButton, QString("sub ")); 00601 signalMapper->setMapping(callsubButton, QString("callsub ")); 00602 00603 connect(varButton, SIGNAL(clicked()), signalMapper, SLOT(map())); 00604 connect(ifButton, SIGNAL(clicked()), signalMapper, SLOT(map())); 00605 connect(elseifButton, SIGNAL(clicked()), signalMapper, SLOT(map())); 00606 connect(elseButton, SIGNAL(clicked()), signalMapper, SLOT(map())); 00607 connect(oneventButton, SIGNAL(clicked()), signalMapper, SLOT(map())); 00608 connect(whileButton, SIGNAL(clicked()), signalMapper, SLOT(map())); 00609 connect(forButton, SIGNAL(clicked()), signalMapper, SLOT(map())); 00610 connect(subroutineButton, SIGNAL(clicked()), signalMapper, SLOT(map())); 00611 connect(callsubButton, SIGNAL(clicked()), signalMapper, SLOT(map())); 00612 00613 connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(keywordClicked(QString))); 00614 00615 // following default settings 00616 if (mainWindow->autoMemoryRefresh) 00617 autoRefreshMemoryCheck->setChecked(true); 00618 } 00619 00620 ScriptTab::SavedPlugins NodeTab::savePlugins() const 00621 { 00622 SavedPlugins savedPlugins; 00623 for (NodeToolInterfaces::const_iterator it(tools.begin()); it != tools.end(); ++it) 00624 { 00625 const SavedContent savedContent((*it)->getSaved()); 00626 if (!savedContent.first.isEmpty() && !savedContent.second.isNull()) 00627 savedPlugins.push_back(savedContent); 00628 } 00629 return savedPlugins; 00630 } 00631 00632 void NodeTab::restorePlugins(const SavedPlugins& savedPlugins, bool fromFile) 00633 { 00634 // first recreate plugins 00635 for (SavedPlugins::const_iterator it(savedPlugins.begin()); it != savedPlugins.end(); ++it) 00636 { 00637 nodeToolRegistrer.update(it->first, this, tools); 00638 } 00639 // then reload data 00640 for (SavedPlugins::const_iterator it(savedPlugins.begin()); it != savedPlugins.end(); ++it) 00641 { 00642 NodeToolInterface* interface(tools.getNamed(it->first)); 00643 interface->loadFromDom(it->second, fromFile); 00644 } 00645 } 00646 00647 void NodeTab::updateToolList() 00648 { 00649 // delete menu entries 00650 int oldCount = toolListLayout->count(); 00651 QLayoutItem *child; 00652 while ((child = toolListLayout->takeAt(0)) != 0) 00653 { 00654 child->widget()->deleteLater(); 00655 delete child; 00656 } 00657 00658 // generate menu entries 00659 for (NodeToolInterfaces::const_iterator it(tools.begin()); it != tools.end(); ++it) 00660 toolListLayout->addWidget((*it)->createMenuEntry()); 00661 00662 // if elements were added, ensure that this tab is visible 00663 if (toolListLayout->count() != oldCount) 00664 toolBox->setCurrentIndex(toolListIndex); 00665 } 00666 00667 void NodeTab::resetClicked() 00668 { 00669 clearExecutionErrors(); 00670 target->reset(id); 00671 } 00672 00673 void NodeTab::loadClicked() 00674 { 00675 if (errorPos == -1) 00676 { 00677 clearEditorProperty("executionError"); 00678 target->uploadBytecode(id, bytecode); 00679 //target->getVariables(id, 0, allocatedVariablesCount); 00680 editor->debugging = true; 00681 reSetBreakpoints(); 00682 rehighlight(); 00683 } 00684 00685 isSynchronized = true; 00686 mainWindow->resetStatusText(); 00687 } 00688 00689 void NodeTab::runInterruptClicked() 00690 { 00691 if (runInterruptButton->text() == tr("Run")) 00692 target->run(id); 00693 else 00694 target->pause(id); 00695 } 00696 00697 void NodeTab::nextClicked() 00698 { 00699 target->next(id); 00700 } 00701 00702 void NodeTab::refreshMemoryClicked() 00703 { 00704 // as we explicitely clicked, refresh all variables 00705 target->getVariables(id, 0, allocatedVariablesCount); 00706 } 00707 00708 void NodeTab::autoRefreshMemoryClicked(int state) 00709 { 00710 if (state == Qt::Checked) 00711 { 00712 refreshMemoryButton->setEnabled(false); 00713 refreshTimer = startTimer(200); // 5 Hz 00714 } 00715 else 00716 { 00717 refreshMemoryButton->setEnabled(true); 00718 killTimer(refreshTimer); 00719 refreshMemoryClicked(); 00720 } 00721 } 00722 00723 void NodeTab::writeBytecode() 00724 { 00725 if (errorPos == -1) 00726 { 00727 loadClicked(); 00728 target->writeBytecode(id); 00729 } 00730 } 00731 00732 void NodeTab::reboot() 00733 { 00734 markTargetUnsynced(); 00735 target->reboot(id); 00736 } 00737 00738 static void write16(QIODevice& dev, const uint16 v) 00739 { 00740 dev.write((const char*)&v, 2); 00741 } 00742 00743 static void write16(QIODevice& dev, const VariablesDataVector& data, const char *varName) 00744 { 00745 if (data.empty()) 00746 { 00747 std::cerr << "Warning, cannot find " << varName << " required to save bytecode, using 0" << std::endl; 00748 write16(dev, 0); 00749 } 00750 else 00751 dev.write((const char *)&data[0], 2); 00752 } 00753 00754 static uint16 crcXModem(const uint16 oldCrc, const QString& s) 00755 { 00756 return crcXModem(oldCrc, s.toStdWString()); 00757 } 00758 00759 void NodeTab::saveBytecode() 00760 { 00761 const QString& nodeName(target->getName(id)); 00762 QString bytecodeFileName = QFileDialog::getSaveFileName(mainWindow, tr("Save the binary code of %0").arg(nodeName), "", "Aseba Binary Object (*.abo);;All Files (*)"); 00763 00764 QFile file(bytecodeFileName); 00765 if (!file.open(QFile::WriteOnly | QFile::Truncate)) 00766 return; 00767 00768 // See AS001 at https://aseba.wikidot.com/asebaspecifications 00769 00770 // header 00771 const char* magic = "ABO"; 00772 file.write(magic, 4); 00773 write16(file, 0); // binary format version 00774 write16(file, ASEBA_PROTOCOL_VERSION); 00775 write16(file, vmMemoryModel->getVariableValue("_productId"), "product identifier (_productId)"); 00776 write16(file, vmMemoryModel->getVariableValue("_fwversion"), "firmware version (_fwversion)"); 00777 write16(file, id); 00778 write16(file, crcXModem(0, nodeName)); 00779 write16(file, target->getDescription(id)->crc()); 00780 00781 // bytecode 00782 write16(file, bytecode.size()); 00783 uint16 crc(0); 00784 for (size_t i = 0; i < bytecode.size(); ++i) 00785 { 00786 const uint16 bc(bytecode[i]); 00787 write16(file, bc); 00788 crc = crcXModem(crc, bc); 00789 } 00790 write16(file, crc); 00791 } 00792 00793 void NodeTab::setVariableValues(unsigned index, const VariablesDataVector &values) 00794 { 00795 target->setVariables(id, index, values); 00796 } 00797 00798 void NodeTab::insertVariableName(const QModelIndex &index) 00799 { 00800 // only top level names have to be inserted 00801 if (!index.parent().isValid() && (index.column() == 0)) 00802 editor->insertPlainText(index.data().toString()); 00803 } 00804 00805 void NodeTab::editorContentChanged() 00806 { 00807 if (rehighlighting) 00808 { 00809 rehighlighting = false; 00810 } 00811 else 00812 { 00813 00814 QTextCursor cursor(editor->textCursor()); 00815 if (ConfigDialog::getAutoCompletion() && cursor.atBlockEnd()) 00816 { 00817 // language completion 00818 const QString& line(cursor.block().text()); 00819 QString keyword(line); 00820 00821 // make sure the string does not have any trailing space 00822 int nonWhitespace(0); 00823 while ((nonWhitespace < keyword.size()) && ((keyword.at(nonWhitespace) == ' ') || (keyword.at(nonWhitespace) == '\t'))) 00824 ++nonWhitespace; 00825 keyword.remove(0,nonWhitespace); 00826 00827 if (!keyword.trimmed().isEmpty()) 00828 { 00829 QString prefix; 00830 QString postfix; 00831 if (keyword == "if") 00832 { 00833 const QString headSpace = line.left(line.indexOf("if")); 00834 prefix = " "; 00835 postfix = " then\n" + headSpace + "\t\n" + headSpace + "end"; 00836 } 00837 else if (keyword == "when") 00838 { 00839 const QString headSpace = line.left(line.indexOf("when")); 00840 prefix = " "; 00841 postfix = " do\n" + headSpace + "\t\n" + headSpace + "end"; 00842 } 00843 else if (keyword == "for") 00844 { 00845 const QString headSpace = line.left(line.indexOf("for")); 00846 prefix = " "; 00847 postfix = "i in 0:0 do\n" + headSpace + "\t\n" + headSpace + "end"; 00848 } 00849 else if (keyword == "while") 00850 { 00851 const QString headSpace = line.left(line.indexOf("while")); 00852 prefix = " "; 00853 postfix = " do\n" + headSpace + "\t\n" + headSpace + "end"; 00854 } 00855 else if ((keyword == "else") && cursor.block().next().isValid()) 00856 { 00857 const QString tab = QString("\t"); 00858 QString headSpace = line.left(line.indexOf("else")); 00859 00860 if( headSpace.size() >= tab.size()) 00861 { 00862 headSpace = headSpace.left(headSpace.size() - tab.size()); 00863 if (cursor.block().next().text() == headSpace + "end") 00864 { 00865 prefix = "\n" + headSpace + "else"; 00866 postfix = "\n" + headSpace + "\t"; 00867 00868 cursor.select(QTextCursor::BlockUnderCursor); 00869 cursor.removeSelectedText(); 00870 } 00871 } 00872 } 00873 else if (keyword == "elseif") 00874 { 00875 const QString headSpace = line.left(line.indexOf("elseif")); 00876 prefix = " "; 00877 postfix = " then"; 00878 } 00879 00880 if (!prefix.isNull() || !postfix.isNull()) 00881 { 00882 cursor.beginEditBlock(); 00883 cursor.insertText(prefix); 00884 const int pos = cursor.position(); 00885 cursor.insertText(postfix); 00886 cursor.setPosition(pos); 00887 cursor.endEditBlock(); 00888 editor->setTextCursor(cursor); 00889 } 00890 } 00891 } 00892 recompile(); 00893 if (!firstCompilation) 00894 mainWindow->sourceChanged(); 00895 else 00896 firstCompilation = false; 00897 } 00898 } 00899 00900 void NodeTab::keywordClicked(QString keyword) 00901 { 00902 QTextCursor cursor(editor->textCursor()); 00903 00904 cursor.beginEditBlock(); 00905 cursor.insertText(keyword); 00906 cursor.endEditBlock(); 00907 editorContentChanged(); 00908 } 00909 00910 void NodeTab::displayCode(QList<QString> code, int line) 00911 { 00912 editor->clear(); 00913 QTextCharFormat format; 00914 QTextCursor cursor(editor->textCursor()); 00915 int pos=0; 00916 00917 for(int i=0; i<code.size(); i++) 00918 { 00919 if( i == line ) 00920 { 00921 format.setBackground(QBrush(QColor(255,255,200))); 00922 cursor.insertText(code[i], format); 00923 pos = cursor.position(); 00924 } 00925 else 00926 { 00927 format.setBackground(QBrush(Qt::white)); 00928 cursor.insertText(code[i], format); 00929 } 00930 } 00931 00932 cursor.setPosition(pos); 00933 editor->setTextCursor(cursor); 00934 editor->ensureCursorVisible(); 00935 } 00936 00937 void NodeTab::showKeywords(bool show) 00938 { 00939 if(show) 00940 keywordsToolbar->show(); 00941 else 00942 keywordsToolbar->hide(); 00943 } 00944 00945 void NodeTab::showMemoryUsage(bool show) 00946 { 00947 memoryUsageText->setVisible(show); 00948 } 00949 00950 void NodeTab::updateHidden() 00951 { 00952 const QString& filterString(vmMemoryFilter->text()); 00953 const QRegExp filterRegexp(filterString); 00954 // Quick hack to hide hidden variable in the treeview and not in vmMemoryModel 00955 // FIXME use a model proxy to perform this task 00956 for(int i = 0; i < vmMemoryModel->rowCount(QModelIndex()); i++) 00957 { 00958 QString name(vmMemoryModel->data(vmMemoryModel->index(i,0), Qt::DisplayRole).toString()); 00959 bool hidden(false); 00960 if ( 00961 (!showHidden && (name.at(0) == '_' || name.contains(QString("._")))) || 00962 (!filterString.isEmpty() && name.indexOf(filterRegexp)==-1) 00963 ) 00964 hidden = true; 00965 vmMemoryView->setRowHidden(i,QModelIndex(), hidden); 00966 } 00967 00968 } 00969 00970 NodeTab::CompilationResult* compilationThread(const TargetDescription targetDescription, const CommonDefinitions commonDefinitions, QString source, bool dump) 00971 { 00972 NodeTab::CompilationResult* result(new NodeTab::CompilationResult(dump)); 00973 00974 Compiler compiler; 00975 compiler.setTargetDescription(&targetDescription); 00976 compiler.setCommonDefinitions(&commonDefinitions); 00977 compiler.setTranslateCallback(CompilerTranslator::translate); 00978 00979 std::wistringstream is(source.toStdWString()); 00980 00981 if (dump) 00982 result->success = compiler.compile(is, result->bytecode, result->allocatedVariablesCount, result->error, &result->compilationMessages); 00983 else 00984 result->success = compiler.compile(is, result->bytecode, result->allocatedVariablesCount, result->error); 00985 00986 if (result->success) 00987 result->variablesMap = *compiler.getVariablesMap(); 00988 00989 return result; 00990 } 00991 00992 void NodeTab::recompile() 00993 { 00994 // compile 00995 if (compilationFuture.isRunning()) 00996 compilationDirty = true; 00997 else 00998 { 00999 bool dump(mainWindow->nodes->currentWidget() == this); 01000 compilationFuture = QtConcurrent::run(compilationThread, *target->getDescription(id), *commonDefinitions, editor->toPlainText(), dump); 01001 compilationWatcher.setFuture(compilationFuture); 01002 compilationDirty = false; 01003 01004 // show progress icon 01005 compilationResultImage->setPixmap(QPixmap(QString(":/images/busy.png"))); 01006 } 01007 } 01008 01009 void NodeTab::compilationCompleted() 01010 { 01011 CompilationResult* result(compilationFuture.result()); 01012 assert(result); 01013 01014 // as long as result is dirty, continue compilation 01015 if (compilationDirty) 01016 { 01017 delete result; 01018 recompile(); 01019 return; 01020 } 01021 01022 // process results 01023 processCompilationResult(result); 01024 } 01025 01026 void NodeTab::processCompilationResult(CompilationResult* result) 01027 { 01028 // clear old user data 01029 // doRehighlight is required to prevent infinite recursion because there are no slot 01030 // to differentiate user changes from highlight changes in documents 01031 bool doRehighlight = clearEditorProperty("errorPos"); 01032 01033 if (result->dump) 01034 { 01035 mainWindow->compilationMessageBox->setWindowTitle( 01036 tr("Aseba Studio: Output of last compilation for %0").arg(target->getName(id)) 01037 ); 01038 01039 if (result->success) 01040 mainWindow->compilationMessageBox->setText( 01041 tr("Compilation success.") + QString("\n\n") + 01042 QString::fromStdWString(result->compilationMessages.str()) 01043 ); 01044 else 01045 mainWindow->compilationMessageBox->setText( 01046 QString::fromStdWString(result->error.toWString()) + ".\n\n" + 01047 QString::fromStdWString(result->compilationMessages.str()) 01048 ); 01049 01050 } 01051 01052 // show memory usage 01053 if (result->success) 01054 { 01055 const unsigned variableCount = result->allocatedVariablesCount; 01056 const unsigned variableTotal = (*target->getDescription(id)).variablesSize; 01057 const unsigned bytecodeCount = result->bytecode.size(); 01058 const unsigned bytecodeTotal = (*target->getDescription(id)).bytecodeSize; 01059 assert(variableCount); 01060 assert(bytecodeCount); 01061 const QString variableText = tr("variables: %1 on %2 (%3\%)").arg(variableCount).arg(variableTotal).arg((double)variableCount*100./variableTotal, 0, 'f', 1); 01062 const QString bytecodeText = tr("bytecode: %1 on %2 (%3\%)").arg(bytecodeCount).arg(bytecodeTotal).arg((double)bytecodeCount*100./bytecodeTotal, 0, 'f', 1); 01063 memoryUsageText->setText(trUtf8("<b>Memory usage</b> : %1, %2").arg(variableText).arg(bytecodeText)); 01064 } 01065 01066 // update state following result 01067 if (result->success) 01068 { 01069 bytecode = result->bytecode; 01070 allocatedVariablesCount = result->allocatedVariablesCount; 01071 vmMemoryModel->updateVariablesStructure(&result->variablesMap); 01072 01073 updateHidden(); 01074 compilationResultText->setText(tr("Compilation success.")); 01075 compilationResultImage->setPixmap(QPixmap(QString(":/images/ok.png"))); 01076 loadButton->setEnabled(true); 01077 emit uploadReadynessChanged(true); 01078 01079 errorPos = -1; 01080 } 01081 else 01082 { 01083 compilationResultText->setText(QString::fromStdWString(result->error.toWString())); 01084 compilationResultImage->setPixmap(QPixmap(QString(":/images/warning.png"))); 01085 loadButton->setEnabled(false); 01086 emit uploadReadynessChanged(false); 01087 01088 // we have an error, set the correct user data 01089 if (result->error.pos.valid) 01090 { 01091 errorPos = result->error.pos.character; 01092 QTextBlock textBlock = editor->document()->findBlock(errorPos); 01093 int posInBlock = errorPos - textBlock.position(); 01094 if (textBlock.userData()) 01095 polymorphic_downcast<AeslEditorUserData *>(textBlock.userData())->properties["errorPos"] = posInBlock; 01096 else 01097 textBlock.setUserData(new AeslEditorUserData("errorPos", posInBlock)); 01098 doRehighlight = true; 01099 } 01100 } 01101 01102 // we have finished with the results 01103 delete result; 01104 01105 // clear bearkpoints of target if currently in debugging mode 01106 if (editor->debugging) 01107 { 01108 //target->stop(id); 01109 markTargetUnsynced(); 01110 doRehighlight = true; 01111 01112 } 01113 01114 if (doRehighlight) 01115 rehighlight(); 01116 } 01117 01119 void NodeTab::markTargetUnsynced() 01120 { 01121 editor->debugging = false; 01122 resetButton->setEnabled(false); 01123 runInterruptButton->setEnabled(false); 01124 nextButton->setEnabled(false); 01125 target->clearBreakpoints(id); 01126 switchEditorProperty("breakpoint", "breakpointPending"); 01127 executionModeLabel->setText(tr("unknown")); 01128 mainWindow->nodes->setExecutionMode(mainWindow->getIndexFromId(id), Target::EXECUTION_UNKNOWN); 01129 } 01130 01131 void NodeTab::cursorMoved() 01132 { 01133 // fix tab 01134 cursorPosText->setText(QString("Line: %0 Col: %1").arg(editor->textCursor().blockNumber() + 1).arg(editor->textCursor().columnNumber() + 1)); 01135 } 01136 01137 void NodeTab::goToError() 01138 { 01139 if (errorPos >= 0) 01140 { 01141 QTextCursor cursor = editor->textCursor(); 01142 cursor.setPosition(errorPos); 01143 editor->setTextCursor(cursor); 01144 editor->ensureCursorVisible(); 01145 } 01146 } 01147 01148 void NodeTab::clearExecutionErrors() 01149 { 01150 // remove execution error 01151 if (clearEditorProperty("executionError")) 01152 rehighlight(); 01153 } 01154 01155 void NodeTab::refreshCompleterModel(LocalContext context) 01156 { 01157 // qDebug() << "New context: " << context; 01158 disconnect(mainWindow->eventsDescriptionsModel, 0, sortingProxy, 0); 01159 01160 if ((context == GeneralContext) || (context == UnknownContext)) 01161 { 01162 sortingProxy->setSourceModel(variableAggregator); 01163 sortingProxy->sort(0); 01164 editor->setCompleterModel(sortingProxy); // both variables and constants 01165 } 01166 else if (context == LeftValueContext) 01167 { 01168 sortingProxy->setSourceModel(vmMemoryModel); 01169 sortingProxy->sort(0); 01170 editor->setCompleterModel(sortingProxy); // only variables 01171 } 01172 else if (context == VarDefContext) 01173 editor->setCompleterModel(0); // disable auto-completion in this case 01174 else if (context == FunctionContext) 01175 { 01176 sortingProxy->setSourceModel(functionsFlatModel); 01177 sortingProxy->sort(0); 01178 editor->setCompleterModel(sortingProxy); // native functions 01179 } 01180 else if (context == EventContext) 01181 { 01182 sortingProxy->setSourceModel(eventAggregator); 01183 sortingProxy->sort(0); 01184 //connect(mainWindow->eventsDescriptionsModel, SIGNAL(publicRowsInserted()), SLOT(sortCompleterModel())); 01185 //connect(mainWindow->eventsDescriptionsModel, SIGNAL(publicRowsRemoved()), SLOT(sortCompleterModel())); 01186 editor->setCompleterModel(sortingProxy); // both local and global events 01187 } 01188 } 01189 /* 01190 void NodeTab::sortCompleterModel() 01191 { 01192 sortingProxy->sort(0); 01193 editor->setCompleterModel(0); 01194 editor->setCompleterModel(sortingProxy); 01195 } 01196 */ 01197 void NodeTab::variablesMemoryChanged(unsigned start, const VariablesDataVector &variables) 01198 { 01199 // update memory view 01200 vmMemoryModel->setVariablesData(start, variables); 01201 } 01202 01203 void NodeTab::setBreakpoint(unsigned line) 01204 { 01205 rehighlight(); 01206 target->setBreakpoint(id, line); 01207 } 01208 01209 void NodeTab::clearBreakpoint(unsigned line) 01210 { 01211 rehighlight(); 01212 target->clearBreakpoint(id, line); 01213 } 01214 01215 void NodeTab::breakpointClearedAll() 01216 { 01217 rehighlight(); 01218 target->clearBreakpoints(id); 01219 } 01220 01221 void NodeTab::executionPosChanged(unsigned line) 01222 { 01223 // change active state 01224 currentPC = line; 01225 if (setEditorProperty("active", QVariant(), line, true)) 01226 rehighlight(); 01227 } 01228 01229 void NodeTab::executionModeChanged(Target::ExecutionMode mode) 01230 { 01231 // ignore those messages if we are not in debugging mode 01232 if (!editor->debugging) 01233 return; 01234 01235 resetButton->setEnabled(true); 01236 runInterruptButton->setEnabled(true); 01237 compilationResultImage->setPixmap(QPixmap(QString(":/images/ok.png"))); 01238 01239 // Filter spurious messages, to detect a stop at a breakpoint 01240 if (previousMode != mode) 01241 { 01242 previousMode = mode; 01243 if ((mode == Target::EXECUTION_STEP_BY_STEP) && editor->isBreakpoint(currentPC)) 01244 { 01245 // we are at a breakpoint 01246 if (mainWindow->currentScriptTab != this) 01247 { 01248 // not the current tab -> hidden tab -> highlight me in red 01249 mainWindow->nodes->highlightTab(mainWindow->getIndexFromId(id), Qt::red); 01250 } 01251 // go to this line 01252 editor->setTextCursor(QTextCursor(editor->document()->findBlockByLineNumber(currentPC))); 01253 editor->ensureCursorVisible(); 01254 } 01255 } 01256 01257 if (mode == Target::EXECUTION_RUN) 01258 { 01259 executionModeLabel->setText(tr("running")); 01260 01261 runInterruptButton->setText(tr("Pause")); 01262 runInterruptButton->setIcon(QIcon(":/images/pause.png")); 01263 01264 nextButton->setEnabled(false); 01265 01266 if (clearEditorProperty("active")) 01267 rehighlight(); 01268 } 01269 else if (mode == Target::EXECUTION_STEP_BY_STEP) 01270 { 01271 executionModeLabel->setText(tr("step by step")); 01272 01273 runInterruptButton->setText(tr("Run")); 01274 runInterruptButton->setIcon(QIcon(":/images/play.png")); 01275 01276 nextButton->setEnabled(true); 01277 01278 // go to this line and next line is visible 01279 editor->setTextCursor(QTextCursor(editor->document()->findBlockByLineNumber(currentPC+1))); 01280 editor->ensureCursorVisible(); 01281 editor->setTextCursor(QTextCursor(editor->document()->findBlockByLineNumber(currentPC))); 01282 } 01283 else if (mode == Target::EXECUTION_STOP) 01284 { 01285 executionModeLabel->setText(tr("stopped")); 01286 01287 runInterruptButton->setText(tr("Run")); 01288 runInterruptButton->setIcon(QIcon(":/images/play.png")); 01289 01290 nextButton->setEnabled(false); 01291 01292 if (clearEditorProperty("active")) 01293 rehighlight(); 01294 } 01295 01296 // set the tab icon to show the current execution mode 01297 mainWindow->nodes->setExecutionMode(mainWindow->getIndexFromId(id), mode); 01298 } 01299 01300 void NodeTab::breakpointSetResult(unsigned line, bool success) 01301 { 01302 clearEditorProperty("breakpointPending", line); 01303 if (success) 01304 setEditorProperty("breakpoint", QVariant(), line); 01305 rehighlight(); 01306 } 01307 01308 void NodeTab::closePlugins() 01309 { 01310 for (NodeToolInterfaces::const_iterator it(tools.begin()); it != tools.end(); ++it) 01311 (*it)->closeAsSoonAsPossible(); 01312 } 01313 01314 void NodeTab::rehighlight() 01315 { 01316 rehighlighting = true; 01317 highlighter->rehighlight(); 01318 } 01319 01320 void NodeTab::reSetBreakpoints() 01321 { 01322 target->clearBreakpoints(id); 01323 QTextBlock block = editor->document()->begin(); 01324 unsigned lineCounter = 0; 01325 while (block != editor->document()->end()) 01326 { 01327 AeslEditorUserData *uData = polymorphic_downcast_or_null<AeslEditorUserData *>(block.userData()); 01328 if (uData && (uData->properties.contains("breakpoint") || uData->properties.contains("breakpointPending"))) 01329 target->setBreakpoint(id, lineCounter); 01330 block = block.next(); 01331 lineCounter++; 01332 } 01333 } 01334 01335 bool NodeTab::setEditorProperty(const QString &property, const QVariant &value, unsigned line, bool removeOld) 01336 { 01337 bool changed = false; 01338 01339 QTextBlock block = editor->document()->begin(); 01340 unsigned lineCounter = 0; 01341 while (block != editor->document()->end()) 01342 { 01343 AeslEditorUserData *uData = polymorphic_downcast_or_null<AeslEditorUserData *>(block.userData()); 01344 if (lineCounter == line) 01345 { 01346 // set propety 01347 if (uData) 01348 { 01349 if (!uData->properties.contains(property) || (uData->properties[property] != value)) 01350 { 01351 uData->properties[property] = value; 01352 changed = true; 01353 } 01354 } 01355 else 01356 { 01357 block.setUserData(new AeslEditorUserData(property, value)); 01358 changed = true; 01359 } 01360 } 01361 else 01362 { 01363 // remove if exists 01364 if (uData && removeOld && uData->properties.contains(property)) 01365 { 01366 uData->properties.remove(property); 01367 if (uData->properties.isEmpty()) 01368 { 01369 // garbage collect UserData 01370 block.setUserData(0); 01371 } 01372 changed = true; 01373 } 01374 } 01375 01376 block = block.next(); 01377 lineCounter++; 01378 } 01379 01380 return changed; 01381 } 01382 01383 bool NodeTab::clearEditorProperty(const QString &property, unsigned line) 01384 { 01385 bool changed = false; 01386 01387 // find line, remove property 01388 QTextBlock block = editor->document()->begin(); 01389 unsigned lineCounter = 0; 01390 while (block != editor->document()->end()) 01391 { 01392 if (lineCounter == line) 01393 { 01394 AeslEditorUserData *uData = polymorphic_downcast_or_null<AeslEditorUserData *>(block.userData()); 01395 if (uData && uData->properties.contains(property)) 01396 { 01397 uData->properties.remove(property); 01398 if (uData->properties.isEmpty()) 01399 { 01400 // garbage collect UserData 01401 block.setUserData(0); 01402 } 01403 changed = true; 01404 } 01405 } 01406 block = block.next(); 01407 lineCounter++; 01408 } 01409 01410 return changed; 01411 } 01412 01413 bool NodeTab::clearEditorProperty(const QString &property) 01414 { 01415 bool changed = false; 01416 01417 // go through all blocks, remove property if found 01418 QTextBlock block = editor->document()->begin(); 01419 while (block != editor->document()->end()) 01420 { 01421 AeslEditorUserData *uData = polymorphic_downcast_or_null<AeslEditorUserData *>(block.userData()); 01422 if (uData && uData->properties.contains(property)) 01423 { 01424 uData->properties.remove(property); 01425 if (uData->properties.isEmpty()) 01426 { 01427 // garbage collect UserData 01428 block.setUserData(0); 01429 } 01430 changed = true; 01431 } 01432 block = block.next(); 01433 } 01434 01435 return changed; 01436 } 01437 01438 void NodeTab::switchEditorProperty(const QString &oldProperty, const QString &newProperty) 01439 { 01440 QTextBlock block = editor->document()->begin(); 01441 while (block != editor->document()->end()) 01442 { 01443 AeslEditorUserData *uData = polymorphic_downcast_or_null<AeslEditorUserData *>(block.userData()); 01444 if (uData && uData->properties.contains(oldProperty)) 01445 { 01446 uData->properties.remove(oldProperty); 01447 uData->properties[newProperty] = QVariant(); 01448 } 01449 block = block.next(); 01450 } 01451 } 01452 01453 NewNamedValueDialog::NewNamedValueDialog(QString *name, int *value, int min, int max) : 01454 name(name), 01455 value(value) 01456 { 01457 // create the widgets 01458 label1 = new QLabel(tr("Name", "Name of the named value (can be a constant, event,...)")); 01459 line1 = new QLineEdit(*name); 01460 label2 = new QLabel(tr("Default description", "When no description is given for the named value")); 01461 line2 = new QSpinBox(); 01462 line2->setRange(min, max); 01463 line2->setValue(*value); 01464 QLabel* lineHelp = new QLabel(QString("(%1 ... %2)").arg(min).arg(max)); 01465 QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 01466 01467 // create the layout 01468 QVBoxLayout* mainLayout = new QVBoxLayout(); 01469 mainLayout->addWidget(label1); 01470 mainLayout->addWidget(line1); 01471 mainLayout->addWidget(label2); 01472 mainLayout->addWidget(line2); 01473 mainLayout->addWidget(lineHelp); 01474 mainLayout->addWidget(buttonBox); 01475 setLayout(mainLayout); 01476 01477 // set modal 01478 setModal(true); 01479 01480 // connections 01481 connect(buttonBox, SIGNAL(accepted()), this, SLOT(okSlot())); 01482 connect(buttonBox, SIGNAL(rejected()), this, SLOT(cancelSlot())); 01483 } 01484 01485 bool NewNamedValueDialog::getNamedValue(QString *name, int *value, int min, int max, QString title, QString valueName, QString valueDescription) 01486 { 01487 NewNamedValueDialog dialog(name, value, min, max); 01488 dialog.setWindowTitle(title); 01489 dialog.label1->setText(valueName); 01490 dialog.label2->setText(valueDescription); 01491 dialog.resize(500, 0); // make it wide enough 01492 01493 int ret = dialog.exec(); 01494 01495 if (ret) 01496 return true; 01497 else 01498 return false; 01499 } 01500 01501 void NewNamedValueDialog::okSlot() 01502 { 01503 *name = line1->text(); 01504 *value = line2->value(); 01505 accept(); 01506 } 01507 01508 void NewNamedValueDialog::cancelSlot() 01509 { 01510 *name = ""; 01511 *value = -1; 01512 reject(); 01513 } 01514 01515 MainWindow::MainWindow(QVector<QTranslator*> translators, const QString& commandLineTarget, bool autoRefresh, QWidget *parent) : 01516 QMainWindow(parent), 01517 getDescriptionTimer(0), 01518 sourceModified(false), 01519 autoMemoryRefresh(autoRefresh) 01520 { 01521 // create target 01522 target = new DashelTarget(translators, commandLineTarget); 01523 01524 // create models 01525 eventsDescriptionsModel = new MaskableNamedValuesVectorModel(&commonDefinitions.events, tr("Event number %0"), this); 01526 eventsDescriptionsModel->setExtraMimeType("application/aseba-events"); 01527 constantsDefinitionsModel = new NamedValuesVectorModel(&commonDefinitions.constants, this); 01528 constantsDefinitionsModel->setExtraMimeType("application/aseba-constants"); 01529 constantsDefinitionsModel->setEditable(true); 01530 01531 // create config dialog + read settings on-disk 01532 ConfigDialog::init(this); 01533 01534 // create gui 01535 setupWidgets(); 01536 setupMenu(); 01537 setupConnections(); 01538 01539 // cosmetic fix-up 01540 updateWindowTitle(); 01541 if (readSettings() == false) 01542 resize(1000,700); 01543 01544 // when everything is ready, get description 01545 target->broadcastGetDescription(); 01546 } 01547 01548 MainWindow::~MainWindow() 01549 { 01550 #ifdef HAVE_QWT 01551 for (EventViewers::iterator it = eventsViewers.begin(); it != eventsViewers.end(); ++it) 01552 { 01553 it.value()->detachFromMain(); 01554 } 01555 #endif // HAVE_QWT 01556 01557 delete target; 01558 } 01559 01560 void MainWindow::about() 01561 { 01562 QString text = tr("<p>Aseba version informations:</p>" \ 01563 "<ul><li>Aseba ver. %0"\ 01564 "<br/>(build ver. %1/protocol ver. %2)" \ 01565 "</li><li>Dashel ver. %3"\ 01566 "<br/>(supported stream types: %4)"\ 01567 "</li></ul>" \ 01568 "<p>(c) 2006-2012 <a href=\"http://stephane.magnenat.net\">Stéphane Magnenat</a> and other contributors.</p>" \ 01569 "<p><a href=\"%5\">%5</a></p>" \ 01570 "<p>Aseba is open-source licensed under the LGPL version 3.</p>"); 01571 01572 text = text. 01573 arg(ASEBA_VERSION). 01574 arg(ASEBA_BUILD_VERSION). 01575 arg(ASEBA_PROTOCOL_VERSION). 01576 arg(DASHEL_VERSION). 01577 arg(QString::fromStdString(Dashel::streamTypeRegistry.list())). 01578 arg(tr("http://aseba.wikidot.com/en:start")); 01579 01580 QMessageBox::about(this, tr("About Aseba Studio"), text); 01581 } 01582 01583 bool MainWindow::newFile() 01584 { 01585 if (askUserBeforeDiscarding()) 01586 { 01587 clearDocumentSpecificTabs(); 01588 // we must only have NodeTab* left. 01589 for (int i = 0; i < nodes->count(); i++) 01590 { 01591 NodeTab* tab = polymorphic_downcast<NodeTab*>(nodes->widget(i)); 01592 Q_ASSERT(tab); 01593 tab->editor->clear(); 01594 } 01595 actualFileName.clear(); 01596 sourceModified = false; 01597 constantsDefinitionsModel->clear(); 01598 constantsDefinitionsModel->clearWasModified(); 01599 eventsDescriptionsModel->clear(); 01600 eventsDescriptionsModel->clearWasModified(); 01601 updateWindowTitle(); 01602 return true; 01603 } 01604 return false; 01605 } 01606 01607 void MainWindow::openFile(const QString &path) 01608 { 01609 // make sure we do not loose changes 01610 if (askUserBeforeDiscarding() == false) 01611 return; 01612 01613 QString fileName = path; 01614 01615 if (fileName.isEmpty()) 01616 fileName = QFileDialog::getOpenFileName(this, 01617 tr("Open Script"), "", "Aseba scripts (*.aesl)"); 01618 01619 QFile file(fileName); 01620 if (!file.open(QFile::ReadOnly)) 01621 return; 01622 01623 QDomDocument document("aesl-source"); 01624 QString errorMsg; 01625 int errorLine; 01626 int errorColumn; 01627 if (document.setContent(&file, false, &errorMsg, &errorLine, &errorColumn)) 01628 { 01629 eventsDescriptionsModel->clear(); 01630 constantsDefinitionsModel->clear(); 01631 01632 int noNodeCount = 0; 01633 clearDocumentSpecificTabs(); 01634 actualFileName = fileName; 01635 QDomNode domNode = document.documentElement().firstChild(); 01636 while (!domNode.isNull()) 01637 { 01638 if (domNode.isElement()) 01639 { 01640 QDomElement element = domNode.toElement(); 01641 if (element.tagName() == "node") 01642 { 01643 // load plugins xml data 01644 ScriptTab::SavedPlugins savedPlugins; 01645 QDomElement toolsPlugins(element.firstChildElement("toolsPlugins")); 01646 QDomElement toolPlugin(toolsPlugins.firstChildElement()); 01647 while (!toolPlugin.isNull()) 01648 { 01649 QDomDocument pluginDataDocument("tool-plugin-data"); 01650 pluginDataDocument.appendChild(pluginDataDocument.importNode(toolPlugin.firstChildElement(), true)); 01651 NodeToolInterface::SavedContent savedContent(toolPlugin.nodeName(), pluginDataDocument); 01652 savedPlugins.push_back(savedContent); 01653 toolPlugin = toolPlugin.nextSiblingElement(); 01654 } 01655 01656 // get text 01657 QString text; 01658 for(QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) 01659 { 01660 QDomText t = n.toText(); 01661 if (!t.isNull()) 01662 text += t.data(); 01663 } 01664 01665 // reconstruct nodes 01666 NodeTab* tab = getTabFromName(element.attribute("name"), element.attribute("nodeId", 0).toUInt()); 01667 if (tab) 01668 { 01669 tab->restorePlugins(savedPlugins, true); 01670 tab->editor->setPlainText(text); 01671 } 01672 else 01673 { 01674 nodes->addTab( 01675 new AbsentNodeTab( 01676 0, 01677 element.attribute("name"), text, 01678 savedPlugins 01679 ), 01680 element.attribute("name") + tr(" (not available)") 01681 ); 01682 noNodeCount++; 01683 } 01684 } 01685 else if (element.tagName() == "event") 01686 { 01687 const QString eventName(element.attribute("name")); 01688 const unsigned eventSize(element.attribute("size").toUInt()); 01689 eventsDescriptionsModel->addNamedValue(NamedValue(eventName.toStdWString(), std::min(unsigned(ASEBA_MAX_EVENT_ARG_SIZE), eventSize))); 01690 } 01691 else if (element.tagName() == "constant") 01692 { 01693 constantsDefinitionsModel->addNamedValue(NamedValue(element.attribute("name").toStdWString(), element.attribute("value").toInt())); 01694 } 01695 else if (element.tagName() == "keywords") 01696 { 01697 if( element.attribute("flag") == "true" ) 01698 showKeywordsAct->setChecked(true); 01699 else 01700 showKeywordsAct->setChecked(false); 01701 } 01702 01703 } 01704 domNode = domNode.nextSibling(); 01705 } 01706 01707 // check if there was some matching problem 01708 if (noNodeCount) 01709 QMessageBox::warning(this, 01710 tr("Loading"), 01711 tr("%0 scripts have no corresponding nodes in the current network and have not been loaded.").arg(noNodeCount) 01712 ); 01713 01714 // update recent files 01715 updateRecentFiles(fileName); 01716 regenerateOpenRecentMenu(); 01717 01718 recompileAll(); 01719 01720 sourceModified = false; 01721 constantsDefinitionsModel->clearWasModified(); 01722 eventsDescriptionsModel->clearWasModified(); 01723 updateWindowTitle(); 01724 } 01725 else 01726 { 01727 QMessageBox::warning(this, 01728 tr("Loading"), 01729 tr("Error in XML source file: %0 at line %1, column %2").arg(errorMsg).arg(errorLine).arg(errorColumn) 01730 ); 01731 } 01732 01733 file.close(); 01734 } 01735 01736 void MainWindow::openRecentFile() 01737 { 01738 QAction* entry = polymorphic_downcast<QAction*>(sender()); 01739 openFile(entry->text()); 01740 } 01741 01742 bool MainWindow::save() 01743 { 01744 return saveFile(actualFileName); 01745 } 01746 01747 bool MainWindow::saveFile(const QString &previousFileName) 01748 { 01749 QString fileName = previousFileName; 01750 01751 if (fileName.isEmpty()) 01752 fileName = QFileDialog::getSaveFileName(this, 01753 tr("Save Script"), actualFileName, "Aseba scripts (*.aesl)"); 01754 01755 if (fileName.isEmpty()) 01756 return false; 01757 01758 if (fileName.lastIndexOf(".") < 0) 01759 fileName += ".aesl"; 01760 01761 QFile file(fileName); 01762 if (!file.open(QFile::WriteOnly | QFile::Truncate)) 01763 return false; 01764 01765 actualFileName = fileName; 01766 updateRecentFiles(fileName); 01767 01768 // initiate DOM tree 01769 QDomDocument document("aesl-source"); 01770 QDomElement root = document.createElement("network"); 01771 document.appendChild(root); 01772 01773 root.appendChild(document.createTextNode("\n\n\n")); 01774 root.appendChild(document.createComment("list of global events")); 01775 01776 // events 01777 for (size_t i = 0; i < commonDefinitions.events.size(); i++) 01778 { 01779 QDomElement element = document.createElement("event"); 01780 element.setAttribute("name", QString::fromStdWString(commonDefinitions.events[i].name)); 01781 element.setAttribute("size", QString::number(commonDefinitions.events[i].value)); 01782 root.appendChild(element); 01783 } 01784 01785 root.appendChild(document.createTextNode("\n\n\n")); 01786 root.appendChild(document.createComment("list of constants")); 01787 01788 // constants 01789 for (size_t i = 0; i < commonDefinitions.constants.size(); i++) 01790 { 01791 QDomElement element = document.createElement("constant"); 01792 element.setAttribute("name", QString::fromStdWString(commonDefinitions.constants[i].name)); 01793 element.setAttribute("value", QString::number(commonDefinitions.constants[i].value)); 01794 root.appendChild(element); 01795 } 01796 01797 // keywords 01798 root.appendChild(document.createTextNode("\n\n\n")); 01799 root.appendChild(document.createComment("show keywords state")); 01800 01801 QDomElement keywords = document.createElement("keywords"); 01802 if( showKeywordsAct->isChecked() ) 01803 keywords.setAttribute("flag", "true"); 01804 else 01805 keywords.setAttribute("flag", "false"); 01806 root.appendChild(keywords); 01807 01808 // source code 01809 for (int i = 0; i < nodes->count(); i++) 01810 { 01811 const ScriptTab* tab = dynamic_cast<const ScriptTab*>(nodes->widget(i)); 01812 if (tab) 01813 { 01814 QString nodeName; 01815 01816 const NodeTab* nodeTab = dynamic_cast<const NodeTab*>(tab); 01817 if (nodeTab) 01818 nodeName = target->getName(nodeTab->nodeId()); 01819 01820 const AbsentNodeTab* absentNodeTab = dynamic_cast<const AbsentNodeTab*>(tab); 01821 if (absentNodeTab) 01822 nodeName = absentNodeTab->name; 01823 01824 const QString& nodeContent = tab->editor->toPlainText(); 01825 01826 root.appendChild(document.createTextNode("\n\n\n")); 01827 root.appendChild(document.createComment(QString("node %0").arg(nodeName))); 01828 01829 QDomElement element = document.createElement("node"); 01830 element.setAttribute("name", nodeName); 01831 element.setAttribute("nodeId", tab->nodeId()); 01832 QDomText text = document.createTextNode(nodeContent); 01833 element.appendChild(text); 01834 ScriptTab::SavedPlugins savedPlugins(tab->savePlugins()); 01835 if (!savedPlugins.isEmpty()) 01836 { 01837 QDomElement plugins = document.createElement("toolsPlugins"); 01838 for (ScriptTab::SavedPlugins::const_iterator it(savedPlugins.begin()); it != savedPlugins.end(); ++it) 01839 { 01840 const NodeToolInterface::SavedContent content(*it); 01841 QDomElement plugin(document.createElement(content.first)); 01842 plugin.appendChild(document.importNode(content.second.documentElement(), true)); 01843 plugins.appendChild(plugin); 01844 } 01845 element.appendChild(plugins); 01846 } 01847 root.appendChild(element); 01848 } 01849 } 01850 root.appendChild(document.createTextNode("\n\n\n")); 01851 01852 QTextStream out(&file); 01853 document.save(out, 0); 01854 01855 sourceModified = false; 01856 constantsDefinitionsModel->clearWasModified(); 01857 eventsDescriptionsModel->clearWasModified(); 01858 updateWindowTitle(); 01859 01860 return true; 01861 } 01862 01863 void MainWindow::exportMemoriesContent() 01864 { 01865 QString exportFileName = QFileDialog::getSaveFileName(this, tr("Export memory content"), "", "All Files (*);;CSV files (*.csv);;Text files (*.txt)"); 01866 01867 QFile file(exportFileName); 01868 if (!file.open(QFile::WriteOnly | QFile::Truncate)) 01869 return; 01870 01871 QTextStream out(&file); 01872 01873 for (int i = 0; i < nodes->count(); i++) 01874 { 01875 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 01876 if (tab) 01877 { 01878 const QString nodeName(target->getName(tab->nodeId())); 01879 const QList<TargetVariablesModel::Variable>& variables(tab->vmMemoryModel->getVariables()); 01880 01881 for (int j = 0; j < variables.size(); ++j) 01882 { 01883 const TargetVariablesModel::Variable& variable(variables[j]); 01884 out << nodeName << "." << variable.name << " "; 01885 for (size_t k = 0; k < variable.value.size(); ++k) 01886 { 01887 out << variable.value[k] << " "; 01888 } 01889 out << "\n"; 01890 } 01891 } 01892 } 01893 } 01894 01895 void MainWindow::importMemoriesContent() 01896 { 01897 QString importFileName = QFileDialog::getOpenFileName(this, tr("Import memory content"), "", "All Files (*);;CSV files (*.csv);;Text files (*.txt)"); 01898 01899 QFile file(importFileName); 01900 if (!file.open(QFile::ReadOnly)) 01901 return; 01902 01903 QTextStream in(&file); 01904 01905 QSet<QString> nodesNotFound; 01906 QStringList variablesNotFound; 01907 01908 while (!in.atEnd()) 01909 { 01910 QString line(in.readLine()); 01911 int pointPos(line.indexOf('.')); 01912 QString nodeName(line.left(pointPos)); 01913 NodeTab* tab(getTabFromName(nodeName)); 01914 if (tab) 01915 { 01916 int endVarNamePos(line.indexOf(' ', pointPos+1)); 01917 if (endVarNamePos != -1) 01918 { 01919 QString variableName(line.mid(pointPos+1, endVarNamePos-pointPos-1)); 01920 VariablesDataVector values; 01921 int index(endVarNamePos); 01922 while (index != -1) 01923 { 01924 int nextIndex(line.indexOf(' ', index+1)); 01925 QString value(line.mid(index+1, nextIndex - index - 1)); 01926 if (value.isEmpty()) 01927 break; 01928 values.push_back(value.toShort()); 01929 index = nextIndex; 01930 } 01931 if (!tab->vmMemoryModel->setVariableValues(variableName, values)) 01932 { 01933 variablesNotFound << tr("%0 on node %1").arg(variableName).arg(nodeName); 01934 } 01935 } 01936 } 01937 else 01938 nodesNotFound.insert(nodeName); 01939 } 01940 01941 if (!nodesNotFound.isEmpty() || !variablesNotFound.isEmpty()) 01942 { 01943 QString msg; 01944 if (!nodesNotFound.isEmpty()) 01945 { 01946 msg += tr("The following nodes are not present in the current network and their associated content was not imported:\n"); 01947 foreach (QString value, nodesNotFound) 01948 msg += "• " + value + "\n"; 01949 } 01950 if (!variablesNotFound.isEmpty()) 01951 { 01952 msg += tr("The following variables are not present in the current network and their associated content was not imported:\n"); 01953 foreach (QString value, variablesNotFound) 01954 msg += "• " + value + "\n"; 01955 } 01956 QMessageBox::warning(this, 01957 tr("Some content was not imported"), 01958 msg 01959 ); 01960 } 01961 } 01962 01963 void MainWindow::copyAll() 01964 { 01965 QString toCopy; 01966 for (int i = 0; i < nodes->count(); i++) 01967 { 01968 const NodeTab* nodeTab = dynamic_cast<NodeTab*>(nodes->widget(i)); 01969 if (nodeTab) 01970 { 01971 toCopy += QString("# node %0\n").arg(target->getName(nodeTab->nodeId())); 01972 toCopy += nodeTab->editor->toPlainText(); 01973 toCopy += "\n\n"; 01974 } 01975 const AbsentNodeTab* absentNodeTab = dynamic_cast<AbsentNodeTab*>(nodes->widget(i)); 01976 if (absentNodeTab) 01977 { 01978 toCopy += QString("# absent node named %0\n").arg(absentNodeTab->name); 01979 toCopy += absentNodeTab->editor->toPlainText(); 01980 toCopy += "\n\n"; 01981 } 01982 } 01983 QApplication::clipboard()->setText(toCopy); 01984 } 01985 01986 void MainWindow::findTriggered() 01987 { 01988 ScriptTab* tab = dynamic_cast<ScriptTab*>(nodes->currentWidget()); 01989 if (tab && tab->editor->textCursor().hasSelection()) 01990 findDialog->setFindText(tab->editor->textCursor().selectedText()); 01991 findDialog->replaceGroupBox->setChecked(false); 01992 findDialog->show(); 01993 } 01994 01995 void MainWindow::replaceTriggered() 01996 { 01997 findDialog->replaceGroupBox->setChecked(true); 01998 findDialog->show(); 01999 } 02000 02001 void MainWindow::commentTriggered() 02002 { 02003 assert(currentScriptTab); 02004 currentScriptTab->editor->commentAndUncommentSelection(AeslEditor::CommentSelection); 02005 } 02006 02007 void MainWindow::uncommentTriggered() 02008 { 02009 assert(currentScriptTab); 02010 currentScriptTab->editor->commentAndUncommentSelection(AeslEditor::UncommentSelection); 02011 } 02012 02013 void MainWindow::showLineNumbersChanged(bool state) 02014 { 02015 for (int i = 0; i < nodes->count(); i++) 02016 { 02017 NodeTab* tab = polymorphic_downcast<NodeTab*>(nodes->widget(i)); 02018 Q_ASSERT(tab); 02019 tab->linenumbers->showLineNumbers(state); 02020 } 02021 ConfigDialog::setShowLineNumbers(state); 02022 } 02023 02024 void MainWindow::goToLine() 02025 { 02026 assert(currentScriptTab); 02027 QTextEdit* editor(currentScriptTab->editor); 02028 const QTextDocument* document(editor->document()); 02029 QTextCursor cursor(editor->textCursor()); 02030 bool ok; 02031 const int curLine = cursor.blockNumber() + 1; 02032 const int minLine = 1; 02033 const int maxLine = document->lineCount(); 02034 const int line = QInputDialog::getInt( 02035 this, tr("Go To Line"), tr("Line:"), curLine, minLine, maxLine, 1, &ok); 02036 if (ok) 02037 editor->setTextCursor(QTextCursor(document->findBlockByLineNumber(line-1))); 02038 } 02039 02040 void MainWindow::showSettings() 02041 { 02042 ConfigDialog::showConfig(); 02043 } 02044 02045 void MainWindow::toggleBreakpoint() 02046 { 02047 assert(currentScriptTab); 02048 currentScriptTab->editor->toggleBreakpoint(); 02049 } 02050 02051 void MainWindow::clearAllBreakpoints() 02052 { 02053 assert(currentScriptTab); 02054 currentScriptTab->editor->clearAllBreakpoints(); 02055 } 02056 02057 void MainWindow::resetAll() 02058 { 02059 for (int i = 0; i < nodes->count(); i++) 02060 { 02061 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02062 if (tab) 02063 tab->resetClicked(); 02064 } 02065 } 02066 02067 void MainWindow::loadAll() 02068 { 02069 for (int i = 0; i < nodes->count(); i++) 02070 { 02071 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02072 if (tab) 02073 tab->loadClicked(); 02074 } 02075 } 02076 02077 void MainWindow::runAll() 02078 { 02079 for (int i = 0; i < nodes->count(); i++) 02080 { 02081 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02082 if (tab) 02083 target->run(tab->nodeId()); 02084 } 02085 } 02086 02087 void MainWindow::pauseAll() 02088 { 02089 for (int i = 0; i < nodes->count(); i++) 02090 { 02091 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02092 if (tab) 02093 target->pause(tab->nodeId()); 02094 } 02095 } 02096 02097 void MainWindow::stopAll() 02098 { 02099 for (int i = 0; i < nodes->count(); i++) 02100 { 02101 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02102 if (tab) 02103 target->stop(tab->nodeId()); 02104 } 02105 } 02106 02107 void MainWindow::showHidden(bool show) 02108 { 02109 for (int i = 0; i < nodes->count(); i++) 02110 { 02111 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02112 if (tab) 02113 { 02114 tab->vmFunctionsModel->recreateTreeFromDescription(show); 02115 tab->showHidden = show; 02116 tab->updateHidden(); 02117 } 02118 } 02119 ConfigDialog::setShowHidden(show); 02120 } 02121 02122 void MainWindow::showKeywords(bool show) 02123 { 02124 for (int i = 0; i < nodes->count(); i++) 02125 { 02126 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02127 if (tab) 02128 tab->showKeywords(show); 02129 } 02130 ConfigDialog::setShowKeywordToolbar(show); 02131 } 02132 02133 void MainWindow::clearAllExecutionError() 02134 { 02135 for (int i = 0; i < nodes->count(); i++) 02136 { 02137 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02138 if (tab) 02139 tab->clearExecutionErrors(); 02140 } 02141 logger->setStyleSheet(""); 02142 } 02143 02144 void MainWindow::uploadReadynessChanged() 02145 { 02146 bool ready = true; 02147 for (int i = 0; i < nodes->count(); i++) 02148 { 02149 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02150 if (tab) 02151 { 02152 if (!tab->loadButton->isEnabled()) 02153 { 02154 ready = false; 02155 break; 02156 } 02157 } 02158 } 02159 02160 loadAllAct->setEnabled(ready); 02161 writeAllBytecodesAct->setEnabled(ready); 02162 } 02163 02164 void MainWindow::sendEvent() 02165 { 02166 QModelIndex currentRow = eventsDescriptionsView->selectionModel()->currentIndex(); 02167 Q_ASSERT(currentRow.isValid()); 02168 02169 const unsigned eventId = currentRow.row(); 02170 const QString eventName = QString::fromStdWString(commonDefinitions.events[eventId].name); 02171 const int argsCount = commonDefinitions.events[eventId].value; 02172 VariablesDataVector data(argsCount); 02173 02174 if (argsCount > 0) 02175 { 02176 QString argList; 02177 while (true) 02178 { 02179 bool ok; 02180 argList = QInputDialog::getText(this, tr("Specify event arguments"), tr("Please specify the %0 arguments of event %1").arg(argsCount).arg(eventName), QLineEdit::Normal, argList, &ok); 02181 if (ok) 02182 { 02183 QStringList args = argList.split(QRegExp("[\\s,]+"), QString::SkipEmptyParts); 02184 if (args.size() != argsCount) 02185 { 02186 QMessageBox::warning(this, 02187 tr("Wrong number of arguments"), 02188 tr("You gave %0 arguments where event %1 requires %2").arg(args.size()).arg(eventName).arg(argsCount) 02189 ); 02190 continue; 02191 } 02192 for (int i = 0; i < args.size(); i++) 02193 { 02194 data[i] = args.at(i).toShort(&ok); 02195 if (!ok) 02196 { 02197 QMessageBox::warning(this, 02198 tr("Invalid value"), 02199 tr("Invalid value for argument %0 of event %1").arg(i).arg(eventName) 02200 ); 02201 break; 02202 } 02203 } 02204 if (ok) 02205 break; 02206 } 02207 else 02208 return; 02209 } 02210 } 02211 02212 target->sendEvent(eventId, data); 02213 userEvent(eventId, data); 02214 } 02215 02216 void MainWindow::sendEventIf(const QModelIndex &index) 02217 { 02218 if (index.column() == 0) 02219 sendEvent(); 02220 } 02221 02222 void MainWindow::toggleEventVisibleButton(const QModelIndex &index) 02223 { 02224 if (index.column() == 2) 02225 eventsDescriptionsModel->toggle(index); 02226 } 02227 02228 void MainWindow::plotEvent() 02229 { 02230 #ifdef HAVE_QWT 02231 QModelIndex currentRow = eventsDescriptionsView->selectionModel()->currentIndex(); 02232 Q_ASSERT(currentRow.isValid()); 02233 const unsigned eventId = currentRow.row(); 02234 plotEvent(eventId); 02235 #endif // HAVE_QWT 02236 } 02237 02238 void MainWindow::eventContextMenuRequested(const QPoint & pos) 02239 { 02240 #ifdef HAVE_QWT 02241 const QModelIndex index(eventsDescriptionsView->indexAt(pos)); 02242 if (index.isValid() && (index.column() == 0)) 02243 { 02244 const QString eventName(eventsDescriptionsModel->data(index).toString()); 02245 QMenu menu; 02246 menu.addAction(tr("Plot event %1").arg(eventName)); 02247 const QAction* ret = menu.exec(eventsDescriptionsView->mapToGlobal(pos)); 02248 if (ret) 02249 { 02250 const unsigned eventId = index.row(); 02251 plotEvent(eventId); 02252 } 02253 } 02254 #endif // HAVE_QWT 02255 } 02256 02257 void MainWindow::plotEvent(const unsigned eventId) 02258 { 02259 #ifdef HAVE_QWT 02260 const unsigned eventVariablesCount(eventsDescriptionsModel->data(eventsDescriptionsModel->index(eventId, 1)).toUInt()); 02261 const QString eventName(eventsDescriptionsModel->data(eventsDescriptionsModel->index(eventId, 0)).toString()); 02262 const QString tabTitle(tr("plot of %1").arg(eventName)); 02263 nodes->addTab(new EventViewer(eventId, eventName, eventVariablesCount, &eventsViewers), tabTitle, true); 02264 #endif // HAVE_QWT 02265 } 02266 02267 void MainWindow::logEntryDoubleClicked(QListWidgetItem * item) 02268 { 02269 if (item->data(Qt::UserRole).type() == QVariant::Point) 02270 { 02271 int node = item->data(Qt::UserRole).toPoint().x(); 02272 int line = item->data(Qt::UserRole).toPoint().y(); 02273 02274 NodeTab* tab = getTabFromId(node); 02275 Q_ASSERT(tab); 02276 nodes->setCurrentWidget(tab); 02277 tab->editor->setTextCursor(QTextCursor(tab->editor->document()->findBlockByLineNumber(line))); 02278 tab->editor->setFocus(); 02279 } 02280 } 02281 02282 void MainWindow::tabChanged(int index) 02283 { 02284 // remove old connections, if any 02285 if (currentScriptTab) 02286 { 02287 disconnect(cutAct, SIGNAL(triggered()), currentScriptTab->editor, SLOT(cut())); 02288 disconnect(copyAct, SIGNAL(triggered()), currentScriptTab->editor, SLOT(copy())); 02289 disconnect(pasteAct, SIGNAL(triggered()), currentScriptTab->editor, SLOT(paste())); 02290 disconnect(undoAct, SIGNAL(triggered()), currentScriptTab->editor, SLOT(undo())); 02291 disconnect(redoAct, SIGNAL(triggered()), currentScriptTab->editor, SLOT(redo())); 02292 02293 disconnect(currentScriptTab->editor, SIGNAL(copyAvailable(bool)), cutAct, SLOT(setEnabled(bool))); 02294 disconnect(currentScriptTab->editor, SIGNAL(copyAvailable(bool)), copyAct, SLOT(setEnabled(bool))); 02295 disconnect(currentScriptTab->editor, SIGNAL(undoAvailable(bool)), undoAct, SLOT(setEnabled(bool))); 02296 disconnect(currentScriptTab->editor, SIGNAL(redoAvailable(bool)), redoAct, SLOT(setEnabled(bool))); 02297 02298 pasteAct->setEnabled(false); 02299 findDialog->hide(); 02300 findDialog->editor = 0; 02301 findAct->setEnabled(false); 02302 replaceAct->setEnabled(false); 02303 goToLineAct->setEnabled(false); 02304 } 02305 02306 // reconnect to new 02307 if (index >= 0) 02308 { 02309 ScriptTab *tab = dynamic_cast<ScriptTab*>(nodes->widget(index)); 02310 if (tab) 02311 { 02312 connect(copyAct, SIGNAL(triggered()), tab->editor, SLOT(copy())); 02313 connect(tab->editor, SIGNAL(copyAvailable(bool)), copyAct, SLOT(setEnabled(bool))); 02314 02315 findDialog->editor = tab->editor; 02316 findAct->setEnabled(true); 02317 goToLineAct->setEnabled(true); 02318 02319 NodeTab *nodeTab = dynamic_cast<NodeTab*>(tab); 02320 if (nodeTab) 02321 { 02322 connect(cutAct, SIGNAL(triggered()), tab->editor, SLOT(cut())); 02323 connect(pasteAct, SIGNAL(triggered()), tab->editor, SLOT(paste())); 02324 connect(undoAct, SIGNAL(triggered()), tab->editor, SLOT(undo())); 02325 connect(redoAct, SIGNAL(triggered()), tab->editor, SLOT(redo())); 02326 02327 connect(tab->editor, SIGNAL(copyAvailable(bool)), cutAct, SLOT(setEnabled(bool))); 02328 connect(tab->editor, SIGNAL(undoAvailable(bool)), undoAct, SLOT(setEnabled(bool))); 02329 connect(tab->editor, SIGNAL(redoAvailable(bool)), redoAct, SLOT(setEnabled(bool))); 02330 02331 if (compilationMessageBox->isVisible()) 02332 nodeTab->recompile(); 02333 02334 // because this is a new tab, get content of variables 02335 target->getVariables(nodeTab->id, 0, nodeTab->allocatedVariablesCount); 02336 02337 showCompilationMsg->setEnabled(true); 02338 findDialog->replaceGroupBox->setEnabled(true); 02339 // paste and replace are only available when the editor is in read/write mode 02340 pasteAct->setEnabled(true); 02341 replaceAct->setEnabled(true); 02342 } 02343 else 02344 { 02345 showCompilationMsg->setEnabled(false); 02346 findDialog->replaceGroupBox->setEnabled(false); 02347 } 02348 02349 // TODO: it would be nice to find a way to setup this correctly 02350 cutAct->setEnabled(false); 02351 copyAct->setEnabled(false); 02352 undoAct->setEnabled(false); 02353 redoAct->setEnabled(false); 02354 02355 currentScriptTab = tab; 02356 } 02357 else 02358 currentScriptTab = 0; 02359 } 02360 else 02361 currentScriptTab = 0; 02362 } 02363 02364 void MainWindow::showCompilationMessages(bool doShow) 02365 { 02366 // this slot shouldn't be callable when an unactive tab is show 02367 compilationMessageBox->setVisible(doShow); 02368 if (nodes->currentWidget()) 02369 polymorphic_downcast<NodeTab *>(nodes->currentWidget())->recompile(); 02370 } 02371 02372 void MainWindow::compilationMessagesWasHidden() 02373 { 02374 showCompilationMsg->setChecked(false); 02375 } 02376 02377 void MainWindow::showMemoryUsage(bool show) 02378 { 02379 for (int i = 0; i < nodes->count(); i++) 02380 { 02381 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02382 if (tab) 02383 tab->showMemoryUsage(show); 02384 } 02385 ConfigDialog::setShowMemoryUsage(show); 02386 } 02387 02388 void MainWindow::addEventNameClicked() 02389 { 02390 QString eventName; 02391 int eventNbArgs = 0; 02392 02393 // prompt the user for the named value 02394 const bool ok = NewNamedValueDialog::getNamedValue(&eventName, &eventNbArgs, 0, 32767, tr("Add a new event"), tr("Name:"), tr("Number of arguments", "For the newly created event")); 02395 02396 eventName = eventName.trimmed(); 02397 if (ok && !eventName.isEmpty()) 02398 { 02399 if (commonDefinitions.events.contains(eventName.toStdWString())) 02400 { 02401 QMessageBox::warning(this, tr("Event already exists"), tr("Event %0 already exists.").arg(eventName)); 02402 } 02403 else if (!QRegExp("\\w(\\w|\\.)*").exactMatch(eventName) || eventName[0].isDigit()) 02404 { 02405 QMessageBox::warning(this, tr("Invalid event name"), tr("Event %0 has an invalid name. Valid names start with an alphabetical character or an \"_\", and continue with any number of alphanumeric characters, \"_\" and \".\"").arg(eventName)); 02406 } 02407 else 02408 { 02409 eventsDescriptionsModel->addNamedValue(NamedValue(eventName.toStdWString(), eventNbArgs)); 02410 } 02411 } 02412 } 02413 02414 void MainWindow::removeEventNameClicked() 02415 { 02416 QModelIndex currentRow = eventsDescriptionsView->selectionModel()->currentIndex(); 02417 Q_ASSERT(currentRow.isValid()); 02418 eventsDescriptionsModel->delNamedValue(currentRow.row()); 02419 02420 for (int i = 0; i < nodes->count(); i++) 02421 { 02422 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02423 if (tab) 02424 tab->isSynchronized = false; 02425 } 02426 } 02427 02428 void MainWindow::eventsUpdated(bool indexChanged) 02429 { 02430 if (indexChanged) 02431 { 02432 statusText->setText(tr("Desynchronised! Please reload.")); 02433 statusText->show(); 02434 } 02435 recompileAll(); 02436 updateWindowTitle(); 02437 } 02438 02439 void MainWindow::eventsUpdatedDirty() 02440 { 02441 eventsUpdated(true); 02442 } 02443 02444 void MainWindow::eventsDescriptionsSelectionChanged() 02445 { 02446 bool isSelected = eventsDescriptionsView->selectionModel()->currentIndex().isValid(); 02447 removeEventNameButton->setEnabled(isSelected); 02448 sendEventButton->setEnabled(isSelected); 02449 #ifdef HAVE_QWT 02450 plotEventButton->setEnabled(isSelected); 02451 #endif // HAVE_QWT 02452 } 02453 02454 void MainWindow::resetStatusText() 02455 { 02456 bool flag = true; 02457 02458 for (int i = 0; i < nodes->count(); i++) 02459 { 02460 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02461 if (tab) 02462 { 02463 if( !tab->isSynchronized ) 02464 { 02465 flag = false; 02466 break; 02467 } 02468 } 02469 } 02470 02471 if (flag) 02472 { 02473 statusText->clear(); 02474 statusText->hide(); 02475 } 02476 } 02477 02478 void MainWindow::addConstantClicked() 02479 { 02480 bool ok; 02481 QString constantName; 02482 int constantValue = 0; 02483 02484 // prompt the user for the named value 02485 ok = NewNamedValueDialog::getNamedValue(&constantName, &constantValue, -32768, 32767, tr("Add a new constant"), tr("Name:"), tr("Value", "Value assigned to the constant")); 02486 02487 if (ok && !constantName.isEmpty()) 02488 { 02489 if (commonDefinitions.constants.contains(constantName.toStdWString())) 02490 { 02491 QMessageBox::warning(this, tr("Constant already defined"), tr("Constant %0 is already defined.").arg(constantName)); 02492 } 02493 else 02494 { 02495 constantsDefinitionsModel->addNamedValue(NamedValue(constantName.toStdWString(), constantValue)); 02496 recompileAll(); 02497 updateWindowTitle(); 02498 } 02499 } 02500 } 02501 02502 void MainWindow::removeConstantClicked() 02503 { 02504 QModelIndex currentRow = constantsView->selectionModel()->currentIndex(); 02505 Q_ASSERT(currentRow.isValid()); 02506 constantsDefinitionsModel->delNamedValue(currentRow.row()); 02507 02508 recompileAll(); 02509 updateWindowTitle(); 02510 } 02511 02512 void MainWindow::constantsSelectionChanged() 02513 { 02514 bool isSelected = constantsView->selectionModel()->currentIndex().isValid(); 02515 removeConstantButton->setEnabled(isSelected); 02516 } 02517 02518 void MainWindow::recompileAll() 02519 { 02520 for (int i = 0; i < nodes->count(); i++) 02521 { 02522 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02523 if (tab) 02524 tab->recompile(); 02525 } 02526 } 02527 02528 void MainWindow::writeAllBytecodes() 02529 { 02530 for (int i = 0; i < nodes->count(); i++) 02531 { 02532 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02533 if (tab) 02534 tab->writeBytecode(); 02535 } 02536 } 02537 02538 void MainWindow::rebootAllNodes() 02539 { 02540 for (int i = 0; i < nodes->count(); i++) 02541 { 02542 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02543 if (tab) 02544 tab->reboot(); 02545 } 02546 } 02547 02548 void MainWindow::sourceChanged() 02549 { 02550 sourceModified = true; 02551 updateWindowTitle(); 02552 } 02553 02554 void MainWindow::showUserManual() 02555 { 02556 helpViewer.showHelp(HelpViewer::USERMANUAL); 02557 } 02558 02560 void MainWindow::nodeConnected(unsigned node) 02561 { 02562 // create a new tab for the node 02563 NodeTab* tab = new NodeTab(this, target, &commonDefinitions, node); 02564 tab->showKeywords(showKeywordsAct->isChecked()); 02565 tab->linenumbers->showLineNumbers(showLineNumbers->isChecked()); 02566 tab->showMemoryUsage(showMemoryUsageAct->isChecked()); 02567 02568 // check if there is an absent node tab with this id and name, and copy data 02569 const int absentIndex(getAbsentIndexFromId(node)); 02570 const AbsentNodeTab* absentTab(getAbsentTabFromId(node)); 02571 if (absentTab && nodes->tabText(absentIndex) == target->getName(node)) 02572 { 02573 tab->editor->document()->setPlainText(absentTab->editor->document()->toPlainText()); 02574 tab->restorePlugins(absentTab->savePlugins(), false); 02575 tab->updateToolList(); 02576 nodes->removeAndDeleteTab(absentIndex); 02577 } 02578 02579 // connect and show new tab 02580 connect(tab, SIGNAL(uploadReadynessChanged(bool)), SLOT(uploadReadynessChanged())); 02581 nodes->addTab(tab, target->getName(node)); 02582 02583 regenerateToolsMenus(); 02584 } 02585 02587 void MainWindow::nodeDisconnected(unsigned node) 02588 { 02589 const int index = getIndexFromId(node); 02590 Q_ASSERT(index >= 0); 02591 const NodeTab* tab = getTabFromId(node); 02592 const QString& tabName = nodes->tabText(index); 02593 02594 nodes->addTab( 02595 new AbsentNodeTab( 02596 node, 02597 tabName, 02598 tab->editor->document()->toPlainText(), 02599 tab->savePlugins() 02600 ), 02601 tabName 02602 ); 02603 02604 nodes->removeAndDeleteTab(index); 02605 02606 regenerateToolsMenus(); 02607 regenerateHelpMenu(); 02608 02609 if (!getDescriptionTimer) 02610 getDescriptionTimer = startTimer(2000); 02611 } 02612 02614 void MainWindow::networkDisconnected() 02615 { 02616 // collect all node ids to disconnect 02617 std::vector<unsigned> toDisconnect; 02618 for (int i = 0; i < nodes->count(); i++) 02619 { 02620 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02621 if (tab) 02622 toDisconnect.push_back(tab->nodeId()); 02623 } 02624 02625 // disconnect all node ids 02626 for (size_t i = 0; i < toDisconnect.size(); i++) 02627 nodeDisconnected(toDisconnect[i]); 02628 } 02629 02631 void MainWindow::userEvent(unsigned id, const VariablesDataVector &data) 02632 { 02633 if (eventsDescriptionsModel->isVisible(id)) 02634 { 02635 QString text = QTime::currentTime().toString("hh:mm:ss.zzz"); 02636 02637 if (id < commonDefinitions.events.size()) 02638 text += QString("\n%0 : ").arg(QString::fromStdWString(commonDefinitions.events[id].name)); 02639 else 02640 text += tr("\nevent %0 : ").arg(id); 02641 02642 for (size_t i = 0; i < data.size(); i++) 02643 text += QString("%0 ").arg(data[i]); 02644 02645 if (logger->count() > 50) 02646 delete logger->takeItem(0); 02647 QListWidgetItem * item = new QListWidgetItem(QIcon(":/images/info.png"), text, logger); 02648 logger->scrollToBottom(); 02649 Q_UNUSED(item); 02650 } 02651 02652 #ifdef HAVE_QWT 02653 02654 // iterate over all viewer for this event 02655 QList<EventViewer*> viewers = eventsViewers.values(id); 02656 for (int i = 0; i < viewers.size(); ++i) 02657 viewers.at(i)->addData(data); 02658 02659 #endif // HAVE_QWT 02660 } 02661 02663 void MainWindow::userEventsDropped(unsigned amount) 02664 { 02665 QString text = QTime::currentTime().toString("hh:mm:ss.zzz"); 02666 text += QString("\n%0 user events not shown").arg(amount); 02667 02668 if (logger->count() > 50) 02669 delete logger->takeItem(0); 02670 QListWidgetItem * item = new QListWidgetItem(QIcon(":/images/info.png"), text, logger); 02671 logger->scrollToBottom(); 02672 Q_UNUSED(item); 02673 logger->setStyleSheet(" QListView::item { background: rgb(255,128,128); }"); 02674 } 02675 02677 void MainWindow::arrayAccessOutOfBounds(unsigned node, unsigned line, unsigned size, unsigned index) 02678 { 02679 addErrorEvent(node, line, tr("array access at %0 out of bounds [0..%1]").arg(index).arg(size-1)); 02680 } 02681 02683 void MainWindow::divisionByZero(unsigned node, unsigned line) 02684 { 02685 addErrorEvent(node, line, tr("division by zero")); 02686 } 02687 02689 void MainWindow::eventExecutionKilled(unsigned node, unsigned line) 02690 { 02691 addErrorEvent(node, line, tr("event execution killed")); 02692 } 02693 02695 void MainWindow::nodeSpecificError(unsigned node, unsigned line, const QString& message) 02696 { 02697 addErrorEvent(node, line, message); 02698 } 02699 02701 void MainWindow::addErrorEvent(unsigned node, unsigned line, const QString& message) 02702 { 02703 NodeTab* tab = getTabFromId(node); 02704 Q_ASSERT(tab); 02705 02706 if (tab->setEditorProperty("executionError", QVariant(), line, true)) 02707 { 02708 tab->rehighlighting = true; 02709 tab->highlighter->rehighlight(); 02710 } 02711 02712 QString text = QTime::currentTime().toString("hh:mm:ss.zzz"); 02713 text += "\n" + tr("%0:%1: %2").arg(target->getName(node)).arg(line + 1).arg(message); 02714 02715 if (logger->count() > 50) 02716 delete logger->takeItem(0); 02717 QListWidgetItem *item = new QListWidgetItem(QIcon(":/images/warning.png"), text, logger); 02718 item->setData(Qt::UserRole, QPoint(node, line)); 02719 logger->scrollToBottom(); 02720 } 02721 02722 02724 void MainWindow::executionPosChanged(unsigned node, unsigned line) 02725 { 02726 NodeTab* tab = getTabFromId(node); 02727 Q_ASSERT(tab); 02728 02729 tab->executionPosChanged(line); 02730 } 02731 02733 void MainWindow::executionModeChanged(unsigned node, Target::ExecutionMode mode) 02734 { 02735 NodeTab* tab = getTabFromId(node); 02736 Q_ASSERT(tab); 02737 02738 tab->executionModeChanged(mode); 02739 } 02740 02742 void MainWindow::variablesMemoryEstimatedDirty(unsigned node) 02743 { 02744 NodeTab* tab = getTabFromId(node); 02745 Q_ASSERT(tab); 02746 02747 tab->refreshMemoryClicked(); 02748 } 02749 02751 void MainWindow::variablesMemoryChanged(unsigned node, unsigned start, const VariablesDataVector &variables) 02752 { 02753 NodeTab* tab = getTabFromId(node); 02754 Q_ASSERT(tab); 02755 02756 tab->vmMemoryModel->setVariablesData(start, variables); 02757 } 02758 02760 void MainWindow::breakpointSetResult(unsigned node, unsigned line, bool success) 02761 { 02762 NodeTab* tab = getTabFromId(node); 02763 Q_ASSERT(tab); 02764 02765 tab->breakpointSetResult(line, success); 02766 } 02767 02769 void MainWindow::timerEvent ( QTimerEvent * event ) 02770 { 02771 bool doSend(false); 02772 02773 for (int i = 0; i < nodes->count(); i++) 02774 { 02775 AbsentNodeTab* tab = dynamic_cast<AbsentNodeTab*>(nodes->widget(i)); 02776 if (tab) 02777 doSend = doSend || (tab->id != 0); 02778 } 02779 02780 if (doSend) 02781 { 02782 target->broadcastGetDescription(); 02783 } 02784 else 02785 { 02786 killTimer(getDescriptionTimer); 02787 getDescriptionTimer = 0; 02788 } 02789 } 02790 02792 int MainWindow::getIndexFromId(unsigned node) const 02793 { 02794 for (int i = 0; i < nodes->count(); i++) 02795 { 02796 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02797 if (tab) 02798 { 02799 if (tab->nodeId() == node) 02800 return i; 02801 } 02802 } 02803 return -1; 02804 } 02805 02807 NodeTab* MainWindow::getTabFromId(unsigned node) const 02808 { 02809 for (int i = 0; i < nodes->count(); i++) 02810 { 02811 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02812 if (tab) 02813 { 02814 if (tab->nodeId() == node) 02815 return tab; 02816 } 02817 } 02818 return 0; 02819 } 02820 02822 NodeTab* MainWindow::getTabFromName(const QString& name, unsigned preferedId) const 02823 { 02824 NodeTab* bestFound(0); 02825 for (int i = 0; i < nodes->count(); i++) 02826 { 02827 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 02828 if (tab) 02829 { 02830 const unsigned id(tab->nodeId()); 02831 if (target->getName(id) == name) 02832 { 02833 if (id == preferedId) 02834 return tab; 02835 else if (!bestFound) 02836 bestFound = tab; 02837 } 02838 } 02839 } 02840 return bestFound; 02841 } 02842 02844 int MainWindow::getAbsentIndexFromId(unsigned node) const 02845 { 02846 for (int i = 0; i < nodes->count(); i++) 02847 { 02848 AbsentNodeTab* tab = dynamic_cast<AbsentNodeTab*>(nodes->widget(i)); 02849 if (tab) 02850 { 02851 if (tab->nodeId() == node) 02852 return i; 02853 } 02854 } 02855 return -1; 02856 } 02857 02859 AbsentNodeTab* MainWindow::getAbsentTabFromId(unsigned node) const 02860 { 02861 for (int i = 0; i < nodes->count(); i++) 02862 { 02863 AbsentNodeTab* tab = dynamic_cast<AbsentNodeTab*>(nodes->widget(i)); 02864 if (tab) 02865 { 02866 if (tab->nodeId() == node) 02867 return tab; 02868 } 02869 } 02870 return 0; 02871 } 02872 02873 void MainWindow::clearDocumentSpecificTabs() 02874 { 02875 bool changed = false; 02876 do 02877 { 02878 changed = false; 02879 for (int i = 0; i < nodes->count(); i++) 02880 { 02881 QWidget* tab = nodes->widget(i); 02882 02883 #ifdef HAVE_QWT 02884 if (dynamic_cast<AbsentNodeTab*>(tab) || dynamic_cast<EventViewer*>(tab)) 02885 #else // HAVE_QWT 02886 if (dynamic_cast<AbsentNodeTab*>(tab)) 02887 #endif // HAVE_QWT 02888 { 02889 nodes->removeAndDeleteTab(i); 02890 changed = true; 02891 break; 02892 } 02893 } 02894 } 02895 while (changed); 02896 } 02897 02898 void MainWindow::setupWidgets() 02899 { 02900 currentScriptTab = 0; 02901 nodes = new EditorsPlotsTabWidget; 02902 nodes->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); 02903 02904 QSplitter *splitter = new QSplitter(); 02905 splitter->addWidget(nodes); 02906 setCentralWidget(splitter); 02907 02908 addConstantButton = new QPushButton(QPixmap(QString(":/images/add.png")), ""); 02909 removeConstantButton = new QPushButton(QPixmap(QString(":/images/remove.png")), ""); 02910 addConstantButton->setToolTip(tr("Add a new constant")); 02911 removeConstantButton->setToolTip(tr("Remove this constant")); 02912 removeConstantButton->setEnabled(false); 02913 02914 constantsView = new FixedWidthTableView; 02915 constantsView->setShowGrid(false); 02916 constantsView->verticalHeader()->hide(); 02917 constantsView->horizontalHeader()->hide(); 02918 constantsView->setModel(constantsDefinitionsModel); 02919 constantsView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); 02920 constantsView->setSelectionMode(QAbstractItemView::SingleSelection); 02921 constantsView->setSelectionBehavior(QAbstractItemView::SelectRows); 02922 constantsView->setDragDropMode(QAbstractItemView::InternalMove); 02923 constantsView->setDragEnabled(true); 02924 constantsView->setDropIndicatorShown(true); 02925 constantsView->setItemDelegateForColumn(1, new SpinBoxDelegate(-32768, 32767, this)); 02926 constantsView->setMinimumHeight(100); 02927 constantsView->setSecondColumnLongestContent("-888888##"); 02928 constantsView->resizeRowsToContents(); 02929 02930 QGridLayout* constantsLayout = new QGridLayout; 02931 constantsLayout->addWidget(new QLabel(tr("<b>Constants</b>")),0,0); 02932 constantsLayout->setColumnStretch(0, 1); 02933 constantsLayout->addWidget(addConstantButton,0,1); 02934 constantsLayout->setColumnStretch(1, 0); 02935 constantsLayout->addWidget(removeConstantButton,0,2); 02936 constantsLayout->setColumnStretch(2, 0); 02937 constantsLayout->addWidget(constantsView, 1, 0, 1, 3); 02938 //setColumnStretch 02939 02940 /*QHBoxLayout* constantsAddRemoveLayout = new QHBoxLayout;; 02941 constantsAddRemoveLayout->addStretch(); 02942 addConstantButton = new QPushButton(QPixmap(QString(":/images/add.png")), ""); 02943 constantsAddRemoveLayout->addWidget(addConstantButton); 02944 removeConstantButton = new QPushButton(QPixmap(QString(":/images/remove.png")), ""); 02945 removeConstantButton->setEnabled(false); 02946 constantsAddRemoveLayout->addWidget(removeConstantButton); 02947 02948 eventsDockLayout->addLayout(constantsAddRemoveLayout); 02949 eventsDockLayout->addWidget(constantsView, 1);*/ 02950 02951 02952 /*eventsDockLayout->addWidget(new QLabel(tr("<b>Events</b>"))); 02953 02954 QHBoxLayout* eventsAddRemoveLayout = new QHBoxLayout;; 02955 eventsAddRemoveLayout->addStretch(); 02956 addEventNameButton = new QPushButton(QPixmap(QString(":/images/add.png")), ""); 02957 eventsAddRemoveLayout->addWidget(addEventNameButton); 02958 removeEventNameButton = new QPushButton(QPixmap(QString(":/images/remove.png")), ""); 02959 removeEventNameButton->setEnabled(false); 02960 eventsAddRemoveLayout->addWidget(removeEventNameButton); 02961 sendEventButton = new QPushButton(QPixmap(QString(":/images/newmsg.png")), ""); 02962 sendEventButton->setEnabled(false); 02963 eventsAddRemoveLayout->addWidget(sendEventButton); 02964 02965 eventsDockLayout->addLayout(eventsAddRemoveLayout); 02966 02967 eventsDockLayout->addWidget(eventsDescriptionsView, 1);*/ 02968 02969 02970 addEventNameButton = new QPushButton(QPixmap(QString(":/images/add.png")), ""); 02971 removeEventNameButton = new QPushButton(QPixmap(QString(":/images/remove.png")), ""); 02972 removeEventNameButton->setEnabled(false); 02973 sendEventButton = new QPushButton(QPixmap(QString(":/images/newmsg.png")), ""); 02974 sendEventButton->setEnabled(false); 02975 02976 addEventNameButton->setToolTip(tr("Add a new event")); 02977 removeEventNameButton->setToolTip(tr("Remove this event")); 02978 sendEventButton->setToolTip(tr("Send this event")); 02979 02980 #ifdef HAVE_QWT 02981 plotEventButton = new QPushButton(QPixmap(QString(":/images/plot.png")), ""); 02982 plotEventButton->setEnabled(false); 02983 plotEventButton->setToolTip(tr("Plot this event")); 02984 #endif // HAVE_QWT 02985 02986 eventsDescriptionsView = new FixedWidthTableView; 02987 eventsDescriptionsView->setShowGrid(false); 02988 eventsDescriptionsView->verticalHeader()->hide(); 02989 eventsDescriptionsView->horizontalHeader()->hide(); 02990 eventsDescriptionsView->setModel(eventsDescriptionsModel); 02991 eventsDescriptionsView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); 02992 eventsDescriptionsView->setSelectionMode(QAbstractItemView::SingleSelection); 02993 eventsDescriptionsView->setSelectionBehavior(QAbstractItemView::SelectRows); 02994 eventsDescriptionsView->setDragDropMode(QAbstractItemView::InternalMove); 02995 eventsDescriptionsView->setDragEnabled(true); 02996 eventsDescriptionsView->setDropIndicatorShown(true); 02997 eventsDescriptionsView->setItemDelegateForColumn(1, new SpinBoxDelegate(0, ASEBA_MAX_EVENT_ARG_COUNT, this)); 02998 eventsDescriptionsView->setMinimumHeight(100); 02999 eventsDescriptionsView->setSecondColumnLongestContent("255###"); 03000 eventsDescriptionsView->resizeRowsToContents(); 03001 eventsDescriptionsView->setContextMenuPolicy(Qt::CustomContextMenu); 03002 03003 QGridLayout* eventsLayout = new QGridLayout; 03004 eventsLayout->addWidget(new QLabel(tr("<b>Global Events</b>")),0,0,1,4); 03005 eventsLayout->addWidget(addEventNameButton,1,0); 03006 //eventsLayout->setColumnStretch(2, 0); 03007 eventsLayout->addWidget(removeEventNameButton,1,1); 03008 //eventsLayout->setColumnStretch(3, 0); 03009 //eventsLayout->setColumnStretch(0, 1); 03010 eventsLayout->addWidget(sendEventButton,1,2); 03011 //eventsLayout->setColumnStretch(1, 0); 03012 #ifdef HAVE_QWT 03013 eventsLayout->addWidget(plotEventButton,1,3); 03014 #endif // HAVE_QWT 03015 eventsLayout->addWidget(eventsDescriptionsView, 2, 0, 1, 4); 03016 03017 /*logger = new QListWidget; 03018 logger->setMinimumSize(80,100); 03019 logger->setSelectionMode(QAbstractItemView::NoSelection); 03020 eventsDockLayout->addWidget(logger, 3); 03021 clearLogger = new QPushButton(tr("Clear")); 03022 eventsDockLayout->addWidget(clearLogger);*/ 03023 03024 logger = new QListWidget; 03025 logger->setMinimumSize(80,100); 03026 logger->setSelectionMode(QAbstractItemView::NoSelection); 03027 clearLogger = new QPushButton(tr("Clear")); 03028 statusText = new QLabel(""); 03029 statusText->hide(); 03030 03031 QVBoxLayout* loggerLayout = new QVBoxLayout; 03032 loggerLayout->addWidget(statusText); 03033 loggerLayout->addWidget(logger); 03034 loggerLayout->addWidget(clearLogger); 03035 03036 // panel 03037 QSplitter* rightPanelSplitter = new QSplitter(Qt::Vertical); 03038 03039 QWidget* constantsWidget = new QWidget; 03040 constantsWidget->setLayout(constantsLayout); 03041 rightPanelSplitter->addWidget(constantsWidget); 03042 03043 QWidget* eventsWidget = new QWidget; 03044 eventsWidget->setLayout(eventsLayout); 03045 rightPanelSplitter->addWidget(eventsWidget); 03046 03047 QWidget* loggerWidget = new QWidget; 03048 loggerWidget->setLayout(loggerLayout); 03049 rightPanelSplitter->addWidget(loggerWidget); 03050 03051 // main window 03052 splitter->addWidget(rightPanelSplitter); 03053 splitter->setSizes(QList<int>() << 800 << 200); 03054 03055 // dialog box 03056 compilationMessageBox = new CompilationLogDialog(); 03057 connect(this, SIGNAL(MainWindowClosed()), compilationMessageBox, SLOT(close())); 03058 connect(this, SIGNAL(MainWindowClosed()), compilationMessageBox, SLOT(deleteLater())); 03059 findDialog = new FindDialog(this); 03060 connect(this, SIGNAL(MainWindowClosed()), findDialog, SLOT(close())); 03061 03062 // help viewer 03063 QString lang = target->getLanguage().left(2); 03064 if (lang != "") 03065 helpViewer.setLanguage(lang); 03066 else 03067 helpViewer.setLanguage(QLocale::system().name()); 03068 connect(this, SIGNAL(MainWindowClosed()), &helpViewer, SLOT(close())); 03069 } 03070 03071 void MainWindow::setupConnections() 03072 { 03073 // general connections 03074 connect(nodes, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); 03075 connect(logger, SIGNAL(itemDoubleClicked(QListWidgetItem *)), SLOT(logEntryDoubleClicked(QListWidgetItem *))); 03076 connect(ConfigDialog::getInstance(), SIGNAL(settingsChanged()), SLOT(applySettings())); 03077 03078 // global actions 03079 connect(loadAllAct, SIGNAL(triggered()), SLOT(loadAll())); 03080 connect(resetAllAct, SIGNAL(triggered()), SLOT(resetAll())); 03081 connect(runAllAct, SIGNAL(triggered()), SLOT(runAll())); 03082 connect(pauseAllAct, SIGNAL(triggered()), SLOT(pauseAll())); 03083 03084 // events 03085 connect(addEventNameButton, SIGNAL(clicked()), SLOT(addEventNameClicked())); 03086 connect(removeEventNameButton, SIGNAL(clicked()), SLOT(removeEventNameClicked())); 03087 connect(sendEventButton, SIGNAL(clicked()), SLOT(sendEvent())); 03088 #ifdef HAVE_QWT 03089 connect(plotEventButton, SIGNAL(clicked()), SLOT(plotEvent())); 03090 #endif // HAVE_QWT 03091 connect(eventsDescriptionsView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(eventsDescriptionsSelectionChanged())); 03092 connect(eventsDescriptionsView, SIGNAL(doubleClicked(const QModelIndex &)), SLOT(sendEventIf(const QModelIndex &))); 03093 connect(eventsDescriptionsView, SIGNAL(clicked(const QModelIndex &)), SLOT(toggleEventVisibleButton(const QModelIndex &)) ); 03094 connect(eventsDescriptionsModel, SIGNAL(dataChanged ( const QModelIndex &, const QModelIndex & ) ), SLOT(eventsUpdated())); 03095 connect(eventsDescriptionsModel, SIGNAL(publicRowsInserted()), SLOT(eventsUpdated())); 03096 connect(eventsDescriptionsModel, SIGNAL(publicRowsRemoved()), SLOT(eventsUpdatedDirty())); 03097 connect(eventsDescriptionsView, SIGNAL(customContextMenuRequested ( const QPoint & )), SLOT(eventContextMenuRequested(const QPoint & ))); 03098 03099 // logger 03100 connect(clearLogger, SIGNAL(clicked()), logger, SLOT(clear())); 03101 connect(clearLogger, SIGNAL(clicked()), SLOT(clearAllExecutionError())); 03102 03103 // constants 03104 connect(addConstantButton, SIGNAL(clicked()), SLOT(addConstantClicked())); 03105 connect(removeConstantButton, SIGNAL(clicked()), SLOT(removeConstantClicked())); 03106 connect(constantsView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), SLOT(constantsSelectionChanged())); 03107 connect(constantsDefinitionsModel, SIGNAL(dataChanged ( const QModelIndex &, const QModelIndex & ) ), SLOT(recompileAll())); 03108 connect(constantsDefinitionsModel, SIGNAL(dataChanged ( const QModelIndex &, const QModelIndex & ) ), SLOT(updateWindowTitle())); 03109 03110 // target events 03111 connect(target, SIGNAL(nodeConnected(unsigned)), SLOT(nodeConnected(unsigned))); 03112 connect(target, SIGNAL(nodeDisconnected(unsigned)), SLOT(nodeDisconnected(unsigned))); 03113 connect(target, SIGNAL(networkDisconnected()), SLOT(networkDisconnected())); 03114 03115 connect(target, SIGNAL(userEvent(unsigned, const VariablesDataVector &)), SLOT(userEvent(unsigned, const VariablesDataVector &))); 03116 connect(target, SIGNAL(userEventsDropped(unsigned)), SLOT(userEventsDropped(unsigned))); 03117 connect(target, SIGNAL(arrayAccessOutOfBounds(unsigned, unsigned, unsigned, unsigned)), SLOT(arrayAccessOutOfBounds(unsigned, unsigned, unsigned, unsigned))); 03118 connect(target, SIGNAL(divisionByZero(unsigned, unsigned)), SLOT(divisionByZero(unsigned, unsigned))); 03119 connect(target, SIGNAL(eventExecutionKilled(unsigned, unsigned)), SLOT(eventExecutionKilled(unsigned, unsigned))); 03120 connect(target, SIGNAL(nodeSpecificError(unsigned, unsigned, QString)), SLOT(nodeSpecificError(unsigned, unsigned, QString))); 03121 03122 connect(target, SIGNAL(executionPosChanged(unsigned, unsigned)), SLOT(executionPosChanged(unsigned, unsigned))); 03123 connect(target, SIGNAL(executionModeChanged(unsigned, Target::ExecutionMode)), SLOT(executionModeChanged(unsigned, Target::ExecutionMode))); 03124 connect(target, SIGNAL(variablesMemoryEstimatedDirty(unsigned)), SLOT(variablesMemoryEstimatedDirty(unsigned))); 03125 03126 connect(target, SIGNAL(variablesMemoryChanged(unsigned, unsigned, const VariablesDataVector &)), SLOT(variablesMemoryChanged(unsigned, unsigned, const VariablesDataVector &))); 03127 03128 connect(target, SIGNAL(breakpointSetResult(unsigned, unsigned, bool)), SLOT(breakpointSetResult(unsigned, unsigned, bool))); 03129 } 03130 03131 void MainWindow::regenerateOpenRecentMenu() 03132 { 03133 openRecentMenu->clear(); 03134 03135 // Add all other actions excepted the one we are processing 03136 QSettings settings; 03137 QStringList recentFiles = settings.value("recent files").toStringList(); 03138 for (int i = 0; i < recentFiles.size(); i++) 03139 { 03140 const QString& fileName(recentFiles.at(i)); 03141 openRecentMenu->addAction(fileName, this, SLOT(openRecentFile())); 03142 } 03143 } 03144 03145 void MainWindow::updateRecentFiles(const QString& fileName) 03146 { 03147 QSettings settings; 03148 QStringList recentFiles = settings.value("recent files").toStringList(); 03149 if (recentFiles.contains(fileName)) 03150 recentFiles.removeAt(recentFiles.indexOf(fileName)); 03151 recentFiles.push_front(fileName); 03152 const int maxRecentFiles = 8; 03153 if (recentFiles.size() > maxRecentFiles) 03154 recentFiles.pop_back(); 03155 settings.setValue("recent files", recentFiles); 03156 } 03157 03158 void MainWindow::regenerateToolsMenus() 03159 { 03160 writeBytecodeMenu->clear(); 03161 rebootMenu->clear(); 03162 saveBytecodeMenu->clear(); 03163 03164 unsigned activeVMCount(0); 03165 for (int i = 0; i < nodes->count(); i++) 03166 { 03167 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 03168 if (tab) 03169 { 03170 QAction *act = writeBytecodeMenu->addAction(tr("...inside %0").arg(target->getName(tab->nodeId())),tab, SLOT(writeBytecode())); 03171 connect(tab, SIGNAL(uploadReadynessChanged(bool)), act, SLOT(setEnabled(bool))); 03172 03173 rebootMenu->addAction(tr("...%0").arg(target->getName(tab->nodeId())),tab, SLOT(reboot())); 03174 03175 act = saveBytecodeMenu->addAction(tr("...of %0").arg(target->getName(tab->nodeId())),tab, SLOT(saveBytecode())); 03176 connect(tab, SIGNAL(uploadReadynessChanged(bool)), act, SLOT(setEnabled(bool))); 03177 03178 ++activeVMCount; 03179 } 03180 } 03181 03182 writeBytecodeMenu->addSeparator(); 03183 writeAllBytecodesAct = writeBytecodeMenu->addAction(tr("...inside all nodes"), this, SLOT(writeAllBytecodes())); 03184 03185 rebootMenu->addSeparator(); 03186 rebootMenu->addAction(tr("...all nodes"), this, SLOT(rebootAllNodes())); 03187 03188 globalToolBar->setVisible(activeVMCount > 1); 03189 } 03190 03191 void MainWindow::generateHelpMenu() 03192 { 03193 helpMenu->addAction(tr("&User Manual..."), this, SLOT(showUserManual()), QKeySequence(tr("F1", "Help|User Manual"))); 03194 helpMenu->addSeparator(); 03195 03196 helpMenuTargetSpecificSeparator = helpMenu->addSeparator(); 03197 helpMenu->addAction(tr("Web site Aseba..."), this, SLOT(openToUrlFromAction()))->setData(QUrl(tr("http://aseba.wikidot.com/en:start"))); 03198 helpMenu->addAction(tr("Report bug..."), this, SLOT(openToUrlFromAction()))->setData(QUrl(tr("http://github.com/aseba-community/aseba/issues/new"))); 03199 03200 #ifdef Q_WS_MAC 03201 helpMenu->addAction("about", this, SLOT(about())); 03202 helpMenu->addAction("About &Qt...", qApp, SLOT(aboutQt())); 03203 #else // Q_WS_MAC 03204 helpMenu->addSeparator(); 03205 helpMenu->addAction(tr("&About..."), this, SLOT(about())); 03206 helpMenu->addAction(tr("About &Qt..."), qApp, SLOT(aboutQt())); 03207 #endif // Q_WS_MAC 03208 } 03209 03210 void MainWindow::regenerateHelpMenu() 03211 { 03212 // remove old target-specific actions 03213 while (!targetSpecificHelp.isEmpty()) 03214 { 03215 QAction *action(targetSpecificHelp.takeFirst()); 03216 helpMenu->removeAction(action); 03217 delete action; 03218 } 03219 03220 // add back target-specific actions 03221 typedef std::set<int> ProductIds; 03222 ProductIds productIds; 03223 for (int i = 0; i < nodes->count(); i++) 03224 { 03225 NodeTab* tab = dynamic_cast<NodeTab*>(nodes->widget(i)); 03226 if (tab) 03227 productIds.insert(tab->productId()); 03228 } 03229 for (ProductIds::const_iterator it(productIds.begin()); it != productIds.end(); ++it) 03230 { 03231 QAction *action; 03232 switch (*it) 03233 { 03234 case ASEBA_PID_THYMIO2: 03235 action = new QAction(tr("Thymio programming tutorial..."), helpMenu); 03236 connect(action, SIGNAL(triggered()), SLOT(openToUrlFromAction())); 03237 action->setData(QUrl(tr("http://aseba.wikidot.com/en:thymiotutoriel"))); 03238 targetSpecificHelp.append(action); 03239 helpMenu->insertAction(helpMenuTargetSpecificSeparator, action); 03240 action = new QAction(tr("Thymio programming interface..."), helpMenu); 03241 connect(action, SIGNAL(triggered()), SLOT(openToUrlFromAction())); 03242 action->setData(QUrl(tr("http://aseba.wikidot.com/en:thymioapi"))); 03243 targetSpecificHelp.append(action); 03244 helpMenu->insertAction(helpMenuTargetSpecificSeparator, action); 03245 break; 03246 03247 case ASEBA_PID_CHALLENGE: 03248 action = new QAction(tr("Challenge tutorial..."), helpMenu); 03249 connect(action, SIGNAL(triggered()), SLOT(openToUrlFromAction())); 03250 action->setData(QUrl(tr("http://aseba.wikidot.com/en:gettingstarted"))); 03251 targetSpecificHelp.append(action); 03252 helpMenu->insertAction(helpMenuTargetSpecificSeparator, action); 03253 break; 03254 03255 case ASEBA_PID_MARXBOT: 03256 action = new QAction(tr("MarXbot user manual..."), helpMenu); 03257 connect(action, SIGNAL(triggered()), SLOT(openToUrlFromAction())); 03258 action->setData(QUrl(tr("http://mobots.epfl.ch/data/robots/marxbot-user-manual.pdf"))); 03259 targetSpecificHelp.append(action); 03260 helpMenu->insertAction(helpMenuTargetSpecificSeparator, action); 03261 break; 03262 03263 default: 03264 break; 03265 } 03266 } 03267 } 03268 03269 void MainWindow::openToUrlFromAction() const 03270 { 03271 const QAction *action(reinterpret_cast<QAction *>(sender())); 03272 QDesktopServices::openUrl(action->data().toUrl()); 03273 } 03274 03275 void MainWindow::setupMenu() 03276 { 03277 // File menu 03278 QMenu *fileMenu = new QMenu(tr("&File"), this); 03279 menuBar()->addMenu(fileMenu); 03280 03281 fileMenu->addAction(QIcon(":/images/filenew.png"), tr("&New"), 03282 this, SLOT(newFile()), 03283 QKeySequence(tr("Ctrl+N", "File|New"))); 03284 fileMenu->addAction(QIcon(":/images/fileopen.png"), tr("&Open..."), 03285 this, SLOT(openFile()), 03286 QKeySequence(tr("Ctrl+O", "File|Open"))); 03287 openRecentMenu = new QMenu(tr("Open &Recent"), fileMenu); 03288 regenerateOpenRecentMenu(); 03289 fileMenu->addMenu(openRecentMenu)->setIcon(QIcon(":/images/fileopen.png")); 03290 03291 fileMenu->addAction(QIcon(":/images/filesave.png"), tr("&Save..."), 03292 this, SLOT(save()), 03293 QKeySequence(tr("Ctrl+S", "File|Save"))); 03294 fileMenu->addAction(QIcon(":/images/filesaveas.png"), tr("Save &As..."), 03295 this, SLOT(saveFile())); 03296 03297 fileMenu->addSeparator(); 03298 /*fileMenu->addAction(QIcon(":/images/network.png"), tr("Connect to &target"), 03299 target, SLOT(connect()), 03300 QKeySequence(tr("Ctrl+T", "File|Connect to target"))); 03301 fileMenu->addSeparator();*/ 03302 fileMenu->addAction(QIcon(":/images/filesaveas.png"), tr("Export &memories content..."), 03303 this, SLOT(exportMemoriesContent())); 03304 fileMenu->addAction(QIcon(":/images/fileopen.png"), tr("&Import memories content..."), 03305 this, SLOT(importMemoriesContent())); 03306 fileMenu->addSeparator(); 03307 #ifdef Q_WS_MAC 03308 fileMenu->addAction(QIcon(":/images/exit.png"), "quit", 03309 this, SLOT(close()), 03310 QKeySequence(tr("Ctrl+Q", "File|Quit"))); 03311 #else // Q_WS_MAC 03312 fileMenu->addAction(QIcon(":/images/exit.png"), tr("&Quit"), 03313 this, SLOT(close()), 03314 QKeySequence(tr("Ctrl+Q", "File|Quit"))); 03315 #endif // Q_WS_MAC 03316 03317 // Edit menu 03318 cutAct = new QAction(QIcon(":/images/editcut.png"), tr("Cu&t"), this); 03319 cutAct->setShortcut(tr("Ctrl+X", "Edit|Cut")); 03320 cutAct->setEnabled(false); 03321 03322 copyAct = new QAction(QIcon(":/images/editcopy.png"), tr("&Copy"), this); 03323 copyAct->setShortcut(tr("Ctrl+C", "Edit|Copy")); 03324 copyAct->setEnabled(false); 03325 03326 pasteAct = new QAction(QIcon(":/images/editpaste.png"), tr("&Paste"), this); 03327 pasteAct->setShortcut(tr("Ctrl+V", "Edit|Paste")); 03328 pasteAct->setEnabled(false); 03329 03330 undoAct = new QAction(QIcon(":/images/undo.png"), tr("&Undo"), this); 03331 undoAct->setShortcut(tr("Ctrl+Z", "Edit|Undo")); 03332 undoAct->setEnabled(false); 03333 03334 redoAct = new QAction(QIcon(":/images/redo.png"), tr("Re&do"), this); 03335 redoAct->setShortcut(tr("Ctrl+Shift+Z", "Edit|Redo")); 03336 redoAct->setEnabled(false); 03337 03338 findAct = new QAction(QIcon(":/images/find.png"), tr("&Find..."), this); 03339 findAct->setShortcut(tr("Ctrl+F", "Edit|Find")); 03340 connect(findAct, SIGNAL(triggered()), SLOT(findTriggered())); 03341 findAct->setEnabled(false); 03342 03343 replaceAct = new QAction(QIcon(":/images/edit.png"), tr("&Replace..."), this); 03344 replaceAct->setShortcut(tr("Ctrl+R", "Edit|Replace")); 03345 connect(replaceAct, SIGNAL(triggered()), SLOT(replaceTriggered())); 03346 replaceAct->setEnabled(false); 03347 03348 commentAct = new QAction(tr("Comment the selection"), this); 03349 commentAct->setShortcut(tr("Ctrl+D", "Edit|Comment the selection")); 03350 connect(commentAct, SIGNAL(triggered()), SLOT(commentTriggered())); 03351 03352 uncommentAct = new QAction(tr("Uncomment the selection"), this); 03353 uncommentAct->setShortcut(tr("Shift+Ctrl+D", "Edit|Uncomment the selection")); 03354 connect(uncommentAct, SIGNAL(triggered()), SLOT(uncommentTriggered())); 03355 03356 QMenu *editMenu = new QMenu(tr("&Edit"), this); 03357 menuBar()->addMenu(editMenu); 03358 editMenu->addAction(cutAct); 03359 editMenu->addAction(copyAct); 03360 editMenu->addAction(pasteAct); 03361 editMenu->addSeparator(); 03362 editMenu->addAction(QIcon(":/images/editcopy.png"), tr("Copy &all"), this, SLOT(copyAll())); 03363 editMenu->addSeparator(); 03364 editMenu->addAction(undoAct); 03365 editMenu->addAction(redoAct); 03366 editMenu->addSeparator(); 03367 editMenu->addAction(findAct); 03368 editMenu->addAction(replaceAct); 03369 editMenu->addSeparator(); 03370 editMenu->addAction(commentAct); 03371 editMenu->addAction(uncommentAct); 03372 03373 // View menu 03374 showKeywordsAct = new QAction(tr("Show &keywords"), this); 03375 showKeywordsAct->setCheckable(true); 03376 connect(showKeywordsAct, SIGNAL(toggled(bool)), SLOT(showKeywords(bool))); 03377 03378 showMemoryUsageAct = new QAction(tr("Show memory usage"), this); 03379 showMemoryUsageAct->setCheckable(true); 03380 connect(showMemoryUsageAct, SIGNAL(toggled(bool)), SLOT(showMemoryUsage(bool))); 03381 03382 showHiddenAct = new QAction(tr("S&how hidden variables and functions"), this); 03383 showHiddenAct->setCheckable(true); 03384 connect(showHiddenAct, SIGNAL(toggled(bool)), SLOT(showHidden(bool))); 03385 03386 showLineNumbers = new QAction(tr("Show Line Numbers"), this); 03387 showLineNumbers->setShortcut(tr("F11", "Edit|Show Line Numbers")); 03388 showLineNumbers->setCheckable(true); 03389 connect(showLineNumbers, SIGNAL(toggled(bool)), SLOT(showLineNumbersChanged(bool))); 03390 03391 goToLineAct = new QAction(QIcon(":/images/goto.png"), tr("&Go To Line..."), this); 03392 goToLineAct->setShortcut(tr("Ctrl+G", "Edit|Go To Line")); 03393 goToLineAct->setEnabled(false); 03394 connect(goToLineAct, SIGNAL(triggered()), SLOT(goToLine())); 03395 03396 QMenu *viewMenu = new QMenu(tr("&View"), this); 03397 viewMenu->addAction(showKeywordsAct); 03398 viewMenu->addAction(showMemoryUsageAct); 03399 viewMenu->addAction(showHiddenAct); 03400 viewMenu->addSeparator(); 03401 viewMenu->addAction(showLineNumbers); 03402 viewMenu->addAction(goToLineAct); 03403 viewMenu->addSeparator(); 03404 viewMenu->addAction(tr("&Settings"), this, SLOT(showSettings())); 03405 menuBar()->addMenu(viewMenu); 03406 03407 // Debug actions 03408 loadAllAct = new QAction(QIcon(":/images/upload.png"), tr("&Load all"), this); 03409 loadAllAct->setShortcut(tr("F7", "Load|Load all")); 03410 03411 resetAllAct = new QAction(QIcon(":/images/reset.png"), tr("&Reset all"), this); 03412 resetAllAct->setShortcut(tr("F8", "Debug|Reset all")); 03413 03414 runAllAct = new QAction(QIcon(":/images/play.png"), tr("Ru&n all"), this); 03415 runAllAct->setShortcut(tr("F9", "Debug|Run all")); 03416 03417 pauseAllAct = new QAction(QIcon(":/images/pause.png"), tr("&Pause all"), this); 03418 pauseAllAct->setShortcut(tr("F10", "Debug|Pause all")); 03419 03420 // Debug toolbar 03421 globalToolBar = addToolBar(tr("Debug")); 03422 globalToolBar->setObjectName("debug toolbar"); 03423 globalToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); 03424 globalToolBar->addAction(loadAllAct); 03425 globalToolBar->addAction(resetAllAct); 03426 globalToolBar->addAction(runAllAct); 03427 globalToolBar->addAction(pauseAllAct); 03428 03429 // Debug menu 03430 toggleBreakpointAct = new QAction(tr("Toggle breakpoint"), this); 03431 toggleBreakpointAct->setShortcut(tr("Ctrl+B", "Debug|Toggle breakpoint")); 03432 connect(toggleBreakpointAct, SIGNAL(triggered()), SLOT(toggleBreakpoint())); 03433 03434 clearAllBreakpointsAct = new QAction(tr("Clear all breakpoints"), this); 03435 //clearAllBreakpointsAct->setShortcut(); 03436 connect(clearAllBreakpointsAct, SIGNAL(triggered()), SLOT(clearAllBreakpoints())); 03437 03438 QMenu *debugMenu = new QMenu(tr("&Debug"), this); 03439 menuBar()->addMenu(debugMenu); 03440 debugMenu->addAction(toggleBreakpointAct); 03441 debugMenu->addAction(clearAllBreakpointsAct); 03442 debugMenu->addSeparator(); 03443 debugMenu->addAction(loadAllAct); 03444 debugMenu->addAction(resetAllAct); 03445 debugMenu->addAction(runAllAct); 03446 debugMenu->addAction(pauseAllAct); 03447 03448 // Tool menu 03449 QMenu *toolMenu = new QMenu(tr("&Tools"), this); 03450 menuBar()->addMenu(toolMenu); 03451 /*toolMenu->addAction(QIcon(":/images/view_text.png"), tr("&Show last compilation messages"), 03452 this, SLOT(showCompilationMessages()), 03453 QKeySequence(tr("Ctrl+M", "Tools|Show last compilation messages")));*/ 03454 showCompilationMsg = new QAction(QIcon(":/images/view_text.png"), tr("&Show last compilation messages"), this); 03455 showCompilationMsg->setCheckable(true); 03456 toolMenu->addAction(showCompilationMsg); 03457 connect(showCompilationMsg, SIGNAL(toggled(bool)), SLOT(showCompilationMessages(bool))); 03458 connect(compilationMessageBox, SIGNAL(hidden()), SLOT(compilationMessagesWasHidden())); 03459 toolMenu->addSeparator(); 03460 writeBytecodeMenu = new QMenu(tr("Write the program(s)..."), toolMenu); 03461 toolMenu->addMenu(writeBytecodeMenu); 03462 rebootMenu = new QMenu(tr("Reboot..."), toolMenu); 03463 toolMenu->addMenu(rebootMenu); 03464 saveBytecodeMenu = new QMenu(tr("Save the binary code..."), toolMenu); 03465 toolMenu->addMenu(saveBytecodeMenu); 03466 03467 // Help menu 03468 helpMenu = new QMenu(tr("&Help"), this); 03469 menuBar()->addMenu(helpMenu); 03470 generateHelpMenu(); 03471 regenerateHelpMenu(); 03472 03473 // add dynamic stuff 03474 regenerateToolsMenus(); 03475 03476 // Load the state from the settings (thus from hard drive) 03477 applySettings(); 03478 } 03480 03483 bool MainWindow::askUserBeforeDiscarding() 03484 { 03485 const bool anythingModified = sourceModified || constantsDefinitionsModel->checkIfModified() || eventsDescriptionsModel->checkIfModified(); 03486 if (anythingModified == false) 03487 return true; 03488 03489 QString docName(tr("Untitled")); 03490 if (!actualFileName.isEmpty()) 03491 docName = actualFileName.mid(actualFileName.lastIndexOf("/") + 1); 03492 03493 QMessageBox msgBox; 03494 msgBox.setWindowTitle(tr("Aseba Studio - Confirmation Dialog")); 03495 msgBox.setText(tr("The document \"%0\" has been modified.").arg(docName)); 03496 msgBox.setInformativeText(tr("Do you want to save your changes or discard them?")); 03497 msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); 03498 msgBox.setDefaultButton(QMessageBox::Save); 03499 03500 int ret = msgBox.exec(); 03501 switch (ret) 03502 { 03503 case QMessageBox::Save: 03504 // Save was clicked 03505 if (save()) 03506 return true; 03507 else 03508 return false; 03509 case QMessageBox::Discard: 03510 // Don't Save was clicked 03511 return true; 03512 case QMessageBox::Cancel: 03513 // Cancel was clicked 03514 return false; 03515 default: 03516 // should never be reached 03517 assert(false); 03518 break; 03519 } 03520 03521 return false; 03522 } 03523 03524 void MainWindow::closeEvent ( QCloseEvent * event ) 03525 { 03526 if (askUserBeforeDiscarding()) 03527 { 03528 writeSettings(); 03529 event->accept(); 03530 emit MainWindowClosed(); 03531 } 03532 else 03533 { 03534 event->ignore(); 03535 } 03536 } 03537 03538 bool MainWindow::readSettings() 03539 { 03540 bool result; 03541 03542 QSettings settings; 03543 result = restoreGeometry(settings.value("MainWindow/geometry").toByteArray()); 03544 restoreState(settings.value("MainWindow/windowState").toByteArray()); 03545 return result; 03546 } 03547 03548 void MainWindow::writeSettings() 03549 { 03550 QSettings settings; 03551 settings.setValue("MainWindow/geometry", saveGeometry()); 03552 settings.setValue("MainWindow/windowState", saveState()); 03553 } 03554 03555 void MainWindow::updateWindowTitle() 03556 { 03557 const bool anythingModified = sourceModified || constantsDefinitionsModel->checkIfModified() || eventsDescriptionsModel->checkIfModified(); 03558 03559 QString modifiedText; 03560 if (anythingModified) 03561 modifiedText = tr("[modified] "); 03562 03563 QString docName(tr("Untitled")); 03564 if (!actualFileName.isEmpty()) 03565 docName = actualFileName.mid(actualFileName.lastIndexOf("/") + 1); 03566 03567 setWindowTitle(tr("%0 %1- Aseba Studio").arg(docName).arg(modifiedText)); 03568 } 03569 03570 void MainWindow::applySettings() 03571 { 03572 showKeywordsAct->setChecked(ConfigDialog::getShowKeywordToolbar()); 03573 showMemoryUsageAct->setChecked(ConfigDialog::getShowMemoryUsage()); 03574 showHiddenAct->setChecked(ConfigDialog::getShowHidden()); 03575 showLineNumbers->setChecked(ConfigDialog::getShowLineNumbers()); 03576 } 03577 03579 }; // Aseba 03580