diff --git a/src/cantor.cpp b/src/cantor.cpp index 8f892df0..6d5cb8cf 100644 --- a/src/cantor.cpp +++ b/src/cantor.cpp @@ -1,638 +1,641 @@ /* 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 "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); } void CantorShell::load(const QUrl &url) { if (!m_part||!m_part->url().isEmpty() || m_part->isModified() ) { addWorksheet(QLatin1String("null")); m_tabWidget->setCurrentIndex(m_parts.size()-1); } m_part->openUrl( url ); } bool CantorShell::hasAvailableBackend() { bool hasBackend=false; foreach(Cantor::Backend* b, Cantor::Backend::availableBackends()) { if(b->isEnabled()) hasBackend=true; } return hasBackend; } 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); 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' } void CantorShell::fileNew() { if (sender()->inherits("QAction")) { QAction * a = qobject_cast(sender()); QString backendName = a->data().toString(); if (!backendName.isEmpty()) { addWorksheet(backendName); return; } } 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 QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Open file"), QUrl(), i18n("Cantor Worksheet (*.cws)")); if (url.isEmpty() == false) { // About this function, the style guide ( // http://developer.kde.org/documentation/standards/kde/style/basics/index.html ) // 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() { if(hasAvailableBackend()) //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 misssing 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 KPluginFactory* factory = KPluginLoader(QLatin1String("libcantorpart")).factory(); if (factory) { - // 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()<isEnabled() || backendName == QLatin1String("null")) { - connect(part, SIGNAL(setCaption(QString,QIcon)), this, SLOT(setTabCaption(QString,QIcon))); - m_parts.append(part); - - int tab = m_tabWidget->addTab(part->widget(), QIcon::fromTheme(backend->icon()), 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(); + // 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(), QIcon::fromTheme(backend->icon()), 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(); + } + else + { + qDebug()<<"error creating part "; + } } else - delete part; + { + KMessageBox::error(this, i18n("%1 backend installed, but innactive. Please check installation and Cantor settings", backendName), i18n("Cantor")); + } } else - { - qDebug()<<"error creating part "; - } - + KMessageBox::error(this, i18n("Backend %1 is not installed", backendName), i18n("Cantor")); } else { // if we couldn't find our Part, we exit since the Shell by // itself can't do anything useful KMessageBox::error(this, i18n("Could not find the Cantor Part.")); 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()); 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; } } 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 (m_pluginsVisibility[m_part].contains(plugin->name())) docker->show(); else docker->hide(); else docker->show(); if(last!=nullptr) tabifyDockWidget(last, docker); last=docker; connect(plugin, SIGNAL(visibilityRequested()), docker, SLOT(raise())); m_panels.append(docker); //Create the action to show/hide this panel panelActions<toggleViewAction(); } plugActionList(QLatin1String("view_show_panel_list"), panelActions); unplugActionList(QLatin1String("new_worksheet_with_backend_list")); QList newBackendActions; 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())); newBackendActions << action; } plugActionList(QLatin1String("new_worksheet_with_backend_list"), 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; } diff --git a/src/cantor_part.cpp b/src/cantor_part.cpp index aef08c31..30d32538 100644 --- a/src/cantor_part.cpp +++ b/src/cantor_part.cpp @@ -1,976 +1,960 @@ /* 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_part.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 #include "worksheet.h" #include "worksheetview.h" #include "searchbar.h" #include "scripteditor/scripteditorwidget.h" #include "lib/backend.h" #include "lib/extension.h" #include "lib/assistant.h" #include "lib/panelpluginhandler.h" #include "lib/panelplugin.h" #include "lib/worksheetaccess.h" #include "settings.h" //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, SIGNAL(modified()), this, SIGNAL(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_initProgressDlg(nullptr), m_showProgressDlg(true), m_showBackendHelp(nullptr), m_statusBarBlocked(false), m_sessionStatusCounter(0) { m_panelHandler=new Cantor::PanelPluginHandler(this); connect(m_panelHandler, SIGNAL(pluginsChanged()), this, SLOT(pluginsChanged())); QString backendName; if(args.isEmpty()) backendName=QLatin1String("null"); else 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=Cantor::Backend::getBackend(backendName); - if(!b) - { - KMessageBox::error(parentWidget, i18n("Backend %1 is not installed", backendName), i18n("Error - Cantor")); - setWidget(new QWidget(parentWidget)); - return; - } - - if (b && !b->isEnabled() && backendName != QLatin1String("null")) - { - KMessageBox::information(parentWidget, i18n("There are some problems with the %1 backend,\n"\ - "please check your configuration or install the needed packages.\n" - "You will only be able to view this worksheet.", backendName), i18n("Cantor")); - setWidget(new QWidget(parentWidget)); - return; - } - qDebug()<<"Backend "<name()<<" offers extensions: "<extensions(); auto* collection = actionCollection(); //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, SIGNAL(modified()), this, SLOT(setModified())); connect(m_worksheet, SIGNAL(showHelp(QString)), this, SIGNAL(showHelp(QString))); connect(m_worksheet, SIGNAL(loaded()), this, SLOT(initialized())); connect(collection, SIGNAL(inserted(QAction*)), m_worksheet, SLOT(registerShortcut(QAction*))); 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 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, SIGNAL(triggered()), this, SLOT(fileSavePlain())); QAction* undo = KStandardAction::undo(m_worksheet, SIGNAL(undo()), collection); undo->setPriority(QAction::LowPriority); connect(m_worksheet, SIGNAL(undoAvailable(bool)), undo, SLOT(setEnabled(bool))); m_editActions.push_back(undo); QAction* redo = KStandardAction::redo(m_worksheet, SIGNAL(redo()), collection); redo->setPriority(QAction::LowPriority); connect(m_worksheet, SIGNAL(redoAvailable(bool)), redo, SLOT(setEnabled(bool))); m_editActions.push_back(redo); QAction* cut = KStandardAction::cut(m_worksheet, SIGNAL(cut()), collection); cut->setPriority(QAction::LowPriority); connect(m_worksheet, SIGNAL(cutAvailable(bool)), cut, SLOT(setEnabled(bool))); m_editActions.push_back(cut); QAction* copy = KStandardAction::copy(m_worksheet, SIGNAL(copy()), collection); copy->setPriority(QAction::LowPriority); connect(m_worksheet, SIGNAL(copyAvailable(bool)), copy, SLOT(setEnabled(bool))); QAction* paste = KStandardAction::paste(m_worksheet, SLOT(paste()), collection); paste->setPriority(QAction::LowPriority); connect(m_worksheet, SIGNAL(pasteAvailable(bool)), paste, SLOT(setEnabled(bool))); 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, SIGNAL(triggered()), this, SLOT(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, SIGNAL(triggered()), this, SLOT(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, SIGNAL(toggled(bool)), this, SLOT(enableTypesetting(bool))); m_highlight = new KToggleAction(i18n("Syntax Highlighting"), collection); m_highlight->setChecked(Settings::self()->highlightDefault()); collection->addAction(QLatin1String("enable_highlighting"), m_highlight); connect(m_highlight, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableHighlighting(bool))); m_completion = new KToggleAction(i18n("Completion"), collection); m_completion->setChecked(Settings::self()->completionDefault()); collection->addAction(QLatin1String("enable_completion"), m_completion); connect(m_completion, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableCompletion(bool))); 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, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableExpressionNumbering(bool))); m_animateWorksheet = new KToggleAction(i18n("Animate Worksheet"), collection); m_animateWorksheet->setChecked(Settings::self()->animationDefault()); collection->addAction(QLatin1String("enable_animations"), m_animateWorksheet); connect(m_animateWorksheet, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableAnimations(bool))); 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, SIGNAL(triggered()), this, SLOT(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, SIGNAL(triggered()), m_worksheet, SLOT(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, SIGNAL(triggered()), m_worksheet, SLOT(removeCurrentEntry())); m_editActions.push_back(removeCurrent); m_showBackendHelp = new QAction(i18n("Show %1 Help", b->name()) , collection); m_showBackendHelp->setIcon(QIcon::fromTheme(QLatin1String("help-contents"))); collection->addAction(QLatin1String("backend_help"), m_showBackendHelp); connect(m_showBackendHelp, SIGNAL(triggered()), this, SLOT(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, SIGNAL(triggered()), this, SLOT(publishWorksheet())); */ KToggleAction* showEditor = new KToggleAction(i18n("Show Script Editor"), collection); showEditor->setChecked(false); collection->addAction(QLatin1String("show_editor"), showEditor); connect(showEditor, SIGNAL(toggled(bool)), this, SLOT(showScriptEditor(bool))); showEditor->setEnabled(b->extensions().contains(QLatin1String("ScriptExtension"))); 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); 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("http://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==nullptr) { 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); return true; } void CantorPart::fileSaveAs() { // this slot is called whenever the File->Save As menu is selected QString worksheetFilter = i18n("Cantor Worksheet (*.cws)"); QString filter = worksheetFilter; if (!m_worksheet->isReadOnly()) { //if the backend supports scripts, also append their scriptFile endings to the filter Cantor::Backend * const backend=m_worksheet->session()->backend(); if (backend->extensions().contains(QLatin1String("ScriptExtension"))) { Cantor::ScriptExtension* e=dynamic_cast(backend->extension(QLatin1String("ScriptExtension"))); filter+=QLatin1String(";;")+e->scriptFileFilter(); } } QString selectedFilter; QString file_name = QFileDialog::getSaveFileName(widget(), i18n("Save as"), QString(), filter, &selectedFilter); if (file_name.isEmpty()) return; //depending on user's selection, save as a worksheet or as a plain script file if (selectedFilter == worksheetFilter) { if (!file_name.endsWith(QLatin1String(".cws"))) file_name += QLatin1String(".cws"); saveAs(QUrl::fromLocalFile(file_name)); } 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 Settings::self()->setWarnAboutSessionRestart( KMessageBox::shouldBeShownYesNo(QLatin1String("WarnAboutSessionRestart"), tmp)); Settings::self()->save(); restart = rc == KMessageBox::ButtonCode::Yes; } else { KMessageBox::ButtonCode rc; KMessageBox::shouldBeShownYesNo(QLatin1String("WarnAboutSessionRestart"), rc); restart = rc == KMessageBox::ButtonCode::Yes; } 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(), SIGNAL(statusChanged(Cantor::Session::Status)), this, SLOT(worksheetStatusChanged(Cantor::Session::Status))); connect(m_worksheet->session(), SIGNAL(loginStarted()),this, SLOT(worksheetSessionLoginStarted())); connect(m_worksheet->session(), SIGNAL(loginDone()),this, SLOT(worksheetSessionLoginDone())); connect(m_worksheet->session(), SIGNAL(error(QString)), this, SLOT(showSessionError(QString))); 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"; Cantor::Backend* backend=m_worksheet->session()->backend(); QUrl url = backend->helpUrl(); qDebug()<<"launching url "<isReadOnly()) 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, SIGNAL(requestRunCommand(QString)), this, SLOT(runCommand(QString))); } 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(); Cantor::Assistant* plugin = factory->create(this); Cantor::Backend* 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, SIGNAL(requested()), this, SLOT(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, SIGNAL(destroyed(QObject*)), this, SLOT(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, SIGNAL(destroyed(QObject*)), this, SLOT(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() { Cantor::Backend::Capabilities capabilities = m_worksheet->session()->backend()->capabilities(); #ifdef WITH_EPS 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()); 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, SIGNAL(paintRequested(QPrinter*)), m_worksheet, SLOT(print(QPrinter*))); dialog->exec(); } void CantorPart::showScriptEditor(bool show) { if(show) { if (m_scriptEditor) { return; } Cantor::ScriptExtension* 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, SIGNAL(runScript(QString)), this, SLOT(runScript(QString))); connect(m_scriptEditor, SIGNAL(destroyed()), this, SLOT(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) { Cantor::Backend* 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; } Cantor::ScriptExtension* scriptE=dynamic_cast(backend->extension(QLatin1String("ScriptExtension"))); 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"