diff --git a/libgraphtheory/CMakeLists.txt b/libgraphtheory/CMakeLists.txt index 0065feb1..223cd790 100644 --- a/libgraphtheory/CMakeLists.txt +++ b/libgraphtheory/CMakeLists.txt @@ -1,128 +1,129 @@ # Copyright 2014 Andreas Cord-Landwehr # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. include_directories( ./ ) add_definitions(-DTRANSLATION_DOMAIN=\"libgraphtheory\") set(graphtheory_SRCS edge.cpp edgetype.cpp edgetypestyle.cpp graphdocument.cpp logging.cpp node.cpp nodetype.cpp nodetypestyle.cpp editor.cpp view.cpp dialogs/nodeproperties.cpp dialogs/edgeproperties.cpp dialogs/nodetypeproperties.cpp dialogs/edgetypeproperties.cpp dialogs/propertieswidget.cpp dialogs/propertydelegate.cpp kernel/documentwrapper.cpp kernel/nodewrapper.cpp kernel/edgewrapper.cpp kernel/kernel.cpp kernel/modules/console/consolemodule.cpp models/nodemodel.cpp models/edgemodel.cpp models/nodepropertymodel.cpp models/edgepropertymodel.cpp models/nodetypemodel.cpp models/nodetypepropertymodel.cpp models/edgetypemodel.cpp models/edgetypepropertymodel.cpp modifiers/valueassign.cpp modifiers/topology.cpp fileformats/fileformatinterface.cpp fileformats/fileformatmanager.cpp editorplugins/editorplugininterface.cpp editorplugins/editorpluginmanager.cpp qtquickitems/nodeitem.cpp qtquickitems/edgeitem.cpp qtquickitems/qsgarrowheadnode.cpp qtquickitems/qsglinenode.cpp ) qt5_add_resources(graphtheory_SRCS qml/rocs.qrc) ki18n_wrap_ui(graphtheory_SRCS dialogs/nodeproperties.ui dialogs/edgeproperties.ui ) add_library(rocsgraphtheory SHARED ${graphtheory_SRCS} ) generate_export_header(rocsgraphtheory BASE_NAME graphtheory) target_link_libraries(rocsgraphtheory PUBLIC Qt5::Core Qt5::Quick Qt5::QuickWidgets Qt5::Gui Qt5::Script + Qt5::ScriptTools KF5::I18n KF5::ItemViews KF5::Declarative KF5::XmlGui KF5::Service ) set(rocscore_LIB_HDRS edge.h graphdocument.h node.h ) # KI18N Translation Domain for library add_definitions(-DTRANSLATION_DOMAIN=\"libgraphtheory\") # we use SOVERION 0 to make clear that this is neither stable API nor stable ABI # i.e., the library for now is only used internally in Rocs and we require the exact # version as released alongside Rocs set(GRAPHTHEORY_LIB_VERSION "0.0.1") set(GRAPHTHEORY_LIB_SOVERSION "0") set_target_properties( rocsgraphtheory PROPERTIES # VERSION ${GRAPHTHEORY_LIB_VERSION} SOVERSION ${GRAPHTHEORY_LIB_SOVERSION} ) install(TARGETS rocsgraphtheory ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${rocscore_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/rocs COMPONENT Devel) # Boost requires exceptions kde_source_files_enable_exceptions(modifiers/topology.cpp modifiers/valueassign.cpp) ecm_optional_add_subdirectory(fileformats) ecm_optional_add_subdirectory(editorplugins) ecm_optional_add_subdirectory(kernel) ecm_optional_add_subdirectory(modifiers) ecm_optional_add_subdirectory(qml) ecm_optional_add_subdirectory(autotests) ecm_optional_add_subdirectory(tests) diff --git a/libgraphtheory/kernel/kernel.cpp b/libgraphtheory/kernel/kernel.cpp index d5a4889f..f335efa0 100644 --- a/libgraphtheory/kernel/kernel.cpp +++ b/libgraphtheory/kernel/kernel.cpp @@ -1,134 +1,152 @@ /* * Copyright 2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "kernel.h" #include "graphdocument.h" #include "documentwrapper.h" #include "nodewrapper.h" #include "edgewrapper.h" #include "logging_p.h" #include "kernel/modules/console/consolemodule.h" #include #include +#include using namespace GraphTheory; class GraphTheory::KernelPrivate { public: KernelPrivate() - : m_engine(nullptr) + : m_engine(new QScriptEngine), + m_debugger(new QScriptEngineDebugger) { } ~KernelPrivate() { + m_debugger->detach(); } QScriptValue registerGlobalObject(QObject *qobject, const QString &name); QScriptEngine *m_engine; + QScriptEngineDebugger *m_debugger; ConsoleModule m_consoleModule; }; QScriptValue KernelPrivate::registerGlobalObject(QObject *qobject, const QString &name) { if (!m_engine) { qCCritical(GRAPHTHEORY_KERNEL) << "No engine set, aborting global object creation."; return 0; } QScriptValue globalObject = m_engine->newQObject(qobject); m_engine->globalObject().setProperty(name, globalObject); return globalObject; } + ///BEGIN: Kernel Kernel::Kernel() : d(new KernelPrivate) { connect(&d->m_consoleModule, &ConsoleModule::message, this, &Kernel::processMessage); } Kernel::~Kernel() { } QScriptValue Kernel::execute(GraphDocumentPtr document, const QString &script) { - if (!d->m_engine) { - d->m_engine = new QScriptEngine(this); - } // register meta types qScriptRegisterSequenceMetaType >(d->m_engine); qScriptRegisterSequenceMetaType >(d->m_engine); qRegisterMetaType(); qRegisterMetaType(); if (d->m_engine->isEvaluating()) { d->m_engine->abortEvaluation(); } d->m_engine->collectGarbage(); d->m_engine->pushContext(); // add document DocumentWrapper documentWrapper(document, d->m_engine); d->m_engine->globalObject().setProperty("Document", d->m_engine->newQObject(&documentWrapper)); connect(&documentWrapper, &DocumentWrapper::message, this, &Kernel::processMessage); // set modules d->m_engine->globalObject().setProperty("Console", d->m_engine->newQObject(&d->m_consoleModule)); // set evaluation d->m_engine->setProcessEventsInterval(100); //! TODO: Make that changeable. QScriptValue result = d->m_engine->evaluate(script).toString(); if (d->m_engine && d->m_engine->hasUncaughtException()) { emit message(result.toString(), WarningMessage); emit message(d->m_engine->uncaughtExceptionBacktrace().join("\n"), InfoMessage); } if (d->m_engine) { emit message(i18nc("@info status message after successful script execution", "Execution Finished"), InfoMessage); emit message(result.toString(), InfoMessage); d->m_engine->popContext(); } // end processing messages disconnect(&documentWrapper, &DocumentWrapper::message, this, &Kernel::processMessage); emit executionFinished(); d->m_engine->globalObject().setProperty("Document", QScriptValue()); return result; } void Kernel::stop() { d->m_engine->abortEvaluation(); } void Kernel::processMessage(const QString &messageString, Kernel::MessageType type) { emit message(messageString, type); } +void Kernel::attachDebugger() +{ + d->m_debugger->attachTo(d->m_engine); +} + +void Kernel::detachDebugger() +{ + d->m_debugger->detach(); + d->m_debugger = new QScriptEngineDebugger; +} + +void Kernel::triggerInterruptAction() +{ + d->m_debugger->action(QScriptEngineDebugger::InterruptAction)->trigger(); +} + //END: Kernel diff --git a/libgraphtheory/kernel/kernel.h b/libgraphtheory/kernel/kernel.h index d804098d..cf7d2e49 100644 --- a/libgraphtheory/kernel/kernel.h +++ b/libgraphtheory/kernel/kernel.h @@ -1,74 +1,79 @@ /* * Copyright 2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef KERNEL_H #define KERNEL_H #include "graphtheory_export.h" #include "typenames.h" #include "node.h" #include "graphdocument.h" #include #include +#include + namespace GraphTheory { class KernelPrivate; /** * \class Kernel */ class GRAPHTHEORY_EXPORT Kernel : public QObject { Q_OBJECT public: enum MessageType { InfoMessage, WarningMessage, ErrorMessage }; Kernel(); virtual ~Kernel(); /** * execute javascript @p script on @p document and @return result as reported by engine */ QScriptValue execute(GraphTheory::GraphDocumentPtr document, const QString &script); void stop(); + void attachDebugger(); + void detachDebugger(); + void triggerInterruptAction(); private Q_SLOTS: /** process all incoming messages and resend them afterwards**/ void processMessage(const QString &message, GraphTheory::Kernel::MessageType type); Q_SIGNALS: void message(const QString &message, GraphTheory::Kernel::MessageType type); void executionFinished(); private: const QScopedPointer d; }; } #endif diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index 537a2322..40c89b98 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -1,590 +1,610 @@ /* This file is part of Rocs. Copyright 2008-2011 Tomaz Canabrava Copyright 2008 Ugo Sangiori Copyright 2010-2011 Wagner Reck Copyright 2011-2014 Andreas Cord-Landwehr This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "mainwindow.h" #include "rocsversion.h" #include "settings.h" #include "libgraphtheory/editor.h" #include "libgraphtheory/editorplugins/editorpluginmanager.h" #include "libgraphtheory/kernel/kernel.h" #include "libgraphtheory/view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui/documenttypeswidget.h" #include "ui/codeeditorwidget.h" #include "ui/scriptoutputwidget.h" #include "ui/sidedockwidget.h" #include "ui/fileformatdialog.h" #include "ui/journalwidget.h" #include "grapheditorwidget.h" #include "plugins/scriptapi/scriptapiwidget.h" #include "project/project.h" using namespace GraphTheory; MainWindow::MainWindow() : KXmlGuiWindow() , m_currentProject(0) , m_kernel(new Kernel) , m_codeEditorWidget(new CodeEditorWidget(this)) , m_graphEditorWidget(new GraphEditorWidget(this)) , m_outputWidget(new ScriptOutputWidget(this)) { setObjectName("RocsMainWindow"); m_graphEditor = new GraphTheory::Editor(); setupWidgets(); setupActions(); setupGUI(Keys | Save | Create); setupToolsPluginsAction(); // setup kernel connect(m_kernel, &Kernel::message, m_outputWidget, &ScriptOutputWidget::processMessage); // TODO: use welcome widget instead of creating default empty project createProject(); updateCaption(); // update rocs config version Settings::setVersion(ROCS_VERSION_STRING); // disable save action from kpart, since we take care for the editor by global save action // here "file_save" is the action identifier from katepartui.rc // note that we may not use that name for our own actions foreach(KActionCollection *ac, KActionCollection::allCollections()) { if (ac->action("file_save")) { ac->action("file_save")->setDisabled(true); } } } MainWindow::~MainWindow() { Settings::setVSplitterSizeTop(m_vSplitter->sizes() [0]); Settings::setVSplitterSizeBottom(m_vSplitter->sizes() [1]); Settings::setHSplitterSizeLeft(m_hSplitter->sizes() [0]); Settings::setHSplitterSizeRight(m_hSplitter->sizes() [1]); Settings::setHScriptSplitterSizeLeft(m_hScriptSplitter->sizes() [0]); Settings::setHScriptSplitterSizeRight(m_hScriptSplitter->sizes() [1]); m_recentProjects->saveEntries(Settings::self()->config()->group("RecentFiles")); Settings::self()->save(); m_graphEditor->deleteLater(); m_kernel->deleteLater(); } void MainWindow::closeEvent(QCloseEvent *event) { if (queryClose() == true) { event->accept(); return; } else { event->ignore(); return; } } void MainWindow::setupWidgets() { // setup main widgets QWidget *sidePanel = setupSidePanel(); QWidget *scriptPanel = setupScriptPanel(); // splits the main window horizontal m_vSplitter = new QSplitter(this); m_vSplitter->setOrientation(Qt::Vertical); m_vSplitter->addWidget(m_graphEditorWidget); m_vSplitter->addWidget(scriptPanel); // horizontal arrangement m_hSplitter = new QSplitter(this); m_hSplitter->setOrientation(Qt::Horizontal); m_hSplitter->addWidget(m_vSplitter); m_hSplitter->addWidget(sidePanel); // set sizes for script panel m_hScriptSplitter->setSizes(QList() << Settings::hScriptSplitterSizeLeft() << Settings::hScriptSplitterSizeRight() << 80); // set sizes for vertical splitter m_vSplitter->setSizes(QList() << Settings::vSplitterSizeTop() << Settings::vSplitterSizeBottom()); // set sizes for side panel // the following solves the setting of the panel width if it was closed at previous session int panelWidth = Settings::hSplitterSizeRight(); if (panelWidth == 0) { //FIXME this is only a workaround // that fixes the wrong saving of hSplitterSizeRight panelWidth = 400; } m_hSplitter->setSizes(QList() << Settings::hSplitterSizeLeft() << panelWidth); setCentralWidget(m_hSplitter); } QWidget* MainWindow::setupScriptPanel() { m_hScriptSplitter = new QSplitter(this); m_hScriptSplitter->setOrientation(Qt::Horizontal); KToolBar *executeCommands = new KToolBar(this); executeCommands->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); executeCommands->setOrientation(Qt::Vertical); m_runScript = new QAction(QIcon::fromTheme("media-playback-start"), i18nc("@action:intoolbar Script Execution", "Run"), this); m_runScript->setToolTip(i18nc("@info:tooltip", "Execute currently active script on active graph document.")); m_stopScript = new QAction(QIcon::fromTheme("process-stop"), i18nc("@action:intoolbar Script Execution", "Stop"), this); m_stopScript->setToolTip(i18nc("@info:tooltip", "Stop script execution.")); m_stopScript->setEnabled(false); + m_openDebugger = new QAction(QIcon::fromTheme("system-run"), i18nc("@action:intoolbar Open Debugger", "Debugger"), this); + m_openDebugger->setToolTip(i18nc("@info:tooltip", "Open de Javascript code debugger.")); + m_openDebugger->setCheckable(true); executeCommands->addAction(m_runScript); executeCommands->addAction(m_stopScript); + executeCommands->addAction(m_openDebugger); // add actions to action collection to be able to set shortcuts on them in the ui actionCollection()->addAction("_runScript", m_runScript); actionCollection()->addAction("_stopScript", m_stopScript); + actionCollection()->addAction("_openDebugger", m_openDebugger); connect(m_runScript, &QAction::triggered, this, &MainWindow::executeScript); connect(m_stopScript, &QAction::triggered, this, &MainWindow::stopScript); + connect(m_openDebugger, &QAction::triggered, this, &MainWindow::checkDebugger); m_hScriptSplitter->addWidget(m_codeEditorWidget); m_hScriptSplitter->addWidget(m_outputWidget); QWidget *scriptInterface = new QWidget(this); scriptInterface->setLayout(new QHBoxLayout); scriptInterface->layout()->addWidget(m_hScriptSplitter); scriptInterface->layout()->addWidget(executeCommands); return scriptInterface; } QWidget* MainWindow::setupSidePanel() { QWidget *panel = new QWidget(this); panel->setLayout(new QVBoxLayout); panel->setVisible(false); // add sidebar SidedockWidget* sideDock = new SidedockWidget(panel); addToolBar(Qt::RightToolBarArea, sideDock->toolbar()); panel->layout()->addWidget(sideDock); // add widgets to dock // document property widgets DocumentTypesWidget *documentTypesWidget = new DocumentTypesWidget(panel); connect(this, &MainWindow::graphDocumentChanged, documentTypesWidget, &DocumentTypesWidget::setDocument); sideDock->addDock(documentTypesWidget, i18n("Element Types"), QIcon::fromTheme("document-properties")); if (m_currentProject && m_currentProject->activeGraphDocument()) { documentTypesWidget->setDocument(m_currentProject->activeGraphDocument()); } // Project Journal m_journalWidget = new JournalEditorWidget(panel); sideDock->addDock(m_journalWidget, i18nc("@title", "Journal"), QIcon::fromTheme("story-editor")); // Rocs scripting API documentation ScriptApiWidget* apiDoc = new ScriptApiWidget(panel); sideDock->addDock(apiDoc, i18nc("@title", "Scripting API"), QIcon::fromTheme("documentation")); return panel; } void MainWindow::setProject(Project *project) { m_codeEditorWidget->setProject(project); m_graphEditorWidget->setProject(project); m_journalWidget->openJournal(project); updateCaption(); if (m_currentProject) { m_currentProject->disconnect(this); m_currentProject->deleteLater(); } connect(project, static_cast(&Project::activeGraphDocumentChanged), this, &MainWindow::graphDocumentChanged); connect(project, &Project::modifiedChanged, this, &MainWindow::updateCaption); m_currentProject = project; emit graphDocumentChanged(m_currentProject->activeGraphDocument()); } void MainWindow::setupActions() { KStandardAction::quit(this, SLOT(quit()), actionCollection()); KStandardAction::preferences(this, SLOT(showConfigurationDialog()), actionCollection()); // setup graph visual editor actions and add them to mainwindow action collection // m_graphEditor->setupActions(actionCollection()); //FIXME add editor actions to main action collection // Menu actions QAction *newProjectAction = new QAction(QIcon::fromTheme("document-new"), i18nc("@action:inmenu", "New Project"), this); newProjectAction->setShortcutContext(Qt::ApplicationShortcut); actionCollection()->addAction("new-project", newProjectAction); actionCollection()->setDefaultShortcut(newProjectAction, QKeySequence::New); connect(newProjectAction, &QAction::triggered, this, &MainWindow::createProject); QAction *projectSaveAction = new QAction(QIcon::fromTheme("document-save"), i18nc("@action:inmenu", "Save Project"), this); projectSaveAction->setShortcutContext(Qt::ApplicationShortcut); actionCollection()->addAction("save-project", projectSaveAction); actionCollection()->setDefaultShortcut(projectSaveAction, QKeySequence::Save); connect(projectSaveAction, &QAction::triggered, this, &MainWindow::saveProject); QAction *projectOpenAction = new QAction(QIcon::fromTheme("document-open"), i18nc("@action:inmenu", "Open Project"), this); projectOpenAction->setShortcutContext(Qt::ApplicationShortcut); actionCollection()->addAction("open-project", projectOpenAction); actionCollection()->setDefaultShortcut(projectOpenAction, QKeySequence::Open); connect(projectOpenAction, &QAction::triggered, this, [=] () { openProject(); }); m_recentProjects = new KRecentFilesAction(QIcon ("document-open"), i18nc("@action:inmenu","Recent Projects"), this); connect(m_recentProjects, &KRecentFilesAction::urlSelected, this, &MainWindow::openProject); actionCollection()->addAction("recent-project", m_recentProjects); m_recentProjects->loadEntries(Settings::self()->config()->group("RecentFiles")); createAction("document-save-as", i18nc("@action:inmenu", "Save Project as"), "save-project-as", SLOT(saveProjectAs()), this); createAction("document-new", i18nc("@action:inmenu", "New Graph Document"), "new-graph", SLOT(createGraphDocument()), this); createAction("document-new", i18nc("@action:inmenu", "New Script File"), "new-script", SLOT(tryToCreateCodeDocument()), this); createAction("document-import", i18nc("@action:inmenu", "Import Graph"), "import-graph", SLOT(importGraphDocument()), this); createAction("document-export", i18nc("@action:inmenu", "Export Graph as"), "export-graph-as", SLOT(exportGraphDocument()), this); createAction("document-import", i18nc("@action:inmenu", "Import Script"), "add-script", SLOT(importCodeDocument()), this); createAction("document-export", i18nc("@action:inmenu", "Export Script"), "export-script", SLOT(exportCodeDocument()), this); } void MainWindow::createAction(const QByteArray& iconName, const QString& actionTitle, const QString& actionName, const char* slot, QObject *parent) { QAction* action = new QAction(QIcon::fromTheme(iconName), actionTitle, parent); actionCollection()->addAction(actionName, action); connect(action, SIGNAL(triggered(bool)), parent, slot); } void MainWindow::showConfigurationDialog() { QPointer dialog = new KConfigDialog(this, "settings", Settings::self()); KTextEditor::Editor *editor = KTextEditor::Editor::instance(); for (int index = 0; index < editor->configPages(); ++index) { KTextEditor::ConfigPage *page = editor->configPage(index, dialog); dialog->addPage(page, page->name(), page->icon().name(), page->fullName()); } dialog->exec(); } void MainWindow::setupToolsPluginsAction() { QList availablePlugins = m_graphEditorPluginManager.plugins(); QList actions; int count = 0; for (auto plugin : availablePlugins) { QAction *action = new QAction(plugin->displayName(), this); action->setData(count++); connect(action, &QAction::triggered, this, &MainWindow::showEditorPluginDialog); actions << action; } unplugActionList("tools_plugins"); plugActionList("tools_plugins", actions); } void MainWindow::importCodeDocument() { QString startDirectory = Settings::lastOpenedDirectory(); QUrl fileUrl = QUrl::fromLocalFile(QFileDialog::getOpenFileName(this, i18nc("@title:window", "Import Script into Project"), startDirectory)); if (fileUrl.isEmpty()) { return; } m_currentProject->importCodeDocument(fileUrl); Settings::setLastOpenedDirectory(startDirectory); } void MainWindow::exportCodeDocument() { QString startDirectory = Settings::lastOpenedDirectory(); QUrl fileUrl = QUrl::fromLocalFile(QFileDialog::getSaveFileName(this, i18nc("@title:window", "Export Script"), startDirectory, i18n("JavaScript (*.js)"))); m_codeEditorWidget->activeDocument()->saveAs(fileUrl); } void MainWindow::createProject() { if (!queryClose()) { return; } Project *project = new Project(m_graphEditor); project->createCodeDocument(i18n("untitled")); project->addGraphDocument(m_graphEditor->createDocument()); project->setModified(false); setProject(project); } void MainWindow::saveProject() { if (m_currentProject->projectUrl().isEmpty()) { saveProjectAs(); return; } else { m_currentProject->projectSave(); m_recentProjects->addUrl(m_currentProject->projectUrl()); } updateCaption(); } void MainWindow::saveProjectAs() { QString startDirectory = Settings::lastOpenedDirectory(); QString file = QFileDialog::getSaveFileName(this, i18nc("@title:window", "Save Project As"), startDirectory, i18n("Rocs Projects (*.rocs)")); if (file.isEmpty()) { qCritical() << "Filename is empty and no script file was created."; return; } QFileInfo fi(file); if (fi.exists()) { const int btnCode = KMessageBox::warningContinueCancel( this, i18nc("@info", "A file named \"%1\" already exists. Are you sure you want to overwrite it?", fi.fileName()), i18nc("@title:window", "Overwrite File?"), KStandardGuiItem::overwrite()); if (btnCode == KMessageBox::Cancel) { return; // cancel saving } } Settings::setLastOpenedDirectory(m_currentProject->projectUrl().path()); m_currentProject->projectSaveAs(QUrl::fromLocalFile(file)); m_recentProjects->addUrl(QUrl::fromLocalFile(file)); updateCaption(); } void MainWindow::openProject(const QUrl &fileName) { if (!queryClose()) { return; } QString startDirectory = Settings::lastOpenedDirectory(); QUrl file = fileName; if (file.isEmpty()){ // show open dialog file = QUrl::fromLocalFile(QFileDialog::getOpenFileName(this, i18nc("@title:window", "Open Project Files"), startDirectory, i18n("Rocs projects (*.rocs)"))); if (file.isEmpty()) { return; } } Project *project = new Project(file, m_graphEditor); setProject(project); m_recentProjects->addUrl(file); updateCaption(); Settings::setLastOpenedDirectory(file.path()); } void MainWindow::updateCaption() { if (!m_currentProject) { return; } QString modified; if (m_currentProject->isModified()) { modified = '*'; } if (m_currentProject->projectUrl().isEmpty()) { setCaption(i18nc("caption text for temporary project", "[ untitled ]%1", modified)); } else { setCaption(QString("[ %1 ]%2").arg(m_currentProject->projectUrl().toLocalFile()).arg(modified)); } } QString MainWindow::uniqueFilename(const QString &basePrefix, const QString &suffix) { QFile targetFile; QString basePath = m_currentProject->projectUrl().path(); QString fullSuffix = '.' + suffix; QString fullPrefix = basePrefix; if (fullPrefix.isNull()) { fullPrefix = m_currentProject->projectUrl().fileName().remove(QRegExp(".rocs*$")); } else if (fullPrefix.endsWith(fullSuffix)) { fullPrefix.remove(QRegExp(fullSuffix + '$')); } targetFile.setFileName(basePath + fullPrefix + fullSuffix); for(int i = 1; targetFile.exists(); i++) { targetFile.setFileName(basePath + fullPrefix + QString::number(i) + fullSuffix); } return targetFile.fileName(); } void MainWindow::tryToCreateCodeDocument() { QString basePrefix = QInputDialog::getText(this, i18n("ScriptName"), i18n("Enter the name of your new script")); if (basePrefix.isNull()) { qDebug() << "Filename is empty and no script file was created."; return; } QString fullPath = m_currentProject->workingDir() + QLatin1Char('/') + basePrefix + QStringLiteral(".js"); QFileInfo file(fullPath); if (file.exists()) { KMessageBox::error(this, i18n("File already exists.")); return; } m_currentProject->createCodeDocument(basePrefix); } void MainWindow::createGraphDocument() { GraphDocumentPtr document = m_graphEditor->createDocument(); m_currentProject->addGraphDocument(document); } bool MainWindow::queryClose() { if (!m_currentProject) { return true; } if (m_currentProject->isModified()) { const int btnCode = KMessageBox::warningYesNoCancel(this, i18nc( "@info", "Changes on your project are unsaved. Do you want to save your changes?")); if (btnCode == KMessageBox::Yes) { saveProject(); return true; } if (btnCode == KMessageBox::No) { return true; } return false; // do not close } return true; // save to close project: no changes } void MainWindow::quit() { if (queryClose()) { QApplication::quit(); } } void MainWindow::importGraphDocument() { FileFormatDialog importer(this); GraphDocumentPtr document = importer.importFile(); if (!document) { qWarning() << "No graph document was imported."; return; } m_currentProject->addGraphDocument(document); } void MainWindow::exportGraphDocument() { FileFormatDialog exporter(this); exporter.exportFile(m_currentProject->activeGraphDocument()); } void MainWindow::showEditorPluginDialog() { QAction *action = qobject_cast (sender()); if (!action) { return; } if (EditorPluginInterface *plugin = m_graphEditorPluginManager.plugins().value(action->data().toInt())) { plugin->showDialog(m_currentProject->activeGraphDocument()); } } void MainWindow::executeScript() { if (m_outputWidget->isOutputClearEnabled()) { m_outputWidget->clear(); } QString script = m_codeEditorWidget->activeDocument()->text(); enableStopAction(); + + if (m_openDebugger->isChecked()) { + m_kernel->triggerInterruptAction(); + } + m_kernel->execute(m_currentProject->activeGraphDocument(), script); } void MainWindow::stopScript() { m_kernel->stop(); disableStopAction(); } +void MainWindow::checkDebugger() +{ + if (m_openDebugger->isChecked()) { + m_kernel->attachDebugger(); + } else { + m_kernel->detachDebugger(); + } +} + void MainWindow::enableStopAction() { m_stopScript->setEnabled(true); } void MainWindow::disableStopAction() { m_stopScript->setEnabled(false); } diff --git a/src/ui/mainwindow.h b/src/ui/mainwindow.h index fd4c91e4..de90ca94 100644 --- a/src/ui/mainwindow.h +++ b/src/ui/mainwindow.h @@ -1,170 +1,175 @@ /* This file is part of Rocs. Copyright 2008-2011 Tomaz Canabrava Copyright 2008 Ugo Sangiori Copyright 2009-2011 Wagner Reck Copyright 2011-2014 Andreas Cord-Landwehr This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SUI_MAINWINDOW_H #define SUI_MAINWINDOW_H #include "libgraphtheory/editor.h" #include "libgraphtheory/editorplugins/editorpluginmanager.h" #include #include namespace GraphTheory { class Kernel; } class KRecentFilesAction; class QAction; class Project; class QSplitter; class CodeEditorWidget; class GraphEditorWidget; class QCloseEvent; class ScriptOutputWidget; class JournalEditorWidget; class MainWindow : public KXmlGuiWindow { Q_OBJECT public: /*! default constructor */ MainWindow(); ~MainWindow(); private: void setupWidgets(); void setupActions(); /** * Set @p project as active project. This method updates all responsible widgets. */ void setProject(Project *project); /** * Setup the information panel at the right side. * * \return created widget */ QWidget* setupSidePanel(); QWidget* setupScriptPanel(); // setup the panel with the editors and stuff void createAction(const QByteArray& iconName, const QString& actionTitle, const QString& actionName, const char* slot, QObject *parent); /** * Returns an absolute and unique filename: /path-to-current-project/basePrefix.suffix * A number will follow basePrefix in the filename if there were other files * with that same name in the current project's directory. * * \return unique file path */ QString uniqueFilename(const QString &basePrefix, const QString &suffix); protected: /** * Reimplemented method KMainWindow::queryClose(). */ virtual bool queryClose() Q_DECL_OVERRIDE; void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; private Q_SLOTS: void showConfigurationDialog(); /** * Create an empty project configuration with one graph document and one code file and set as * current project. All files are temporary until saved. */ void createProject(); void saveProject(); void saveProjectAs(); // script file handling void tryToCreateCodeDocument(); void importCodeDocument(); void exportCodeDocument(); // graph file handling void createGraphDocument(); /** * Import dialog to add graph document to project. * Imported graph document is directly set as active document. */ void importGraphDocument(); void exportGraphDocument(); /** * Update window caption according to current project. */ void updateCaption(); void setupToolsPluginsAction(); void quit(); public Q_SLOTS: void openProject(const QUrl& fileName = QUrl()); private Q_SLOTS: // script execution /** * starts simulation kernel with currently active script and graph document. */ void executeScript(); /** * stop script execution */ void stopScript(); + /** + * enable/disable the debugger + **/ + void checkDebugger(); public Q_SLOTS: void showEditorPluginDialog(); void disableStopAction(); void enableStopAction(); Q_SIGNALS: /** emitted when the currently active document (of the active project) changes */ void graphDocumentChanged(GraphTheory::GraphDocumentPtr document); private: Project *m_currentProject; // Right Area: GraphTheory::Editor *m_graphEditor; GraphTheory::Kernel *m_kernel; //!< simulation kernel GraphTheory::EditorPluginManager m_graphEditorPluginManager; CodeEditorWidget *m_codeEditorWidget; GraphEditorWidget *m_graphEditorWidget; ScriptOutputWidget *m_outputWidget; JournalEditorWidget *m_journalWidget; // Other Bunch of stuff. QAction *m_runScript; QAction *m_stopScript; + QAction *m_openDebugger; ///Store the recent files. KRecentFilesAction *m_recentProjects; //! Needed to restore the size of the splitter after closing / opening the UI. QSplitter *m_vSplitter; QSplitter *m_hSplitter; QSplitter *m_hScriptSplitter; void createToolsPluginsAction(); }; #endif