diff --git a/src/cantor.cpp b/src/cantor.cpp index d99e6482..aea5659a 100644 --- a/src/cantor.cpp +++ b/src/cantor.cpp @@ -1,720 +1,722 @@ /* 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #include "cantor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/backend.h" #include "lib/panelpluginhandler.h" #include "lib/panelplugin.h" #include "lib/worksheetaccess.h" #include "settings.h" #include "ui_settings.h" #include "backendchoosedialog.h" CantorShell::CantorShell() : KParts::MainWindow(), m_part(nullptr) { // set the shell's ui resource file setXMLFile(QLatin1String("cantor_shell.rc")); // then, setup our actions setupActions(); createGUI(nullptr); m_tabWidget=new QTabWidget(this); m_tabWidget->setTabsClosable(true); m_tabWidget->setMovable(true); m_tabWidget->setDocumentMode(true); setCentralWidget(m_tabWidget); connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(activateWorksheet(int))); connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); // apply the saved mainwindow settings, if any, and ask the mainwindow // to automatically save settings if changed: window size, toolbar // position, icon size, etc. setAutoSaveSettings(); setDockOptions(QMainWindow::AnimatedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs); updateNewSubmenu(); } CantorShell::~CantorShell() { if (m_recentProjectsAction) m_recentProjectsAction->saveEntries(KSharedConfig::openConfig()->group(QLatin1String("Recent Files"))); if (!m_newBackendActions.isEmpty()) { unplugActionList(QLatin1String("new_worksheet_with_backend_list")); qDeleteAll(m_newBackendActions); m_newBackendActions.clear(); } unplugActionList(QLatin1String("view_show_panel_list")); qDeleteAll(m_panels); m_panels.clear(); } void CantorShell::load(const QUrl &url) { if (!m_part||!m_part->url().isEmpty() || m_part->isModified() ) { addWorksheet(QString()); m_tabWidget->setCurrentIndex(m_parts.size()-1); } if (!m_part->openUrl( url )) closeTab(m_tabWidget->currentIndex()); if (m_recentProjectsAction) m_recentProjectsAction->addUrl(url); } void CantorShell::setupActions() { QAction* openNew = KStandardAction::openNew(this, SLOT(fileNew()), actionCollection()); openNew->setPriority(QAction::LowPriority); QAction* open = KStandardAction::open(this, SLOT(fileOpen()), actionCollection()); open->setPriority(QAction::LowPriority); m_recentProjectsAction = KStandardAction::openRecent(this, &CantorShell::load, actionCollection()); m_recentProjectsAction->setPriority(QAction::LowPriority); m_recentProjectsAction->loadEntries(KSharedConfig::openConfig()->group(QLatin1String("Recent Files"))); KStandardAction::close (this, SLOT(closeTab()), actionCollection()); KStandardAction::quit(qApp, SLOT(closeAllWindows()), actionCollection()); createStandardStatusBarAction(); //setStandardToolBarMenuEnabled(true); KStandardAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection()); KStandardAction::configureToolbars(this, SLOT(configureToolbars()), actionCollection()); KStandardAction::preferences(this, SLOT(showSettings()), actionCollection()); QAction * downloadExamples = new QAction(i18n("Download Example Worksheets"), actionCollection()); downloadExamples->setIcon(QIcon::fromTheme(QLatin1String("get-hot-new-stuff"))); actionCollection()->addAction(QLatin1String("file_example_download"), downloadExamples); connect(downloadExamples, SIGNAL(triggered()), this, SLOT(downloadExamples())); QAction * openExample =new QAction(i18n("&Open Example"), actionCollection()); openExample->setIcon(QIcon::fromTheme(QLatin1String("document-open"))); actionCollection()->addAction(QLatin1String("file_example_open"), openExample); connect(openExample, SIGNAL(triggered()), this, SLOT(openExample())); QAction* toPreviousTab = new QAction(i18n("Go to previous worksheet"), actionCollection()); actionCollection()->addAction(QLatin1String("go_to_previous_tab"), toPreviousTab); actionCollection()->setDefaultShortcut(toPreviousTab, Qt::CTRL+Qt::Key_PageDown); connect(toPreviousTab, &QAction::triggered, toPreviousTab, [this](){ const int index = m_tabWidget->currentIndex()-1; if (index >= 0) m_tabWidget->setCurrentIndex(index); else m_tabWidget->setCurrentIndex(m_tabWidget->count()-1); }); addAction(toPreviousTab); QAction* toNextTab = new QAction(i18n("Go to next worksheet"), actionCollection()); actionCollection()->addAction(QLatin1String("go_to_next_tab"), toNextTab); actionCollection()->setDefaultShortcut(toNextTab, Qt::CTRL+Qt::Key_PageUp); connect(toNextTab, &QAction::triggered, toNextTab, [this](){ const int index = m_tabWidget->currentIndex()+1; if (index < m_tabWidget->count()) m_tabWidget->setCurrentIndex(index); else m_tabWidget->setCurrentIndex(0); }); addAction(toNextTab); } void CantorShell::saveProperties(KConfigGroup & /*config*/) { // the 'config' object points to the session managed // config file. anything you write here will be available // later when this app is restored } void CantorShell::readProperties(const KConfigGroup & /*config*/) { // the 'config' object points to the session managed // config file. this function is automatically called whenever // the app is being restored. read in here whatever you wrote // in 'saveProperties' } /*! * called when one of the "new backend" action or the "New" action are called * adds a new worksheet with the backend assossiated with the called action * or opens the "Choose Backend" dialog, respectively. */ void CantorShell::fileNew() { QAction* a = static_cast(sender()); const QString& backendName = a->data().toString(); if (!backendName.isEmpty()) { addWorksheet(backendName); return; } //"New" action was called -> open the "Choose Backend" dialog. addWorksheet(); } void CantorShell::optionsConfigureKeys() { KShortcutsDialog dlg( KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed, this ); dlg.addCollection( actionCollection(), i18n("Cantor") ); if (m_part) dlg.addCollection( m_part->actionCollection(), i18n("Cantor") ); dlg.configure( true ); } void CantorShell::fileOpen() { // this slot is called whenever the File->Open menu is selected, // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar // button is clicked static const QString& worksheetFilter = i18n("Cantor Worksheet (*.cws)"); static const QString& notebookFilter = i18n("Jupyter Notebook (*.ipynb)"); QString filter; if (m_previousFilter == notebookFilter) filter = notebookFilter + QLatin1String(";;") + worksheetFilter; else filter = worksheetFilter + QLatin1String(";;") + notebookFilter; QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Open file"), QUrl(), filter, &m_previousFilter); if (url.isEmpty() == false) { // About this function, the style guide // says that it should open a new window if the document is _not_ // in its initial state. This is what we do here.. /*if ( m_part->url().isEmpty() && ! m_part->isModified() ) { // we open the file in this window... load( url ); } else { // we open the file in a new window... CantorShell* newWin = new CantorShell; newWin->load( url ); newWin->show(); }*/ load( url ); } } void CantorShell::addWorksheet() { bool hasBackend = false; for (auto* b : Cantor::Backend::availableBackends()) { if(b->isEnabled()) { hasBackend = true; break; } } if(hasBackend) //There is no point in asking for the backend, if no one is available { QString backend = Settings::self()->defaultBackend(); if (backend.isEmpty()) { QPointer dlg=new BackendChooseDialog(this); if(dlg->exec()) { backend = dlg->backendName(); addWorksheet(backend); } delete dlg; } else { addWorksheet(backend); } }else { QTextBrowser *browser=new QTextBrowser(this); QString backendList=QLatin1String("
    "); int backendListSize = 0; foreach(Cantor::Backend* b, Cantor::Backend::availableBackends()) { if(!b->requirementsFullfilled()) //It's disabled because of missing dependencies, not because of some other reason(like eg. nullbackend) { backendList+=QString::fromLatin1("
  • %1: %2
  • ").arg(b->name(), b->url()); ++backendListSize; } } browser->setHtml(i18np("

    No Backend Found

    \n" \ "
    You could try:\n" \ "
      " \ "
    • Changing the settings in the config dialog;
    • " \ "
    • Installing packages for the following program:
    • " \ " %2 " \ "
    " \ "
    " , "

    No Backend Found

    \n" \ "
    You could try:\n" \ "
      " \ "
    • Changing the settings in the config dialog;
    • " \ "
    • Installing packages for one of the following programs:
    • " \ " %2 " \ "
    " \ "
    " , backendListSize, backendList )); browser->setObjectName(QLatin1String("ErrorMessage")); m_tabWidget->addTab(browser, i18n("Error")); } } void CantorShell::addWorksheet(const QString& backendName) { static int sessionCount=1; // this routine will find and load our Part. it finds the Part by // name which is a bad idea usually.. but it's alright in this // case since our Part is made for this Shell KPluginLoader loader(QLatin1String("cantorpart")); KPluginFactory* factory = loader.factory(); if (factory) { if (!backendName.isEmpty()) { Cantor::Backend* backend = Cantor::Backend::getBackend(backendName); if (!backend) { KMessageBox::error(this, i18n("Backend %1 is not installed", backendName), i18n("Cantor")); return; } else { if (!backend->isEnabled()) { KMessageBox::error(this, i18n("%1 backend installed, but inactive. Please check installation and Cantor settings", backendName), i18n("Cantor")); return; } } } // now that the Part is loaded, we cast it to a Part to get our hands on it KParts::ReadWritePart* part = factory->create(m_tabWidget, QVariantList()<addTab(part->widget(), i18n("Session %1", sessionCount++)); m_tabWidget->setCurrentIndex(tab); // Setting focus on worksheet view, because Qt clear focus of added widget inside addTab // This fix https://bugs.kde.org/show_bug.cgi?id=395976 part->widget()->findChild()->setFocus(); + + // Force run updateCaption for getting proper backend icon + QMetaObject::invokeMethod(part, "updateCaption"); } else { qDebug()<<"error creating part "; } } else { // if we couldn't find our Part, we exit since the Shell by // itself can't do anything useful KMessageBox::error(this, i18n("Failed to find the Cantor Part with error %1", loader.errorString())); qApp->quit(); // we return here, cause qApp->quit() only means "exit the // next time we enter the event loop... return; } } void CantorShell::activateWorksheet(int index) { QObject* pluginHandler=m_part->findChild(QLatin1String("PanelPluginHandler")); if (pluginHandler) disconnect(pluginHandler,SIGNAL(pluginsChanged()), this, SLOT(updatePanel())); // Save part state before change worksheet if (m_part) { QStringList visiblePanelNames; foreach (QDockWidget* doc, m_panels) { if (doc->widget() && doc->widget()->isVisible()) visiblePanelNames << doc->objectName(); } m_pluginsVisibility[m_part] = visiblePanelNames; } m_part=findPart(m_tabWidget->widget(index)); if(m_part) { createGUI(m_part); QObject* pluginHandler=m_part->findChild(QLatin1String("PanelPluginHandler")); connect(pluginHandler, SIGNAL(pluginsChanged()), this, SLOT(updatePanel())); updatePanel(); } else qDebug()<<"selected part doesn't exist"; m_tabWidget->setCurrentIndex(index); } void CantorShell::setTabCaption(const QString& caption, const QIcon& icon) { - if (caption.isEmpty()) return; - KParts::ReadWritePart* part=dynamic_cast(sender()); if (part) { - m_tabWidget->setTabText(m_tabWidget->indexOf(part->widget()), caption); + if (!caption.isEmpty()) + m_tabWidget->setTabText(m_tabWidget->indexOf(part->widget()), caption); m_tabWidget->setTabIcon(m_tabWidget->indexOf(part->widget()), icon); } } void CantorShell::closeTab(int index) { if (!reallyClose(false)) { return; } QWidget* widget = nullptr; if (index >= 0) { widget = m_tabWidget->widget(index); } else if (m_part) { widget = m_part->widget(); } if (!widget) { qWarning() << "Could not find widget by tab index" << index; return; } m_tabWidget->removeTab(index); if(widget->objectName()==QLatin1String("ErrorMessage")) { widget->deleteLater(); }else { KParts::ReadWritePart* part= findPart(widget); if(part) { m_parts.removeAll(part); m_pluginsVisibility.remove(part); delete part; } } if (m_tabWidget->count() == 0) setCaption(QString()); updatePanel(); } bool CantorShell::reallyClose(bool checkAllParts) { if(checkAllParts && m_parts.count() > 1) { bool modified = false; foreach( KParts::ReadWritePart* const part, m_parts) { if(part->isModified()) { modified = true; break; } } if(!modified) return true; int want_save = KMessageBox::warningYesNo( this, i18n("Multiple unsaved Worksheets are opened. Do you want to close them?"), i18n("Close Cantor")); switch (want_save) { case KMessageBox::Yes: return true; case KMessageBox::No: return false; } } if (m_part && m_part->isModified() ) { int want_save = KMessageBox::warningYesNoCancel( this, i18n("The current project has been modified. Do you want to save it?"), i18n("Save Project")); switch (want_save) { case KMessageBox::Yes: m_part->save(); if(m_part->waitSaveComplete()) { return true; } else { m_part->setModified(true); return false; } case KMessageBox::Cancel: return false; case KMessageBox::No: return true; } } return true; } void CantorShell::closeEvent(QCloseEvent* event) { if(!reallyClose()) { event->ignore(); } else { KParts::MainWindow::closeEvent(event); } } void CantorShell::showSettings() { KConfigDialog *dialog = new KConfigDialog(this, QLatin1String("settings"), Settings::self()); QWidget *generalSettings = new QWidget; Ui::SettingsBase base; base.setupUi(generalSettings); base.kcfg_DefaultBackend->addItems(Cantor::Backend::listAvailableBackends()); dialog->addPage(generalSettings, i18n("General"), QLatin1String("preferences-other")); foreach(Cantor::Backend* backend, Cantor::Backend::availableBackends()) { if (backend->config()) //It has something to configure, so add it to the dialog dialog->addPage(backend->settingsWidget(dialog), backend->config(), backend->name(), backend->icon()); } dialog->show(); } void CantorShell::downloadExamples() { KNS3::DownloadDialog dialog; dialog.exec(); foreach (const KNS3::Entry& e, dialog.changedEntries()) { qDebug() << "Changed Entry: " << e.name(); } } void CantorShell::openExample() { QString dir = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/examples"); if (dir.isEmpty()) return; QDir().mkpath(dir); QStringList files=QDir(dir).entryList(QDir::Files); QPointer dlg=new QDialog(this); QListWidget* list=new QListWidget(dlg); foreach(const QString& file, files) { QString name=file; name.remove(QRegExp(QLatin1String("-.*\\.hotstuff-access$"))); list->addItem(name); } QVBoxLayout *mainLayout = new QVBoxLayout; dlg->setLayout(mainLayout); mainLayout->addWidget(list); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); buttonBox->button(QDialogButtonBox::Ok)->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogOkButton)); buttonBox->button(QDialogButtonBox::Cancel)->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogCancelButton)); connect(buttonBox, SIGNAL(accepted()), dlg, SLOT(accept()) ); connect(buttonBox, SIGNAL(rejected()), dlg, SLOT(reject()) ); if (dlg->exec()==QDialog::Accepted&&list->currentRow()>=0) { const QString& selectedFile=files[list->currentRow()]; QUrl url = QUrl::fromLocalFile(QDir(dir).absoluteFilePath(selectedFile)); qDebug()<<"loading file "<widget()==widget) return part; } return nullptr; } void CantorShell::updatePanel() { unplugActionList(QLatin1String("view_show_panel_list")); //remove all of the previous panels (but do not delete the widgets) foreach(QDockWidget* dock, m_panels) { QWidget* widget=dock->widget(); if(widget!=nullptr) { widget->setParent(this); widget->hide(); } dock->deleteLater(); } m_panels.clear(); QList panelActions; Cantor::PanelPluginHandler* handler=m_part->findChild(QLatin1String("PanelPluginHandler")); if(!handler) { qDebug()<<"no PanelPluginHandle found for this part"; return; } QDockWidget* last=nullptr; QList plugins=handler->plugins(); const bool isNewWorksheet = !m_pluginsVisibility.contains(m_part); foreach(Cantor::PanelPlugin* plugin, plugins) { if(plugin==nullptr) { qDebug()<<"somethings wrong"; continue; } qDebug()<<"adding panel for "<name(); plugin->setParentWidget(this); QDockWidget* docker=new QDockWidget(plugin->name(), this); docker->setObjectName(plugin->name()); docker->setWidget(plugin->widget()); addDockWidget ( Qt::RightDockWidgetArea, docker ); // Set visibility for dock from saved info if (isNewWorksheet) { if (plugin->showOnStartup()) docker->show(); else docker->hide(); } else { if (m_pluginsVisibility[m_part].contains(plugin->name())) docker->show(); else docker->hide(); } if(last!=nullptr) tabifyDockWidget(last, docker); last=docker; connect(plugin, &Cantor::PanelPlugin::visibilityRequested, this, &CantorShell::pluginVisibilityRequested); m_panels.append(docker); //Create the action to show/hide this panel panelActions<toggleViewAction(); } plugActionList(QLatin1String("view_show_panel_list"), panelActions); updateNewSubmenu(); } void CantorShell::updateNewSubmenu() { unplugActionList(QLatin1String("new_worksheet_with_backend_list")); qDeleteAll(m_newBackendActions); m_newBackendActions.clear(); foreach (Cantor::Backend* backend, Cantor::Backend::availableBackends()) { if (!backend->isEnabled()) continue; QAction * action = new QAction(QIcon::fromTheme(backend->icon()), backend->name(), nullptr); action->setData(backend->name()); connect(action, SIGNAL(triggered()), this, SLOT(fileNew())); m_newBackendActions << action; } plugActionList(QLatin1String("new_worksheet_with_backend_list"), m_newBackendActions); } Cantor::WorksheetAccessInterface* CantorShell::currentWorksheetAccessInterface() { Cantor::WorksheetAccessInterface* wa=m_part->findChild(Cantor::WorksheetAccessInterface::Name); if (!wa) qDebug()<<"failed to access worksheet access interface for current part"; return wa; } void CantorShell::pluginVisibilityRequested() { Cantor::PanelPlugin* plugin = static_cast(sender()); for (QDockWidget* docker: m_panels) { if (plugin->name() == docker->windowTitle()) { if (docker->isHidden()) docker->show(); docker->raise(); } } } void CantorShell::onWorksheetSave(const QUrl& url) { if (m_recentProjectsAction) m_recentProjectsAction->addUrl(url); } diff --git a/src/cantor_part.cpp b/src/cantor_part.cpp index e8b4e104..503ef6f6 100644 --- a/src/cantor_part.cpp +++ b/src/cantor_part.cpp @@ -1,1000 +1,1000 @@ /* 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder Copyright (C) 2017-2019 Alexander Semke Copyright (C) 2019 Sirgienko Nikita */ #include #include "cantor_part.h" #include "lib/assistant.h" #include "lib/backend.h" #include "lib/extension.h" #include "lib/panelpluginhandler.h" #include "lib/panelplugin.h" #include "lib/worksheetaccess.h" #include "scripteditor/scripteditorwidget.h" #include "searchbar.h" #include "settings.h" #include "worksheet.h" #include "worksheetview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //A concrete implementation of the WorksheetAccesssInterface class WorksheetAccessInterfaceImpl : public Cantor::WorksheetAccessInterface { public: WorksheetAccessInterfaceImpl(QObject* parent, Worksheet* worksheet) : WorksheetAccessInterface(parent), m_worksheet(worksheet) { qDebug()<<"new worksheetaccess interface"; connect(worksheet, &Worksheet::modified, this, &WorksheetAccessInterfaceImpl::modified); } ~WorksheetAccessInterfaceImpl() override = default; QByteArray saveWorksheetToByteArray() override { return m_worksheet->saveToByteArray(); } void loadWorksheetFromByteArray(QByteArray* data) override { m_worksheet->load(data); } Cantor::Session* session() override { return m_worksheet->session(); } void evaluate() override { m_worksheet->evaluate(); } void interrupt() override { m_worksheet->interrupt(); } private: Worksheet* m_worksheet; }; CantorPart::CantorPart( QWidget *parentWidget, QObject *parent, const QVariantList & args ): KParts::ReadWritePart(parent), m_searchBar(nullptr), m_panelHandler(new Cantor::PanelPluginHandler(this)), m_initProgressDlg(nullptr), m_showProgressDlg(true), m_showBackendHelp(nullptr), m_statusBarBlocked(false), m_sessionStatusCounter(0) { connect(m_panelHandler, &Cantor::PanelPluginHandler::pluginsChanged, this, &CantorPart::pluginsChanged); QString backendName; if(!args.isEmpty()) backendName = args.first().toString(); for (const QVariant& arg : args) { if (arg.toString() == QLatin1String("--noprogress") ) { qWarning()<<"not showing the progress bar by request"; m_showProgressDlg=false; } } Cantor::Backend* b = nullptr; if (!backendName.isEmpty()) { b = Cantor::Backend::getBackend(backendName); qDebug()<<"Backend "<name()<<" offers extensions: "<extensions(); } //central widget QWidget* widget = new QWidget(parentWidget); QVBoxLayout* layout = new QVBoxLayout(widget); m_worksheet = new Worksheet(b, widget); m_worksheetview = new WorksheetView(m_worksheet, widget); m_worksheetview->setEnabled(false); //disable input until the session has successfully logged in and emits the ready signal connect(m_worksheet, &Worksheet::modified, this, static_cast(&KParts::ReadWritePart::setModified)); connect(m_worksheet, &Worksheet::showHelp, this, &CantorPart::showHelp); connect(m_worksheet, &Worksheet::loaded, this, &CantorPart::initialized); layout->addWidget(m_worksheetview); setWidget(widget); //create WorksheetAccessInterface, used at the moment by LabPlot only to access Worksheet's API Cantor::WorksheetAccessInterface* iface = new WorksheetAccessInterfaceImpl(this, m_worksheet); Q_UNUSED(iface); //initialize actions auto* collection = actionCollection(); connect(collection, &KActionCollection::inserted, m_worksheet, &Worksheet::registerShortcut); m_worksheet->createActions(collection); KStandardAction::saveAs(this, SLOT(fileSaveAs()), collection); m_save = KStandardAction::save(this, SLOT(save()), collection); m_save->setPriority(QAction::LowPriority); QAction* savePlain = new QAction(i18n("Save Plain Text"), collection); collection->addAction(QLatin1String("file_save_plain"), savePlain); savePlain->setIcon(QIcon::fromTheme(QLatin1String("document-save"))); connect(savePlain, &QAction::triggered, this, &CantorPart::fileSavePlain); QAction* undo = KStandardAction::undo(m_worksheet, SIGNAL(undo()), collection); undo->setPriority(QAction::LowPriority); connect(m_worksheet, &Worksheet::undoAvailable, undo, &QAction::setEnabled); m_editActions.push_back(undo); QAction* redo = KStandardAction::redo(m_worksheet, SIGNAL(redo()), collection); redo->setPriority(QAction::LowPriority); connect(m_worksheet, &Worksheet::redoAvailable, redo, &QAction::setEnabled); m_editActions.push_back(redo); QAction* cut = KStandardAction::cut(m_worksheet, SIGNAL(cut()), collection); cut->setPriority(QAction::LowPriority); connect(m_worksheet, &Worksheet::cutAvailable, cut, &QAction::setEnabled); m_editActions.push_back(cut); QAction* copy = KStandardAction::copy(m_worksheet, SIGNAL(copy()), collection); copy->setPriority(QAction::LowPriority); connect(m_worksheet, &Worksheet::copyAvailable, copy, &QAction::setEnabled); QAction* paste = KStandardAction::paste(m_worksheet, SLOT(paste()), collection); paste->setPriority(QAction::LowPriority); connect(m_worksheet, &Worksheet::pasteAvailable, paste, &QAction::setEnabled); m_editActions.push_back(paste); QAction* find = KStandardAction::find(this, SLOT(showSearchBar()), collection); find->setPriority(QAction::LowPriority); QAction* replace = KStandardAction::replace(this, SLOT(showExtendedSearchBar()), collection); replace->setPriority(QAction::LowPriority); m_editActions.push_back(replace); m_findNext = KStandardAction::findNext(this, SLOT(findNext()), collection); m_findNext->setEnabled(false); m_findPrev = KStandardAction::findPrev(this, SLOT(findPrev()), collection); m_findPrev->setEnabled(false); QAction* latexExport = new QAction(i18n("Export to LaTeX"), collection); collection->addAction(QLatin1String("file_export_latex"), latexExport); latexExport->setIcon(QIcon::fromTheme(QLatin1String("document-export"))); connect(latexExport, &QAction::triggered, this, &CantorPart::exportToLatex); QAction* print = KStandardAction::print(this, SLOT(print()), collection); print->setPriority(QAction::LowPriority); QAction* printPreview = KStandardAction::printPreview(this, SLOT(printPreview()), collection); printPreview->setPriority(QAction::LowPriority); KStandardAction::zoomIn(m_worksheetview, SLOT(zoomIn()), collection); KStandardAction::zoomOut(m_worksheetview, SLOT(zoomOut()), collection); KStandardAction::actualSize(m_worksheetview, SLOT(actualSize()), collection); m_evaluate = new QAction(i18n("Evaluate Worksheet"), collection); collection->addAction(QLatin1String("evaluate_worksheet"), m_evaluate); m_evaluate->setIcon(QIcon::fromTheme(QLatin1String("system-run"))); collection->setDefaultShortcut(m_evaluate, Qt::CTRL+Qt::Key_E); connect(m_evaluate, &QAction::triggered, this, &CantorPart::evaluateOrInterrupt); m_editActions.push_back(m_evaluate); m_typeset = new KToggleAction(i18n("Typeset using LaTeX"), collection); m_typeset->setChecked(Settings::self()->typesetDefault()); // Disable until login, because we use session command for this action m_typeset->setEnabled(false); collection->addAction(QLatin1String("enable_typesetting"), m_typeset); connect(m_typeset, &KToggleAction::toggled, this, &CantorPart::enableTypesetting); if (!Cantor::LatexRenderer::isLatexAvailable()) m_typeset->setVisible(false); m_highlight = new KToggleAction(i18n("Syntax Highlighting"), collection); m_highlight->setChecked(Settings::self()->highlightDefault()); collection->addAction(QLatin1String("enable_highlighting"), m_highlight); connect(m_highlight, &KToggleAction::toggled, m_worksheet, &Worksheet::enableHighlighting); m_completion = new KToggleAction(i18n("Completion"), collection); m_completion->setChecked(Settings::self()->completionDefault()); collection->addAction(QLatin1String("enable_completion"), m_completion); connect(m_completion, &KToggleAction::toggled, m_worksheet, &Worksheet::enableCompletion); m_exprNumbering = new KToggleAction(i18n("Line Numbers"), collection); m_exprNumbering->setChecked(Settings::self()->expressionNumberingDefault()); collection->addAction(QLatin1String("enable_expression_numbers"), m_exprNumbering); connect(m_exprNumbering, &KToggleAction::toggled, m_worksheet, &Worksheet::enableExpressionNumbering); m_animateWorksheet = new KToggleAction(i18n("Animate Worksheet"), collection); m_animateWorksheet->setChecked(Settings::self()->animationDefault()); collection->addAction(QLatin1String("enable_animations"), m_animateWorksheet); connect(m_animateWorksheet, &KToggleAction::toggled, m_worksheet, &Worksheet::enableAnimations); if (MathRenderer::mathRenderAvailable()) { m_embeddedMath= new KToggleAction(i18n("Embedded Math"), collection); m_embeddedMath->setChecked(Settings::self()->embeddedMathDefault()); collection->addAction(QLatin1String("enable_embedded_math"), m_embeddedMath); connect(m_embeddedMath, &KToggleAction::toggled, m_worksheet, &Worksheet::enableEmbeddedMath); } m_restart = new QAction(i18n("Restart Backend"), collection); collection->addAction(QLatin1String("restart_backend"), m_restart); m_restart->setIcon(QIcon::fromTheme(QLatin1String("system-reboot"))); connect(m_restart, &QAction::triggered, this, &CantorPart::restartBackend); m_restart->setEnabled(false); // No need show restart button before login m_editActions.push_back(m_restart); QAction* evaluateCurrent = new QAction(QIcon::fromTheme(QLatin1String("media-playback-start")), i18n("Evaluate Entry"), collection); collection->addAction(QLatin1String("evaluate_current"), evaluateCurrent); collection->setDefaultShortcut(evaluateCurrent, Qt::SHIFT + Qt::Key_Return); connect(evaluateCurrent, &QAction::triggered, m_worksheet, &Worksheet::evaluateCurrentEntry); m_editActions.push_back(evaluateCurrent); QAction* insertCommandEntry = new QAction(QIcon::fromTheme(QLatin1String("run-build")), i18n("Insert Command Entry"), collection); collection->addAction(QLatin1String("insert_command_entry"), insertCommandEntry); collection->setDefaultShortcut(insertCommandEntry, Qt::CTRL + Qt::Key_Return); connect(insertCommandEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertCommandEntry())); m_editActions.push_back(insertCommandEntry); QAction* insertTextEntry = new QAction(QIcon::fromTheme(QLatin1String("draw-text")), i18n("Insert Text Entry"), collection); collection->addAction(QLatin1String("insert_text_entry"), insertTextEntry); connect(insertTextEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertTextEntry())); m_editActions.push_back(insertTextEntry); #ifdef Discount_FOUND QAction* insertMarkdownEntry = new QAction(QIcon::fromTheme(QLatin1String("text-x-markdown")), i18n("Insert Markdown Entry"), collection); collection->addAction(QLatin1String("insert_markdown_entry"), insertMarkdownEntry); connect(insertMarkdownEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertMarkdownEntry())); m_editActions.push_back(insertMarkdownEntry); #endif #ifdef WITH_EPS QAction* insertLatexEntry = new QAction(QIcon::fromTheme(QLatin1String("text-x-tex")), i18n("Insert Latex Entry"), collection); collection->addAction(QLatin1String("insert_latex_entry"), insertLatexEntry); connect(insertLatexEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertLatexEntry())); m_editActions.push_back(insertLatexEntry); #endif QAction* insertPageBreakEntry = new QAction(QIcon::fromTheme(QLatin1String("go-next-view-page")), i18n("Insert Page Break"), collection); collection->addAction(QLatin1String("insert_page_break_entry"), insertPageBreakEntry); connect(insertPageBreakEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertPageBreakEntry())); m_editActions.push_back(insertPageBreakEntry); QAction* insertImageEntry = new QAction(QIcon::fromTheme(QLatin1String("image-x-generic")), i18n("Insert Image"), collection); collection->addAction(QLatin1String("insert_image_entry"), insertImageEntry); connect(insertImageEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertImageEntry())); m_editActions.push_back(insertImageEntry); QAction* removeCurrent = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Remove current Entry"), collection); collection->addAction(QLatin1String("remove_current"), removeCurrent); collection->setDefaultShortcut(removeCurrent, Qt::ShiftModifier + Qt::Key_Delete); connect(removeCurrent, &QAction::triggered, m_worksheet, &Worksheet::removeCurrentEntry); m_editActions.push_back(removeCurrent); m_showBackendHelp = new QAction(i18n("Show Help") , collection); m_showBackendHelp->setIcon(QIcon::fromTheme(QLatin1String("help-contents"))); collection->addAction(QLatin1String("backend_help"), m_showBackendHelp); connect(m_showBackendHelp, &QAction::triggered, this, &CantorPart::showBackendHelp); // Disabled, because uploading to kde store from program don't work // See https://phabricator.kde.org/T9980 for details // If this situation will changed, then uncomment this action /* QAction* publishWorksheet = new QAction(i18n("Publish Worksheet"), collection); publishWorksheet->setIcon(QIcon::fromTheme(QLatin1String("get-hot-new-stuff"))); collection->addAction(QLatin1String("file_publish_worksheet"), publishWorksheet); connect(publishWorksheet, &QAction::triggered, this, SLOT(publishWorksheet())); */ KToggleAction* showEditor = new KToggleAction(i18n("Show Script Editor"), collection); showEditor->setChecked(false); collection->addAction(QLatin1String("show_editor"), showEditor); connect(showEditor, &KToggleAction::toggled, this, &CantorPart::showScriptEditor); QAction* showCompletion = new QAction(i18n("Show Completion"), collection); collection->addAction(QLatin1String("show_completion"), showCompletion); QList showCompletionShortcuts; showCompletionShortcuts << Qt::Key_Tab << Qt::CTRL + Qt::Key_Space; collection->setDefaultShortcuts(showCompletion, showCompletionShortcuts); connect(showCompletion, SIGNAL(triggered()), m_worksheet, SLOT(showCompletion())); m_editActions.push_back(showCompletion); // set our XML-UI resource file setXMLFile(QLatin1String("cantor_part.rc")); // we are read-write by default setReadWrite(true); // we are not modified since we haven't done anything yet setModified(false); if (b) { m_showBackendHelp->setText(i18n("Show %1 Help", b->name())); showEditor->setEnabled(b->extensions().contains(QLatin1String("ScriptExtension"))); initialized(); } } CantorPart::~CantorPart() { if (m_scriptEditor) { disconnect(m_scriptEditor, SIGNAL(destroyed()), this, SLOT(scriptEditorClosed())); delete m_scriptEditor; } if (m_searchBar) delete m_searchBar; } void CantorPart::setReadWrite(bool rw) { // notify your internal widget of the read-write state m_worksheetview->setInteractive(rw); ReadWritePart::setReadWrite(rw); } void CantorPart::setReadOnly() { for (QAction* action : m_editActions) action->setEnabled(false); if (m_showBackendHelp) { m_showBackendHelp->setEnabled(false); m_showBackendHelp->setVisible(false); } } void CantorPart::setModified(bool modified) { // get a handle on our Save action and make sure it is valid if (!m_save) return; // if so, we either enable or disable it based on the current state m_save->setEnabled(modified); // in any event, we want our parent to do it's thing ReadWritePart::setModified(modified); } KAboutData& CantorPart::createAboutData() { // the non-i18n name here must be the same as the directory in // which the part's rc file is installed ('partrcdir' in the Makefile) static KAboutData about(QLatin1String("cantorpart"), QLatin1String("Cantor"), QLatin1String(CANTOR_VERSION), i18n("CantorPart"), KAboutLicense::GPL, i18n("(C) 2009-2015 Alexander Rieder"), QString(), QLatin1String("https://edu.kde.org/cantor/")); about.addAuthor( i18n("Alexander Rieder"), QString(), QLatin1String("alexanderrieder@gmail.com") ); return about; } bool CantorPart::openFile() { //don't crash if for some reason the worksheet is invalid if(!m_worksheet) { qWarning()<<"trying to open in an invalid cantor part"; return false; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QElapsedTimer timer; timer.start(); const bool rc = m_worksheet->load(localFilePath()); QApplication::restoreOverrideCursor(); if (rc) { qDebug()<< "Worksheet successfully loaded in " << (float)timer.elapsed()/1000 << " seconds"; updateCaption(); // We modified, but it we load file now, so no need in save option setModified(false); } return rc; } bool CantorPart::saveFile() { // if we aren't read-write, return immediately if (isReadWrite() == false) return false; qDebug()<<"saving to: "<save( localFilePath() ); setModified(false); emit worksheetSave(QUrl::fromLocalFile(localFilePath())); return true; } void CantorPart::fileSaveAs() { // this slot is called whenever the File->Save As menu is selected static const QString& worksheetFilter = i18n("Cantor Worksheet (*.cws)"); static const QString& notebookFilter = i18n("Jupyter Notebook (*.ipynb)"); QString filter = worksheetFilter + QLatin1String(";;") + notebookFilter; if (!m_worksheet->isReadOnly()) { //if the backend supports scripts, also append their scriptFile endings to the filter auto* const backend = m_worksheet->session()->backend(); if (backend->extensions().contains(QLatin1String("ScriptExtension"))) { auto* e = dynamic_cast(backend->extension(QLatin1String("ScriptExtension"))); if (e) filter += QLatin1String(";;") + e->scriptFileFilter(); } } QString selectedFilter; QString file_name = QFileDialog::getSaveFileName(widget(), i18n("Save as"), QString(), filter, &selectedFilter); if (file_name.isEmpty()) return; static const QString jupyterExtension = QLatin1String(".ipynb"); static const QString cantorExtension = QLatin1String(".cws"); // Append file extension, if it isn't specified // And change filter, if it specified to supported extension if (file_name.contains(QLatin1String("."))) { if (file_name.endsWith(cantorExtension)) selectedFilter = worksheetFilter; else if (file_name.endsWith(jupyterExtension)) selectedFilter = notebookFilter; } else { if (selectedFilter == worksheetFilter) file_name += cantorExtension; else if (selectedFilter == notebookFilter) file_name += jupyterExtension; } //depending on user's selection, save as a worksheet, as a Jupyter notebook or as a plain script file if (selectedFilter == worksheetFilter) { m_worksheet->setType(Worksheet::CantorWorksheet); const QUrl& url = QUrl::fromLocalFile(file_name); saveAs(url); emit worksheetSave(url); } else if (selectedFilter == notebookFilter) { m_worksheet->setType(Worksheet::JupyterNotebook); const QUrl& url = QUrl::fromLocalFile(file_name); saveAs(url); emit worksheetSave(url); } else m_worksheet->savePlain(file_name); updateCaption(); } void CantorPart::fileSavePlain() { QString file_name = QFileDialog::getSaveFileName(widget(), i18n("Save"), QString(), QString()); if (!file_name.isEmpty()) m_worksheet->savePlain(file_name); } void CantorPart::exportToLatex() { QString file_name = QFileDialog::getSaveFileName(widget(), i18n("Export to LaTeX"), QString(), QString()); if (file_name.isEmpty() == false) { if (!file_name.endsWith(QLatin1String(".tex"))) file_name += QLatin1String(".tex"); m_worksheet->saveLatex(file_name); } } void CantorPart::guiActivateEvent( KParts::GUIActivateEvent * event ) { KParts::ReadWritePart::guiActivateEvent(event); if(event->activated()) { if(m_scriptEditor) m_scriptEditor->show(); }else { if(m_scriptEditor) m_scriptEditor->hide(); } } void CantorPart::evaluateOrInterrupt() { qDebug()<<"evalorinterrupt"; if(m_worksheet->isRunning()) m_worksheet->interrupt(); else m_worksheet->evaluate(); } void CantorPart::restartBackend() { bool restart = false; if (Settings::self()->warnAboutSessionRestart()) { KMessageBox::ButtonCode tmp; // If we want the question box, but it is disable, then enable it if (!KMessageBox::shouldBeShownYesNo(QLatin1String("WarnAboutSessionRestart"), tmp)) KMessageBox::enableMessage(QLatin1String("WarnAboutSessionRestart")); const QString& name = m_worksheet->session()->backend()->name(); KMessageBox::ButtonCode rc = KMessageBox::questionYesNo(widget(), i18n("All the available calculation results will be lost. Do you really want to restart %1?", name), i18n("Restart %1?", name), KStandardGuiItem::yes(), KStandardGuiItem::no(), QLatin1String("WarnAboutSessionRestart") ); // Update setting's value // I don't know, that should I do with "No" with "Don't ask me again" // So hide warning only on "Yes" Settings::self()->setWarnAboutSessionRestart( KMessageBox::shouldBeShownYesNo(QLatin1String("WarnAboutSessionRestart"), tmp) || rc == KMessageBox::ButtonCode::No ); Settings::self()->save(); restart = (rc == KMessageBox::ButtonCode::Yes); } else { restart = true; } if (restart) { m_worksheet->session()->logout(); m_worksheet->loginToSession(); } } void CantorPart::worksheetStatusChanged(Cantor::Session::Status status) { qDebug()<<"wsStatusChange"<session()->status() == Cantor::Session::Running && m_sessionStatusCounter == count) { m_evaluate->setText(i18n("Interrupt")); m_evaluate->setShortcut(Qt::CTRL+Qt::Key_I); m_evaluate->setIcon(QIcon::fromTheme(QLatin1String("dialog-close"))); setStatusMessage(i18n("Calculating...")); } }); }else if (status == Cantor::Session::Done) { m_evaluate->setText(i18n("Evaluate Worksheet")); m_evaluate->setShortcut(Qt::CTRL+Qt::Key_E); m_evaluate->setIcon(QIcon::fromTheme(QLatin1String("system-run"))); setStatusMessage(i18n("Ready")); } } void CantorPart::showSessionError(const QString& message) { qDebug()<<"Error: "<isReadOnly()) { connect(m_worksheet->session(), &Cantor::Session::statusChanged, this, &CantorPart::worksheetStatusChanged); connect(m_worksheet->session(), &Cantor::Session::loginStarted,this, &CantorPart::worksheetSessionLoginStarted); connect(m_worksheet->session(), &Cantor::Session::loginDone,this, &CantorPart::worksheetSessionLoginDone); connect(m_worksheet->session(), &Cantor::Session::error, this, &CantorPart::showSessionError); loadAssistants(); m_panelHandler->setSession(m_worksheet->session()); adjustGuiToSession(); // Don't set modification flag, if we add command entry in empty worksheet const bool modified = this->isModified(); if (m_worksheet->isEmpty()) m_worksheet->appendCommandEntry(); setModified(modified); } else { setReadOnly(); // Clear assistants for (KXMLGUIClient* client: childClients()) { Cantor::Assistant* assistant = dynamic_cast(client); if (assistant) { factory()->removeClient(client); removeChildClient(client); assistant->deleteLater(); } } } m_worksheetview->setEnabled(true); m_worksheetview->setFocus(); setStatusMessage(i18n("Initialization complete")); updateCaption(); } void CantorPart::worksheetSessionLoginStarted() { setStatusMessage(i18n("Initializing...")); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } void CantorPart::worksheetSessionLoginDone() { setStatusMessage(i18n("Ready")); m_typeset->setEnabled(true); m_restart->setEnabled(true); QApplication::restoreOverrideCursor(); } void CantorPart::enableTypesetting(bool enable) { m_worksheet->session()->setTypesettingEnabled(enable); } void CantorPart::showBackendHelp() { qDebug()<<"showing backends help"; auto* backend = m_worksheet->session()->backend(); QUrl url = backend->helpUrl(); qDebug()<<"launching url "<isReadOnly()) - emit setCaption(filename, QIcon::fromTheme(m_worksheet->session()->backend()->icon())); + { + if (m_worksheet->session()) + emit setCaption(filename, QIcon::fromTheme(m_worksheet->session()->backend()->icon())); + } else emit setCaption(filename+QLatin1Char(' ') + i18n("[read-only]"), QIcon()); } void CantorPart::pluginsChanged() { for (auto* plugin : m_panelHandler->plugins()) connect(plugin, &Cantor::PanelPlugin::requestRunCommand, this, &CantorPart::runCommand); } void CantorPart::loadAssistants() { qDebug()<<"loading assistants..."; QStringList assistantDirs; for (const QString& dir : QCoreApplication::libraryPaths()) assistantDirs << dir + QDir::separator() + QLatin1String("cantor/assistants"); QPluginLoader loader; for (const QString& dir : assistantDirs) { qDebug() << "dir: " << dir; QStringList assistants; QDir assistantDir = QDir(dir); assistants = assistantDir.entryList(); for (const QString& assistant : assistants) { if (assistant == QLatin1String(".") || assistant == QLatin1String("..")) continue; loader.setFileName(dir + QDir::separator() + assistant); if (!loader.load()){ qDebug() << "Error while loading assistant: " << assistant; continue; } KPluginFactory* factory = KPluginLoader(loader.fileName()).factory(); auto* plugin = factory->create(this); auto* backend=worksheet()->session()->backend(); KPluginMetaData info(loader); plugin->setPluginInfo(info); plugin->setBackend(backend); bool supported=true; for (const QString& req : plugin->requiredExtensions()) supported = supported && backend->extensions().contains(req); if(supported) { qDebug() << "plugin " << info.name() << " is supported by " << backend->name() << ", requires extensions " << plugin->requiredExtensions(); plugin->initActions(); connect(plugin, &Cantor::Assistant::requested, this, &CantorPart::runAssistant); }else { qDebug() << "plugin " << info.name() << " is not supported by "<name(); removeChildClient(plugin); plugin->deleteLater(); } } } } void CantorPart::runAssistant() { Cantor::Assistant* a = qobject_cast(sender()); QStringList cmds = a->run(widget()); qDebug()<appendCommandEntry(cmd); } void CantorPart::showSearchBar() { if (!m_searchBar) { m_searchBar = new SearchBar(widget(), m_worksheet); widget()->layout()->addWidget(m_searchBar); connect(m_searchBar, &SearchBar::destroyed, this, &CantorPart::searchBarDeleted); } m_findNext->setEnabled(true); m_findPrev->setEnabled(true); m_searchBar->showStandard(); m_searchBar->setFocus(); } void CantorPart::showExtendedSearchBar() { if (!m_searchBar) { m_searchBar = new SearchBar(widget(), m_worksheet); widget()->layout()->addWidget(m_searchBar); connect(m_searchBar, &SearchBar::destroyed, this, &CantorPart::searchBarDeleted); } m_findNext->setEnabled(true); m_findPrev->setEnabled(true); m_searchBar->showExtended(); m_searchBar->setFocus(); } void CantorPart::findNext() { if (m_searchBar) m_searchBar->next(); } void CantorPart::findPrev() { if (m_searchBar) m_searchBar->prev(); } void CantorPart::searchBarDeleted() { m_searchBar = nullptr; m_findNext->setEnabled(false); m_findPrev->setEnabled(false); } void CantorPart::adjustGuiToSession() { auto capabilities = m_worksheet->session()->backend()->capabilities(); #ifdef WITH_EPS if (Cantor::LatexRenderer::isLatexAvailable()) m_typeset->setVisible(capabilities.testFlag(Cantor::Backend::LaTexOutput)); #else m_typeset->setVisible(false); #endif m_completion->setVisible(capabilities.testFlag(Cantor::Backend::Completion)); //this is 0 on the first call if(m_showBackendHelp) m_showBackendHelp->setText(i18n("Show %1 Help", m_worksheet->session()->backend()->name())); } void CantorPart::publishWorksheet() { int ret = KMessageBox::questionYesNo(widget(), i18n("Do you want to upload current Worksheet to public web server?"), i18n("Question - Cantor")); if (ret != KMessageBox::Yes) return; if (isModified()||url().isEmpty()) { ret = KMessageBox::warningContinueCancel(widget(), i18n("The Worksheet is not saved. You should save it before uploading."), i18n("Warning - Cantor"), KStandardGuiItem::save(), KStandardGuiItem::cancel()); if (ret != KMessageBox::Continue) return; if (!saveFile()) return; } qDebug()<<"uploading file "<session()->backend()->id().toLower()), widget()); dialog.setUploadFile(url()); Q_UNUSED(dialog.exec()); } void CantorPart::print() { QPrinter printer; QPointer dialog = new QPrintDialog(&printer, widget()); // TODO: Re-enable print selection //if (m_worksheet->textCursor().hasSelection()) // dialog->addEnabledOption(QAbstractPrintDialog::PrintSelection); if (dialog->exec() == QDialog::Accepted) m_worksheet->print(&printer); delete dialog; } void CantorPart::printPreview() { QPrintPreviewDialog* dialog = new QPrintPreviewDialog(widget()); connect(dialog, &QPrintPreviewDialog::paintRequested, m_worksheet, &Worksheet::print); Q_UNUSED(dialog->exec()); } void CantorPart::showScriptEditor(bool show) { if(show) { if (m_scriptEditor) { return; } auto* scriptE = dynamic_cast(m_worksheet->session()->backend()->extension(QLatin1String("ScriptExtension"))); if (!scriptE) { return; } m_scriptEditor = new ScriptEditorWidget(scriptE->scriptFileFilter(), scriptE->highlightingMode(), widget()->window()); connect(m_scriptEditor, &ScriptEditorWidget::runScript, this, &CantorPart::runScript); connect(m_scriptEditor, &ScriptEditorWidget::destroyed, this, &CantorPart::scriptEditorClosed); m_scriptEditor->show(); }else { m_scriptEditor->deleteLater(); } } void CantorPart::scriptEditorClosed() { QAction* showEditor = actionCollection()->action(QLatin1String("show_editor")); if (showEditor) { showEditor->setChecked(false); } } void CantorPart::runScript(const QString& file) { auto* backend = m_worksheet->session()->backend(); if(!backend->extensions().contains(QLatin1String("ScriptExtension"))) { KMessageBox::error(widget(), i18n("This backend does not support scripts."), i18n("Error - Cantor")); return; } auto* scriptE = dynamic_cast(backend->extension(QLatin1String("ScriptExtension"))); if (scriptE) m_worksheet->appendCommandEntry(scriptE->runExternalScript(file)); } void CantorPart::blockStatusBar() { m_statusBarBlocked=true; } void CantorPart::unblockStatusBar() { m_statusBarBlocked = false; if(!m_cachedStatusMessage.isNull()) setStatusMessage(m_cachedStatusMessage); m_cachedStatusMessage.clear(); } void CantorPart::setStatusMessage(const QString& message) { if(!m_statusBarBlocked) emit setStatusBarText(message); else m_cachedStatusMessage = message; } void CantorPart::showImportantStatusMessage(const QString& message) { setStatusMessage(message); blockStatusBar(); QTimer::singleShot(3000, this, SLOT(unblockStatusBar())); } K_PLUGIN_FACTORY_WITH_JSON(CantorPartFactory, "cantor_part.json", registerPlugin();) #include "cantor_part.moc" diff --git a/src/cantor_part.h b/src/cantor_part.h index ef68e789..452198ac 100644 --- a/src/cantor_part.h +++ b/src/cantor_part.h @@ -1,187 +1,189 @@ /* 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #ifndef CANTORPART_H #define CANTORPART_H #include #include #include #include class QWidget; class Worksheet; class WorksheetView; class SarchBar; class SearchBar; class ScriptEditorWidget; class KAboutData; class QAction; class KToggleAction; class QProgressDialog; namespace Cantor{ class PanelPluginHandler; } /** * This is a "Part". It that does all the real work in a KPart * application. * * @short Main Part * @author Alexander Rieder */ class CantorPart : public KParts::ReadWritePart { Q_OBJECT public: /** * Default constructor */ CantorPart(QWidget *parentWidget,QObject *parent, const QVariantList &args); /** * Destructor */ ~CantorPart() override; /** * This is a virtual function inherited from KParts::ReadWritePart. * A shell will use this to inform this Part if it should act * read-only */ void setReadWrite(bool rw) override; /** * Reimplemented to disable and enable Save action */ void setModified(bool modified) override; KAboutData& createAboutData(); Worksheet* worksheet(); Q_SIGNALS: void setCaption(const QString& caption, const QIcon& icon); void showHelp(const QString& help); void worksheetSave(const QUrl& url); +public Q_SLOTS: + void updateCaption(); + protected: /** * This must be implemented by each part */ bool openFile() override; /** * This must be implemented by each read-write part */ bool saveFile() override; /** * Called when this part becomes the active one, * or if it looses activity **/ void guiActivateEvent( KParts::GUIActivateEvent * event ) override; void loadAssistants(); void adjustGuiToSession(); void setReadOnly(); protected Q_SLOTS: void fileSaveAs(); void fileSavePlain(); void exportToLatex(); void evaluateOrInterrupt(); void restartBackend(); void enableTypesetting(bool enable); void showBackendHelp(); void print(); void printPreview(); void worksheetStatusChanged(Cantor::Session::Status stauts); void showSessionError(const QString& error); void worksheetSessionLoginStarted(); void worksheetSessionLoginDone(); void initialized(); - void updateCaption(); void pluginsChanged(); void runCommand(const QString& value); void runAssistant(); void publishWorksheet(); void showScriptEditor(bool show); void scriptEditorClosed(); void runScript(const QString& file); void showSearchBar(); void showExtendedSearchBar(); void findNext(); void findPrev(); void searchBarDeleted(); /** sets the status message, or cached it, if the StatusBar is blocked. * Use this method instead of "emit setStatusBarText" */ void setStatusMessage(const QString& message); /** Shows an important status message. It makes sure the message is displayed, * by blocking the statusbarText for 3 seconds */ void showImportantStatusMessage(const QString& message); /** Blocks the StatusBar for new messages, so the currently shown one won't be overridden */ void blockStatusBar(); /** Removes the block from the StatusBar, and shows the last one of the StatusMessages that where set during the block **/ void unblockStatusBar(); private: Worksheet *m_worksheet; WorksheetView *m_worksheetview; SearchBar *m_searchBar; QPointer m_scriptEditor; Cantor::PanelPluginHandler* m_panelHandler; QProgressDialog* m_initProgressDlg; bool m_showProgressDlg; QAction * m_evaluate; QAction * m_restart; QAction * m_save; QAction * m_findNext; QAction * m_findPrev; KToggleAction* m_typeset; KToggleAction* m_highlight; KToggleAction* m_completion; KToggleAction* m_exprNumbering; KToggleAction* m_animateWorksheet; KToggleAction* m_embeddedMath; QAction * m_showBackendHelp; QVector m_editActions; QString m_cachedStatusMessage; bool m_statusBarBlocked; unsigned int m_sessionStatusCounter; }; #endif // CANTORPART_H