diff --git a/src/cantor.kcfg b/src/cantor.kcfg index 1dca74a6..60c291f7 100644 --- a/src/cantor.kcfg +++ b/src/cantor.kcfg @@ -1,42 +1,46 @@ PopupCompletion true true true false + + + true + false diff --git a/src/cantor_part.cpp b/src/cantor_part.cpp index 5d0c60d5..60e1ddce 100644 --- a/src/cantor_part.cpp +++ b/src/cantor_part.cpp @@ -1,672 +1,687 @@ /* 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 "cantor_part.moc" #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 "scripteditorwidget.h" #include "lib/backend.h" #include "lib/extension.h" #include "lib/assistant.h" #include "lib/panelpluginhandler.h" #include "lib/panelplugin.h" #include "settings.h" K_PLUGIN_FACTORY(CantorPartFactory, registerPlugin();) K_EXPORT_PLUGIN(CantorPartFactory("cantor")) CantorPart::CantorPart( QWidget *parentWidget, QObject *parent, const QVariantList & args ): KParts::ReadWritePart(parent) { // we need an instance setComponentData( CantorPartFactory::componentData() ); m_showBackendHelp=0; m_initProgressDlg=0; m_statusBarBlocked=false; m_panelHandler=new Cantor::PanelPluginHandler(this); connect(m_panelHandler, SIGNAL(pluginsChanged()), this, SLOT(pluginsChanged())); kDebug()<<"Created a CantorPart"; QString backendName; if(args.isEmpty()) backendName="null"; else backendName=args.first().toString(); Cantor::Backend* b=Cantor::Backend::createBackend(backendName); if(!b) { KMessageBox::error(parentWidget, i18n("Backend %1 is not installed", backendName), i18n("Error - Cantor")); setWidget(new QWidget(parentWidget)); //fake being modified so the shell won't try to reuse this part ReadWritePart::setModified(true); return; } kDebug()<<"Backend "<name()<<" offers extensions: "<extensions(); m_worksheet=new Worksheet(b, parentWidget); m_worksheetview=new WorksheetView(m_worksheet, parentWidget); 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(const QString&)), this, SIGNAL(showHelp(const QString&))); connect(m_worksheet, SIGNAL(sessionChanged()), this, SLOT(worksheetSessionChanged())); // notify the part that this is our internal widget setWidget(m_worksheetview); /// How should we add the rich text editing actions? // create our actions //m_worksheet->createActions( actionCollection() ); KStandardAction::saveAs(this, SLOT(fileSaveAs()), actionCollection()); m_save = KStandardAction::save(this, SLOT(save()), actionCollection()); + KAction* savePlain=new KAction(i18n("Save Plain Text"), actionCollection()); + actionCollection()->addAction("file_save_plain", savePlain); + savePlain->setIcon(KIcon("document-save")); + connect(savePlain, SIGNAL(triggered()), this, SLOT(fileSavePlain())); + KAction* latexExport=new KAction(i18n("Export to LaTeX"), actionCollection()); actionCollection()->addAction("file_export_latex", latexExport); latexExport->setIcon(KIcon("document-export")); connect(latexExport, SIGNAL(triggered()), this, SLOT(exportToLatex())); KStandardAction::print(this, SLOT(print()), actionCollection()); KStandardAction::zoomIn(m_worksheetview, SLOT(zoomIn()), actionCollection()); KStandardAction::zoomOut(m_worksheetview, SLOT(zoomOut()), actionCollection()); m_evaluate=new KAction(i18n("Evaluate Worksheet"), actionCollection()); actionCollection()->addAction("evaluate_worksheet", m_evaluate); m_evaluate->setIcon(KIcon("system-run")); connect(m_evaluate, SIGNAL(triggered()), this, SLOT(evaluateOrInterrupt())); m_typeset=new KToggleAction(i18n("Typeset using LaTeX"), actionCollection()); m_typeset->setChecked(Settings::self()->typesetDefault()); actionCollection()->addAction("enable_typesetting", m_typeset); connect(m_typeset, SIGNAL(toggled(bool)), this, SLOT(enableTypesetting(bool))); m_highlight=new KToggleAction(i18n("Syntax Highlighting"), actionCollection()); m_highlight->setChecked(Settings::self()->highlightDefault()); actionCollection()->addAction("enable_highlighting", m_highlight); connect(m_highlight, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableHighlighting(bool))); m_completion=new KToggleAction(i18n("Completion"), actionCollection()); m_completion->setChecked(Settings::self()->completionDefault()); actionCollection()->addAction("enable_completion", m_completion); connect(m_completion, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableCompletion(bool))); m_exprNumbering=new KToggleAction(i18n("Line Numbers"), actionCollection()); m_exprNumbering->setChecked(Settings::self()->expressionNumberingDefault()); actionCollection()->addAction("enable_expression_numbers", m_exprNumbering); connect(m_exprNumbering, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableExpressionNumbering(bool))); + m_animateWorksheet=new KToggleAction(i18n("Animate Worksheet"), actionCollection()); + m_animateWorksheet->setChecked(Settings::self()->animationDefault()); + actionCollection()->addAction("enable_animations", m_animateWorksheet); + connect(m_animateWorksheet, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableAnimations(bool))); + KAction* restart=new KAction(i18n("Restart Backend"), actionCollection()); actionCollection()->addAction("restart_backend", restart); restart->setIcon(KIcon("system-reboot")); connect(restart, SIGNAL(triggered()), this, SLOT(restartBackend())); KAction* evaluateCurrent=new KAction(i18n("Evaluate Entry"), actionCollection()); evaluateCurrent->setShortcut(Qt::SHIFT + Qt::Key_Return); actionCollection()->addAction("evaluate_current", evaluateCurrent); connect(evaluateCurrent, SIGNAL(triggered()), m_worksheet, SLOT(evaluateCurrentEntry())); KAction* insertCommandEntry=new KAction(i18n("Insert Command Entry"), actionCollection()); insertCommandEntry->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_command_entry", insertCommandEntry); connect(insertCommandEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertCommandEntry())); KAction* insertTextEntry=new KAction(i18n("Insert Text Entry"), actionCollection()); //insertEntry->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_text_entry", insertTextEntry); connect(insertTextEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertTextEntry())); KAction* insertLatexEntry=new KAction(i18n("Insert Latex Entry"), actionCollection()); //insertEntry->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_latex_entry", insertLatexEntry); connect(insertLatexEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertLatexEntry())); KAction* insertPageBreakEntry=new KAction(i18n("Insert Page Break"), actionCollection()); actionCollection()->addAction("insert_page_break_entry", insertPageBreakEntry); connect(insertPageBreakEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertPageBreakEntry())); KAction* insertImageEntry=new KAction(i18n("Insert Image"), actionCollection()); actionCollection()->addAction("insert_image_entry", insertImageEntry); connect(insertImageEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertImageEntry())); /* KAction* insertCommandEntryBefore=new KAction(i18n("Insert Command Entry Before"), actionCollection()); //insertCommandEntryBefore->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_command_entry_before", insertCommandEntryBefore); connect(insertCommandEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertCommandEntryBefore())); KAction* insertTextEntryBefore=new KAction(i18n("Insert Text Entry Before"), actionCollection()); //insertTextEntryBefore->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_text_entry_before", insertTextEntryBefore); connect(insertTextEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertTextEntryBefore())); KAction* insertPageBreakEntryBefore=new KAction(i18n("Insert Page Break Before"), actionCollection()); actionCollection()->addAction("insert_page_break_entry_before", insertPageBreakEntryBefore); connect(insertPageBreakEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertPageBreakEntryBefore())); KAction* insertImageEntryBefore=new KAction(i18n("Insert Image Entry Before"), actionCollection()); //insertTextEntryBefore->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_image_entry_before", insertImageEntryBefore); connect(insertImageEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertImageEntryBefore())); */ KAction* removeCurrent=new KAction(i18n("Remove current Entry"), actionCollection()); removeCurrent->setShortcut(Qt::ShiftModifier + Qt::Key_Delete); actionCollection()->addAction("remove_current", removeCurrent); connect(removeCurrent, SIGNAL(triggered()), m_worksheet, SLOT(removeCurrentEntry())); m_showBackendHelp=new KAction(i18n("Show %1 Help", b->name()) , actionCollection()); m_showBackendHelp->setIcon(KIcon("help-contents")); actionCollection()->addAction("backend_help", m_showBackendHelp); connect(m_showBackendHelp, SIGNAL(triggered()), this, SLOT(showBackendHelp())); KAction* publishWorksheet=new KAction(i18n("Publish Worksheet"), actionCollection()); publishWorksheet->setIcon(KIcon("get-hot-new-stuff")); actionCollection()->addAction("file_publish_worksheet", publishWorksheet); connect(publishWorksheet, SIGNAL(triggered()), this, SLOT(publishWorksheet())); KToggleAction* showEditor=new KToggleAction(i18n("Show Script Editor"), actionCollection()); showEditor->setChecked(false); actionCollection()->addAction("show_editor", showEditor); connect(showEditor, SIGNAL(toggled(bool)), this, SLOT(showScriptEditor(bool))); showEditor->setEnabled(b->extensions().contains("ScriptExtension")); KAction* showCompletion=new KAction(i18n("Show Completion"), actionCollection()); actionCollection()->addAction("show_completion", showCompletion); showCompletion->setShortcut(Qt::Key_Tab); //Qt::CTRL + Qt::Key_Space); connect(showCompletion, SIGNAL(triggered()), m_worksheet, SLOT(showCompletion())); // set our XML-UI resource file setXMLFile("cantor_part.rc"); // we are read-write by default setReadWrite(true); // we are not modified since we haven't done anything yet setModified(false); worksheetSessionChanged(); } CantorPart::~CantorPart() { if (m_scriptEditor) { disconnect(m_scriptEditor, SIGNAL(destroyed()), this, SLOT(scriptEditorClosed())); delete m_scriptEditor; } } void CantorPart::setReadWrite(bool rw) { // notify your internal widget of the read-write state m_worksheetview->setInteractive(rw); ReadWritePart::setReadWrite(rw); } 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 if (modified) m_save->setEnabled(true); else m_save->setEnabled(false); // 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) KAboutData *aboutData = new KAboutData("cantorpart", "cantor", ki18n("CantorPart"), "0.3"); aboutData->addAuthor(ki18n("Alexander Rieder"), KLocalizedString(), "alexanderrieder@gmail.com"); return aboutData; } bool CantorPart::openFile() { //don't crash if for some reason the worksheet is invalid if(m_worksheet==0) { kWarning()<<"trying to open in an invalid cantor part"; return false; } m_worksheet->load(localFilePath()); // just for fun, set the status bar //setStatusMessage( m_url.prettyUrl() ); updateCaption(); return true; } bool CantorPart::saveFile() { // if we aren't read-write, return immediately if (isReadWrite() == false) return false; kDebug()<<"saving to: "<save( localFilePath() ); - else - m_worksheet->savePlain( localFilePath()); - - } + m_worksheet->save( localFilePath() ); setModified(false); return true; } void CantorPart::fileSaveAs() { // this slot is called whenever the File->Save As menu is selected, QString filter=i18n("*.cws|Cantor Worksheet"); //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("ScriptExtension")) { Cantor::ScriptExtension* e=dynamic_cast(backend->extension("ScriptExtension")); filter+='\n'+e->scriptFileFilter(); } QString file_name = KFileDialog::getSaveFileName(KUrl(), filter, widget()); - if (file_name.isEmpty() == false) + if (!file_name.isEmpty()) { + if (!file_name.endsWith(QLatin1String(".cws")) && + !file_name.endsWith(QLatin1String(".mws"))) + file_name += ".cws"; saveAs(file_name); + } updateCaption(); } +void CantorPart::fileSavePlain() +{ + QString file_name = KFileDialog::getSaveFileName(KUrl(), "", widget()); + if (!file_name.isEmpty()) + m_worksheet->savePlain(file_name); +} + void CantorPart::exportToLatex() { // this slot is called whenever the File->Save As menu is selected, QString filter=i18n("*.tex|LaTeX Document"); QString file_name = KFileDialog::getSaveFileName(KUrl(), filter, widget()); if (file_name.isEmpty() == false) { int exportImages=KMessageBox::questionYesNo(widget(), i18n("Do you also want to export the images?"), i18n("Question - Cantor")); m_worksheet->saveLatex( file_name , exportImages==KMessageBox::Yes); } } 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() { kDebug()<<"evalorinterrupt"; if(m_worksheet->isRunning()) m_worksheet->interrupt(); else m_worksheet->evaluate(); } void CantorPart::restartBackend() { m_worksheet->session()->logout(); m_worksheet->session()->login(); } void CantorPart::worksheetStatusChanged(Cantor::Session::Status status) { kDebug()<<"wsStatusChange"<setText(i18n("Interrupt")); m_evaluate->setIcon(KIcon("dialog-close")); setStatusMessage(i18n("Calculating...")); }else { m_evaluate->setText(i18n("Evaluate Worksheet")); m_evaluate->setIcon(KIcon("system-run")); setStatusMessage(i18n("Ready")); } } void CantorPart::showSessionError(const QString& message) { kDebug()<<"Error: "<session(), SIGNAL(statusChanged(Cantor::Session::Status)), this, SLOT(worksheetStatusChanged(Cantor::Session::Status))); connect(m_worksheet->session(), SIGNAL(ready()),this, SLOT(initialized())); connect(m_worksheet->session(), SIGNAL(error(const QString&)), this, SLOT(showSessionError(const QString&))); loadAssistants(); m_panelHandler->setSession(m_worksheet->session()); adjustGuiToSession(); if(!m_initProgressDlg) { m_initProgressDlg=new KProgressDialog(widget(), i18n("Cantor"), i18n("Initializing Session")); m_initProgressDlg->setMinimumDuration(500); m_initProgressDlg->progressBar()->setRange(0, 0); } } void CantorPart::initialized() { if (m_worksheet->isEmpty()) m_worksheet->appendCommandEntry(); m_worksheetview->setEnabled(true); m_worksheetview->setFocus(); setStatusMessage(i18n("Initialization complete")); if(m_initProgressDlg) { m_initProgressDlg->deleteLater(); m_initProgressDlg=0; } updateCaption(); } void CantorPart::enableTypesetting(bool enable) { m_worksheet->session()->setTypesettingEnabled(enable); } void CantorPart::showBackendHelp() { kDebug()<<"showing backends help"; Cantor::Backend* backend=m_worksheet->session()->backend(); KUrl url=backend->helpUrl(); kDebug()<<"launching url "<session()->backend()->name(), filename)); } void CantorPart::pluginsChanged() { foreach(Cantor::PanelPlugin* plugin, m_panelHandler->plugins()) { connect(plugin, SIGNAL(requestRunCommand(QString)), this, SLOT(runCommand(QString))); } } void CantorPart::loadAssistants() { kDebug()<<"loading assistants..."; KService::List services; KServiceTypeTrader* trader = KServiceTypeTrader::self(); services = trader->query("Cantor/Assistant"); foreach (const KService::Ptr &service, services) { QString error; kDebug()<<"found service"<name(); Cantor::Assistant* assistant=service->createInstance(this, QVariantList(), &error); if (assistant==0) { kDebug()<<"error loading assistant"<name()<<": "<session()->backend(); KPluginInfo info(service); assistant->setPluginInfo(info); assistant->setBackend(backend); kDebug()<<"plugin "<name()<<" requires "<requiredExtensions(); bool supported=true; foreach(const QString& req, assistant->requiredExtensions()) supported=supported && backend->extensions().contains(req); kDebug()<<"plugin "<name()<<" is "<<(supported ? "":" not ")<<" supported by "<name(); if(supported) { assistant->initActions(); //createGui(assistant); connect(assistant, SIGNAL(requested()), this, SLOT(runAssistant())); }else { removeChildClient(assistant); assistant->deleteLater(); } } } void CantorPart::runAssistant() { Cantor::Assistant* a=qobject_cast(sender()); QStringList cmds=a->run(widget()); kDebug()<appendCommandEntry(cmd); } void CantorPart::adjustGuiToSession() { #ifdef WITH_EPS m_typeset->setVisible(m_worksheet->session()->backend()->capabilities().testFlag(Cantor::Backend::LaTexOutput)); #else m_typeset->setVisible(false); #endif m_completion->setVisible(m_worksheet->session()->backend()->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; } kDebug()<<"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::showScriptEditor(bool show) { if(show) { if (m_scriptEditor) { return; } Cantor::ScriptExtension* scriptE=dynamic_cast(m_worksheet->session()->backend()->extension("ScriptExtension")); if (!scriptE) { return; } m_scriptEditor=new ScriptEditorWidget(scriptE->scriptFileFilter(), widget()->window()); connect(m_scriptEditor, SIGNAL(runScript(const QString&)), this, SLOT(runScript(const QString&))); connect(m_scriptEditor, SIGNAL(destroyed()), this, SLOT(scriptEditorClosed())); m_scriptEditor->show(); }else { delete m_scriptEditor; } } void CantorPart::scriptEditorClosed() { QAction* showEditor = actionCollection()->action("show_editor"); if (showEditor) { showEditor->setChecked(false); } } void CantorPart::runScript(const QString& file) { Cantor::Backend* backend=m_worksheet->session()->backend(); if(!backend->extensions().contains("ScriptExtension")) { KMessageBox::error(widget(), i18n("This backend does not support scripts."), i18n("Error - Cantor")); return; } Cantor::ScriptExtension* scriptE=dynamic_cast(backend->extension("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())); } diff --git a/src/cantor_part.h b/src/cantor_part.h index 94ca91af..2dd68d8b 100644 --- a/src/cantor_part.h +++ b/src/cantor_part.h @@ -1,161 +1,163 @@ /* 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 class QWidget; class Worksheet; class WorksheetView; class ScriptEditorWidget; class KAboutData; class KAction; class KToggleAction; class KProgressDialog; 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 */ virtual ~CantorPart(); /** * This is a virtual function inherited from KParts::ReadWritePart. * A shell will use this to inform this Part if it should act * read-only */ virtual void setReadWrite(bool rw); /** * Reimplemented to disable and enable Save action */ virtual void setModified(bool modified); static KAboutData *createAboutData(); Worksheet* worksheet(); signals: void setCaption(const QString& caption); void showHelp(const QString& help); protected: /** * This must be implemented by each part */ virtual bool openFile(); /** * This must be implemented by each read-write part */ virtual bool saveFile(); /** * Called when this part becomes the active one, * or if it looses activity **/ void guiActivateEvent( KParts::GUIActivateEvent * event ); void loadAssistants(); void adjustGuiToSession(); protected slots: void fileSaveAs(); + void fileSavePlain(); void exportToLatex(); void evaluateOrInterrupt(); void restartBackend(); void enableTypesetting(bool enable); void showBackendHelp(); void print(); void worksheetStatusChanged(Cantor::Session::Status stauts); void showSessionError(const QString& error); void worksheetSessionChanged(); 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); /** 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; QPointer m_scriptEditor; Cantor::PanelPluginHandler* m_panelHandler; KProgressDialog* m_initProgressDlg; KAction* m_evaluate; KAction* m_save; KToggleAction* m_typeset; KToggleAction* m_highlight; KToggleAction* m_completion; KToggleAction* m_exprNumbering; + KToggleAction* m_animateWorksheet; KAction* m_showBackendHelp; QString m_cachedStatusMessage; bool m_statusBarBlocked; }; #endif // CANTORPART_H diff --git a/src/cantor_part.rc b/src/cantor_part.rc index 8a78d2f4..dd168dbc 100644 --- a/src/cantor_part.rc +++ b/src/cantor_part.rc @@ -1,68 +1,70 @@ + &View + &Worksheet diff --git a/src/commandentry.cpp b/src/commandentry.cpp index 26c658d2..1c65592d 100644 --- a/src/commandentry.cpp +++ b/src/commandentry.cpp @@ -1,745 +1,747 @@ /* 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) 2012 Martin Kuettler */ #include "commandentry.h" #include "worksheet.h" #include "worksheettextitem.h" #include "resultitem.h" #include "loadedexpression.h" #include "settings.h" #include "lib/expression.h" #include "lib/result.h" #include "lib/helpresult.h" #include "lib/completionobject.h" #include "lib/syntaxhelpobject.h" #include "lib/defaulthighlighter.h" #include "lib/session.h" #include #include #include #include #include #include #include #include const QString CommandEntry::Prompt=">>> "; const double CommandEntry::HorizontalSpacing = 4; const double CommandEntry::VerticalSpacing = 4; CommandEntry::CommandEntry(Worksheet* worksheet) : WorksheetEntry(worksheet) { m_expression = 0; m_completionObject = 0; m_syntaxHelpObject = 0; m_promptItem = new WorksheetTextItem(this, Qt::NoTextInteraction); m_promptItem->setPlainText(Prompt); m_commandItem = new WorksheetTextItem(this, Qt::TextEditorInteraction); m_commandItem->enableCompletion(true); m_errorItem = 0; m_resultItem = 0; connect(m_commandItem, SIGNAL(tabPressed()), this, SLOT(showCompletion())); connect(m_commandItem, SIGNAL(backtabPressed()), this, SLOT(selectPreviousCompletion())); connect(m_commandItem, SIGNAL(applyCompletion()), this, SLOT(applySelectedCompletion())); connect(m_commandItem, SIGNAL(execute()), this, SLOT(evaluateCommand())); connect(m_commandItem, SIGNAL(moveToPrevious(int, qreal)), this, SLOT(moveToPreviousItem(int, qreal))); connect(m_commandItem, SIGNAL(moveToNext(int, qreal)), this, SLOT(moveToNextItem(int, qreal))); connect(m_commandItem, SIGNAL(receivedFocus(WorksheetTextItem*)), worksheet, SLOT(highlightItem(WorksheetTextItem*))); + connect(worksheet, SIGNAL(updatePrompt()), + this, SLOT(updatePrompt())); } CommandEntry::~CommandEntry() { if (m_completionBox) m_completionBox->deleteLater(); } int CommandEntry::type() const { return Type; } void CommandEntry::populateMenu(KMenu *menu, const QPointF& pos) { kDebug() << "populate Menu"; WorksheetEntry::populateMenu(menu, pos); } void CommandEntry::moveToNextItem(int pos, qreal x) { WorksheetTextItem* item = qobject_cast(sender()); if (!item) return; if (item == m_commandItem || item == 0) { if (m_informationItems.isEmpty()) moveToNextEntry(pos, x); else currentInformationItem()->setFocusAt(pos, x); } else if (item == currentInformationItem()) { moveToNextEntry(pos, x); } } void CommandEntry::moveToPreviousItem(int pos, qreal x) { WorksheetTextItem* item = qobject_cast(sender()); if (!item) return; if (item == m_commandItem || item == 0) { moveToPreviousEntry(pos, x); } else if (item == currentInformationItem()) { m_commandItem->setFocusAt(pos, x); } } QString CommandEntry::command() { QString cmd = m_commandItem->toPlainText(); cmd.replace(QChar::ParagraphSeparator, '\n'); //Replace the U+2029 paragraph break by a Normal Newline cmd.replace(QChar::LineSeparator, '\n'); //Replace the line break by a Normal Newline return cmd; } void CommandEntry::setExpression(Cantor::Expression* expr) { if ( m_expression ) m_expression->deleteLater(); // Delete any previus error and/or result if(m_errorItem) { m_errorItem->deleteLater(); m_errorItem = 0; } foreach(WorksheetTextItem* item, m_informationItems) { item->deleteLater(); } m_informationItems.clear(); m_expression = 0; // Delete any previous result removeResult(); m_expression=expr; connect(expr, SIGNAL(gotResult()), this, SLOT(updateEntry())); connect(expr, SIGNAL(idChanged()), this, SLOT(updatePrompt())); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(expressionChangedStatus(Cantor::Expression::Status))); connect(expr, SIGNAL(needsAdditionalInformation(const QString&)), this, SLOT(showAdditionalInformationPrompt(const QString&))); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(updatePrompt())); updatePrompt(); if(expr->result()) { worksheet()->gotResult(expr); updateEntry(); } if(expr->status()!=Cantor::Expression::Computing) { expressionChangedStatus(expr->status()); } } Cantor::Expression* CommandEntry::expression() { return m_expression; } bool CommandEntry::acceptRichText() { return false; } void CommandEntry::setContent(const QString& content) { m_commandItem->setPlainText(content); } void CommandEntry::setContent(const QDomElement& content, const KZip& file) { m_commandItem->setPlainText(content.firstChildElement("Command").text()); LoadedExpression* expr=new LoadedExpression( worksheet()->session() ); expr->loadFromXml(content, file); setExpression(expr); } void CommandEntry::showCompletion() { //get the current line of the entry. If it's empty, ignore the call, //otherwise check for tab completion (if supported by the backend) const QString line = currentLine(); if(line.trimmed().isEmpty()) { return; } else if (isShowingCompletionPopup()) { QString comp = m_completionObject->completion(); kDebug() << "command" << m_completionObject->command(); kDebug() << "completion" << comp; if (comp != m_completionObject->command() || !m_completionObject->hasMultipleMatches()) { if (m_completionObject->hasMultipleMatches()) { completeCommandTo(comp, PreliminaryCompletion); } else { completeCommandTo(comp, FinalCompletion); m_completionBox->hide(); } } else { m_completionBox->down(); } } else { int p = m_commandItem->textCursor().positionInBlock(); Cantor::CompletionObject* tco=worksheet()->session()->completionFor(line, p); if(tco) setCompletion(tco); } } void CommandEntry::selectPreviousCompletion() { if (isShowingCompletionPopup()) m_completionBox->up(); } QString CommandEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) { Q_UNUSED(commentStartingSeq); Q_UNUSED(commentEndingSeq); if (command().isEmpty()) return QString(); return command() + commandSep; } QDomElement CommandEntry::toXml(QDomDocument& doc, KZip* archive) { if (expression()) { if ( archive ) expression()->saveAdditionalData( archive ); return expression()->toXml(doc); } QDomElement expr=doc.createElement( "Expression" ); QDomElement cmd=doc.createElement( "Command" ); QDomText cmdText=doc.createTextNode( command() ); cmd.appendChild( cmdText ); expr.appendChild( cmd ); return expr; } QString CommandEntry::currentLine() { if (!m_commandItem->hasFocus()) return QString(); QTextBlock block = m_commandItem->textCursor().block(); return block.text(); } bool CommandEntry::evaluate(int evalOp) { bool success = false; if (!(evalOp & FocusedItemOnly) || m_commandItem->hasFocus()) { success = evaluateCommand(evalOp); } else if (informationItemHasFocus()) { addInformation(); success = true; } if (evalOp & EvaluateNextEntries || Settings::self()->autoEval()) m_evaluationFlag = EvaluateNextEntries; else m_evaluationFlag = 0; return success; } bool CommandEntry::evaluateCommand(int evalOp) { removeContextHelp(); QToolTip::hideText(); QString cmd = command(); kDebug()<<"evaluating: "<autoEval()) m_evaluationFlag = EvaluateNextEntries; else m_evaluationFlag = 0; if(cmd.isEmpty()) { removeResult(); foreach(WorksheetTextItem* item, m_informationItems) { item->deleteLater(); } m_informationItems.clear(); recalculateSize(); evaluateNext(m_evaluationFlag); return false; } Cantor::Expression* expr; expr = worksheet()->session()->evaluateExpression(cmd); connect(expr, SIGNAL(gotResult()), worksheet(), SLOT(gotResult())); setExpression(expr); return true; } void CommandEntry::interruptEvaluation() { Cantor::Expression *expr = expression(); if(expr) expr->interrupt(); } void CommandEntry::updateEntry() { kDebug() << "update Entry"; Cantor::Expression *expr = expression(); if (expr == 0 || expr->result() == 0) return; if (expr->result()->type() == Cantor::HelpResult::Type) return; // Help is handled elsewhere if (!m_resultItem) { m_resultItem = ResultItem::create(this, expr->result()); kDebug() << "new result"; animateSizeChange(); } else { m_resultItem = m_resultItem->updateFromResult(expr->result()); kDebug() << "update result"; animateSizeChange(); } } void CommandEntry::expressionChangedStatus(Cantor::Expression::Status status) { QString text; switch (status) { case Cantor::Expression::Error: text = m_expression->errorMessage(); break; case Cantor::Expression::Interrupted: text = i18n("Interrupted"); break; case Cantor::Expression::Done: evaluateNext(m_evaluationFlag); return; default: return; } m_commandItem->setFocusAt(WorksheetTextItem::BottomRight, 0); if(!m_errorItem) { m_errorItem = new WorksheetTextItem(this, Qt::TextSelectableByMouse); } m_errorItem->setHtml(text); recalculateSize(); } bool CommandEntry::isEmpty() { if (m_commandItem->toPlainText().trimmed().isEmpty()) { if (m_resultItem) return false; return true; } return false; } bool CommandEntry::focusEntry(int pos, qreal xCoord) { if (aboutToBeRemoved()) return false; WorksheetTextItem* item; if (pos == WorksheetTextItem::TopLeft || pos == WorksheetTextItem::TopCoord) item = m_commandItem; else if (m_informationItems.size() && currentInformationItem()->isEditable()) item = currentInformationItem(); else item = m_commandItem; item->setFocusAt(pos, xCoord); return true; } void CommandEntry::setCompletion(Cantor::CompletionObject* tc) { if (m_completionObject) removeContextHelp(); m_completionObject = tc; connect(tc, SIGNAL(done()), this, SLOT(showCompletions())); connect(tc, SIGNAL(lineDone(QString, int)), this, SLOT(completeLineTo(QString, int))); } void CommandEntry::showCompletions() { disconnect(m_completionObject, SIGNAL(done()), this, SLOT(showCompletions())); QString completion = m_completionObject->completion(); kDebug()<<"completion: "<allMatches(); if(m_completionObject->hasMultipleMatches()) { completeCommandTo(completion); QToolTip::showText(QPoint(), QString(), worksheetView()); if (m_completionBox) m_completionBox->deleteLater(); m_completionBox = new KCompletionBox(worksheetView()); m_completionBox->setItems(m_completionObject->allMatches()); QList items = m_completionBox->findItems(m_completionObject->command(), Qt::MatchFixedString|Qt::MatchCaseSensitive); if (!items.empty()) m_completionBox->setCurrentItem(items.first()); m_completionBox->setTabHandling(false); m_completionBox->setActivateOnSelect(true); connect(m_completionBox, SIGNAL(activated(const QString&)), this, SLOT(applySelectedCompletion())); connect(m_commandItem->document(), SIGNAL(contentsChanged()), this, SLOT(completedLineChanged())); connect(m_completionObject, SIGNAL(done()), this, SLOT(updateCompletions())); m_commandItem->activateCompletion(true); QPointF cursorPos = m_commandItem->cursorPosition(); m_completionBox->popup(); m_completionBox->move(toGlobalPosition(cursorPos)); } else { completeCommandTo(completion, FinalCompletion); } } bool CommandEntry::isShowingCompletionPopup() { return m_completionBox && m_completionBox->isVisible(); } void CommandEntry::applySelectedCompletion() { QListWidgetItem* item = m_completionBox->currentItem(); if(item) completeCommandTo(item->text(), FinalCompletion); m_completionBox->hide(); } void CommandEntry::completedLineChanged() { if (!isShowingCompletionPopup()) { // the completion popup is not visible anymore, so let's clean up removeContextHelp(); return; } const QString line = currentLine(); m_completionObject->updateLine(line, m_commandItem->textCursor().positionInBlock()); } void CommandEntry::updateCompletions() { if (!m_completionObject) return; QString completion = m_completionObject->completion(); kDebug()<<"completion: "<allMatches(); if(m_completionObject->hasMultipleMatches() || !completion.isEmpty()) { QToolTip::showText(QPoint(), QString(), worksheetView()); m_completionBox->setItems(m_completionObject->allMatches()); QList items = m_completionBox->findItems(m_completionObject->command(), Qt::MatchFixedString|Qt::MatchCaseSensitive); if (!items.empty()) m_completionBox->setCurrentItem(items.first()); QPointF cursorPos = m_commandItem->cursorPosition(); m_completionBox->move(toGlobalPosition(cursorPos)); } else { removeContextHelp(); } } void CommandEntry::completeCommandTo(const QString& completion, CompletionMode mode) { kDebug() << "completion: " << completion; if (mode == FinalCompletion) { Cantor::SyntaxHelpObject* obj = worksheet()->session()->syntaxHelpFor(completion); if(obj) setSyntaxHelp(obj); } else { if(m_syntaxHelpObject) m_syntaxHelpObject->deleteLater(); m_syntaxHelpObject=0; } Cantor::CompletionObject::LineCompletionMode cmode; if (mode == PreliminaryCompletion) cmode = Cantor::CompletionObject::PreliminaryCompletion; else cmode = Cantor::CompletionObject::FinalCompletion; m_completionObject->completeLine(completion, cmode); } void CommandEntry::completeLineTo(const QString& line, int index) { kDebug() << "line completion: " << line; QTextCursor cursor = m_commandItem->textCursor(); cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); int startPosition = cursor.position(); cursor.insertText(line); cursor.setPosition(startPosition + index); m_commandItem->setTextCursor(cursor); if (m_syntaxHelpObject) { m_syntaxHelpObject->fetchSyntaxHelp(); // If we are about to show syntax help, then this was the final // completion, and we should clean up. removeContextHelp(); } } void CommandEntry::setSyntaxHelp(Cantor::SyntaxHelpObject* sh) { if(m_syntaxHelpObject) m_syntaxHelpObject->deleteLater(); m_syntaxHelpObject=sh; connect(sh, SIGNAL(done()), this, SLOT(showSyntaxHelp())); } void CommandEntry::showSyntaxHelp() { const QString& msg = m_syntaxHelpObject->toHtml(); const QPointF cursorPos = m_commandItem->cursorPosition(); QToolTip::showText(toGlobalPosition(cursorPos), msg, worksheetView()); } void CommandEntry::resultDeleted() { kDebug()<<"result got removed..."; } void CommandEntry::addInformation() { WorksheetTextItem *answerItem = currentInformationItem(); answerItem->setTextInteractionFlags(Qt::TextSelectableByMouse); QString inf = answerItem->toPlainText(); inf.replace(QChar::ParagraphSeparator, '\n'); inf.replace(QChar::LineSeparator, '\n'); kDebug()<<"adding information: "<addInformation(inf); } void CommandEntry::showAdditionalInformationPrompt(const QString& question) { WorksheetTextItem* questionItem = new WorksheetTextItem(this, Qt::TextSelectableByMouse); WorksheetTextItem* answerItem = new WorksheetTextItem(this, Qt::TextEditorInteraction); questionItem->setPlainText(question); m_informationItems.append(questionItem); m_informationItems.append(answerItem); connect(answerItem, SIGNAL(moveToPrevious(int, qreal)), this, SLOT(moveToPreviousItem(int, qreal))); connect(answerItem, SIGNAL(moveToNext(int, qreal)), this, SLOT(moveToNextItem(int, qreal))); connect(answerItem, SIGNAL(execute()), this, SLOT(addInformation())); answerItem->setFocus(); recalculateSize(); } void CommandEntry::removeResult() { if(m_expression) { m_expression->clearResult(); } if (m_resultItem) { QGraphicsObject* obj = m_resultItem->graphicsObject(); m_resultItem = 0; fadeOutItem(obj); } } void CommandEntry::removeContextHelp() { disconnect(m_commandItem->document(), SIGNAL(contentsChanged()), this, SLOT(completedLineChanged())); if(m_completionObject) m_completionObject->deleteLater(); m_commandItem->activateCompletion(false); m_completionObject = 0; if (m_completionBox) m_completionBox->hide(); } void CommandEntry::updatePrompt() { KColorScheme color = KColorScheme( QPalette::Normal, KColorScheme::View); m_promptItem->setPlainText(""); QTextCursor c = m_promptItem->textCursor(); QTextCharFormat cformat = c.charFormat(); cformat.clearForeground(); c.setCharFormat(cformat); cformat.setFontWeight(QFont::Bold); //insert the session id if available if(m_expression && worksheet()->showExpressionIds()) c.insertText(QString::number(m_expression->id()),cformat); //detect the correct color for the prompt, depending on the //Expression state if(m_expression) { if(m_expression ->status() == Cantor::Expression::Computing&&worksheet()->isRunning()) cformat.setForeground(color.foreground(KColorScheme::PositiveText)); else if(m_expression ->status() == Cantor::Expression::Error) cformat.setForeground(color.foreground(KColorScheme::NegativeText)); else if(m_expression ->status() == Cantor::Expression::Interrupted) cformat.setForeground(color.foreground(KColorScheme::NeutralText)); else cformat.setFontWeight(QFont::Normal); } c.insertText(CommandEntry::Prompt,cformat); recalculateSize(); } WorksheetTextItem* CommandEntry::currentInformationItem() { if (m_informationItems.isEmpty()) return 0; return m_informationItems.last(); } WorksheetView* CommandEntry::worksheetView() { return worksheet()->worksheetView(); } bool CommandEntry::informationItemHasFocus() { if (m_informationItems.isEmpty()) return false; return m_informationItems.last()->hasFocus(); } bool CommandEntry::focusWithinThisItem() { return focusItem() != 0; } void CommandEntry::invalidate() { kDebug() << "ToDo: Invalidate here"; } bool CommandEntry::wantToEvaluate() { return !isEmpty(); } QPoint CommandEntry::toGlobalPosition(const QPointF& localPos) { const QPointF scenePos = mapToScene(localPos); const QPoint viewportPos = worksheetView()->mapFromScene(scenePos); return worksheetView()->viewport()->mapToGlobal(viewportPos); } void CommandEntry::layOutForWidth(double w, bool force) { if (w == size().width() && !force) return; m_promptItem->setPos(0,0); double x = 0 + m_promptItem->width() + HorizontalSpacing; double y = 0; m_commandItem->setPos(x,y); m_commandItem->setTextWidth(w-x); y += qMax(m_commandItem->height(), m_promptItem->height()); foreach(WorksheetTextItem* information, m_informationItems) { y += VerticalSpacing; information->setPos(x,y); information->setTextWidth(w-x); y += information->height(); } if (m_errorItem) { y += VerticalSpacing; m_errorItem->setPos(x,y); m_errorItem->setTextWidth(w-x); y += m_errorItem->height(); } if (m_resultItem) { y += VerticalSpacing; y += m_resultItem->setGeometry(x, y, w-x); } y += VerticalMargin; QSizeF s(w, y); if (animationActive()) { updateAnimation(s); } else { setSize(s); } } diff --git a/src/settings.ui b/src/settings.ui index 0c6de8d3..d9403a22 100644 --- a/src/settings.ui +++ b/src/settings.ui @@ -1,132 +1,139 @@ SettingsBase 0 0 398 400 Default Backend: true Completion Style: Popup Inline When enabled, Cantor will automatically evaluate every entry below the current one. Reevaluate Entries automatically Defaults Enable LaTeX Typesetting Enable Syntax Highlighting Enable Completion Enable Line Numbers + + + + Enable Worksheet Animations + + + Qt::Vertical 20 40 KComboBox QComboBox
kcombobox.h
diff --git a/src/worksheet.cpp b/src/worksheet.cpp index 85f1d6e4..2cb6d2fe 100644 --- a/src/worksheet.cpp +++ b/src/worksheet.cpp @@ -1,847 +1,858 @@ /* 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) 2012 Martin Kuettler */ #include #include #include #include #include #include #include #include #include "config-cantor.h" #include "worksheet.h" #include "settings.h" #include "commandentry.h" #include "textentry.h" #include "latexentry.h" #include "imageentry.h" #include "pagebreakentry.h" #include "lib/backend.h" #include "lib/extension.h" #include "lib/result.h" #include "lib/helpresult.h" #include "lib/session.h" #include "lib/defaulthighlighter.h" const double Worksheet::LeftMargin = 4; const double Worksheet::RightMargin = 4; const double Worksheet::TopMargin = 4; Worksheet::Worksheet(Cantor::Backend* backend, QWidget* parent) : QGraphicsScene(parent) { m_session = backend->createSession(); m_highlighter = 0; m_firstEntry = 0; m_lastEntry = 0; m_currentEntry = 0; m_width = 0; m_isPrinting = false; m_loginFlag = true; QTimer::singleShot(0, this, SLOT(loginToSession())); } Worksheet::~Worksheet() { m_session->logout(); } void Worksheet::loginToSession() { if(m_loginFlag==true) { m_session->login(); enableHighlighting(Settings::self()->highlightDefault()); enableCompletion(Settings::self()->completionDefault()); enableExpressionNumbering(Settings::self()->expressionNumberingDefault()); + enableAnimations(Settings::self()->animationDefault()); #ifdef WITH_EPS session()->setTypesettingEnabled(Settings::self()->typesetDefault()); #else session()->setTypesettingEnabled(false); #endif m_loginFlag=false; } } void Worksheet::print(QPrinter* printer) { m_epsRenderer.useHighResolution(true); m_isPrinting = true; QRect pageRect = printer->pageRect(); qreal scale = 1; // todo: find good scale for page size // todo: use epsRenderer()->scale() for printing ? const qreal width = pageRect.width()/scale; const qreal height = pageRect.height()/scale; setViewSize(width, height, scale, true); QPainter painter(printer); painter.scale(scale, scale); painter.setRenderHint(QPainter::Antialiasing); WorksheetEntry* entry = firstEntry(); qreal y = 0; while (entry) { qreal h = 0; do { if (entry->type() == PageBreakEntry::Type) { entry = entry->next(); break; } h += entry->size().height(); entry = entry->next(); } while (entry && h + entry->size().height() <= height); render(&painter, QRectF(0, 0, width, height), QRectF(0, y, width, h)); y += h; if (entry) printer->newPage(); } //render(&painter); painter.end(); m_isPrinting = false; m_epsRenderer.useHighResolution(false); m_epsRenderer.setScale(-1); // force update in next call to setViewSize, worksheetView()->updateSceneSize(); // ... which happens in here } bool Worksheet::isPrinting() { return m_isPrinting; } void Worksheet::setViewSize(qreal w, qreal h, qreal s, bool forceUpdate) { Q_UNUSED(h); m_width = w; if (s != m_epsRenderer.scale() || forceUpdate) { m_epsRenderer.setScale(s); for (WorksheetEntry *entry = firstEntry(); entry; entry = entry->next()) entry->updateEntry(); } updateLayout(); } void Worksheet::updateLayout() { const qreal w = m_width - LeftMargin - RightMargin; qreal y = TopMargin; const qreal x = LeftMargin; for (WorksheetEntry *entry = firstEntry(); entry; entry = entry->next()) y += entry->setGeometry(x, y, w); setSceneRect(QRectF(0, 0, m_width, y)); } void Worksheet::updateEntrySize(WorksheetEntry* entry) { qreal y = entry->y() + entry->size().height(); for (entry = entry->next(); entry; entry = entry->next()) { entry->setY(y); y += entry->size().height(); } setSceneRect(QRectF(0, 0, m_width, y)); } qreal Worksheet::contentsWidth() { return m_width - LeftMargin - RightMargin; } bool Worksheet::isEmpty() { return !m_firstEntry; } WorksheetView* Worksheet::worksheetView() { return qobject_cast(views()[0]); } void Worksheet::setModified() { emit modified(); } WorksheetEntry* Worksheet::currentEntry() { QGraphicsItem* item = focusItem(); while (item && (item->type() < QGraphicsItem::UserType || item->type() >= QGraphicsItem::UserType + 100)) item = item->parentItem(); if (item) { m_currentEntry = qobject_cast(item->toGraphicsObject()); return m_currentEntry; } else { return m_currentEntry; } return 0; } WorksheetEntry* Worksheet::firstEntry() { return m_firstEntry; } WorksheetEntry* Worksheet::lastEntry() { return m_lastEntry; } void Worksheet::setFirstEntry(WorksheetEntry* entry) { m_firstEntry = entry; } void Worksheet::setLastEntry(WorksheetEntry* entry) { m_lastEntry = entry; } WorksheetEntry* Worksheet::entryAt(qreal x, qreal y) { QGraphicsItem* item = itemAt(x, y); while (item && (item->type() < QGraphicsItem::UserType || item->type() >= QGraphicsItem::UserType + 100)) item = item->parentItem(); if (item) return qobject_cast(item->toGraphicsObject()); return 0; } void Worksheet::focusEntry(WorksheetEntry *entry) { if (!entry) return; entry->focusEntry(); //bool rt = entry->acceptRichText(); //setActionsEnabled(rt); //setAcceptRichText(rt); //ensureCursorVisible(); } void Worksheet::evaluate() { kDebug()<<"evaluate worksheet"; firstEntry()->evaluate(WorksheetEntry::EvaluateNextEntries); emit modified(); } void Worksheet::evaluateCurrentEntry() { kDebug() << "evaluation requested..."; WorksheetEntry* entry = currentEntry(); if(!entry) return; entry->evaluate(WorksheetEntry::FocusedItemOnly); } bool Worksheet::completionEnabled() { return m_completionEnabled; } void Worksheet::showCompletion() { WorksheetEntry* current = currentEntry(); current->showCompletion(); } WorksheetEntry* Worksheet::appendEntry(const int type) { WorksheetEntry* entry = WorksheetEntry::create(type, this); if (entry) { kDebug() << "Entry Appended"; addItem(entry); entry->setPrevious(lastEntry()); if (lastEntry()) lastEntry()->setNext(entry); if (!firstEntry()) m_firstEntry = entry; m_lastEntry = entry; updateLayout(); focusEntry(entry); } return entry; } WorksheetEntry* Worksheet::appendCommandEntry() { return appendEntry(CommandEntry::Type); } WorksheetEntry* Worksheet::appendTextEntry() { return appendEntry(TextEntry::Type); } WorksheetEntry* Worksheet::appendPageBreakEntry() { return appendEntry(PageBreakEntry::Type); } WorksheetEntry* Worksheet::appendImageEntry() { return appendEntry(ImageEntry::Type); } WorksheetEntry* Worksheet::appendLatexEntry() { return appendEntry(LatexEntry::Type); } void Worksheet::appendCommandEntry(const QString& text) { WorksheetEntry* entry = lastEntry(); if(!entry->isEmpty()) { entry = appendCommandEntry(); } if (entry) { focusEntry(entry); entry->setContent(text); evaluateCurrentEntry(); } } WorksheetEntry* Worksheet::insertEntry(const int type) { WorksheetEntry *current = currentEntry(); if (!current) return 0; WorksheetEntry *next = current->next(); WorksheetEntry *entry = 0; if (!next || next->type() != type || !next->isEmpty()) { entry = WorksheetEntry::create(type, this); addItem(entry); entry->setPrevious(current); entry->setNext(next); current->setNext(entry); if (next) next->setPrevious(entry); else m_lastEntry = entry; updateLayout(); } focusEntry(entry); return entry; } WorksheetEntry* Worksheet::insertTextEntry() { return insertEntry(TextEntry::Type); } WorksheetEntry* Worksheet::insertCommandEntry() { return insertEntry(CommandEntry::Type); } WorksheetEntry* Worksheet::insertImageEntry() { return insertEntry(ImageEntry::Type); } WorksheetEntry* Worksheet::insertPageBreakEntry() { return insertEntry(PageBreakEntry::Type); } WorksheetEntry* Worksheet::insertLatexEntry() { return insertEntry(LatexEntry::Type); } void Worksheet::insertCommandEntry(const QString& text) { WorksheetEntry* entry = insertCommandEntry(); if(entry&&!text.isNull()) { entry->setContent(text); evaluateCurrentEntry(); } } WorksheetEntry* Worksheet::insertEntryBefore(int type) { WorksheetEntry *current = currentEntry(); if (!current) return 0; WorksheetEntry *prev = current->previous(); WorksheetEntry *entry = 0; if(!prev || prev->type() != type || !prev->isEmpty()) { entry = WorksheetEntry::create(type, this); addItem(entry); entry->setNext(current); entry->setPrevious(prev); current->setPrevious(entry); if (prev) prev->setNext(entry); else m_firstEntry = entry; updateLayout(); } focusEntry(entry); return entry; } WorksheetEntry* Worksheet::insertTextEntryBefore() { return insertEntryBefore(TextEntry::Type); } WorksheetEntry* Worksheet::insertCommandEntryBefore() { return insertEntryBefore(CommandEntry::Type); } WorksheetEntry* Worksheet::insertPageBreakEntryBefore() { return insertEntryBefore(PageBreakEntry::Type); } WorksheetEntry* Worksheet::insertImageEntryBefore() { return insertEntryBefore(ImageEntry::Type); } WorksheetEntry* Worksheet::insertLatexEntryBefore() { return insertEntryBefore(LatexEntry::Type); } void Worksheet::interrupt() { m_session->interrupt(); emit updatePrompt(); } void Worksheet::interruptCurrentEntryEvaluation() { currentEntry()->interruptEvaluation(); } void Worksheet::highlightItem(WorksheetTextItem* item) { if (!m_highlighter) return; QTextDocument *oldDocument = m_highlighter->document(); QList > formats; if (oldDocument) for (QTextBlock b = oldDocument->firstBlock(); b.isValid(); b = b.next()) formats.append(b.layout()->additionalFormats()); // Not every highlighter is a Cantor::DefaultHighligther (e.g. the // highlighter for KAlgebra) Cantor::DefaultHighlighter* hl = qobject_cast(m_highlighter); if (hl) { hl->setTextItem(item); } else { m_highlighter->setDocument(item->document()); } if (oldDocument) for (QTextBlock b = oldDocument->firstBlock(); b.isValid(); b = b.next()) { b.layout()->setAdditionalFormats(formats.first()); formats.pop_front(); } } void Worksheet::enableHighlighting(bool highlight) { if(highlight) { if(m_highlighter) m_highlighter->deleteLater(); m_highlighter=session()->syntaxHighlighter(this); if(!m_highlighter) m_highlighter=new Cantor::DefaultHighlighter(this); // todo: highlight every entry } else { if(m_highlighter) m_highlighter->deleteLater(); m_highlighter=0; } } void Worksheet::enableCompletion(bool enable) { m_completionEnabled=enable; } Cantor::Session* Worksheet::session() { return m_session; } bool Worksheet::isRunning() { return m_session->status()==Cantor::Session::Running; } bool Worksheet::showExpressionIds() { return m_showExpressionIds; } +bool Worksheet::animationsEnabled() +{ + return m_animationsEnabled; +} + +void Worksheet::enableAnimations(bool enable) +{ + m_animationsEnabled = enable; +} + void Worksheet::enableExpressionNumbering(bool enable) { m_showExpressionIds=enable; emit updatePrompt(); } QDomDocument Worksheet::toXML(KZip* archive) { QDomDocument doc( "CantorWorksheet" ); QDomElement root=doc.createElement( "Worksheet" ); root.setAttribute("backend", m_session->backend()->name()); doc.appendChild(root); for( WorksheetEntry* entry = firstEntry(); entry; entry = entry->next()) { QDomElement el = entry->toXml(doc, archive); root.appendChild( el ); } return doc; } void Worksheet::save( const QString& filename ) { kDebug()<<"saving to filename"; KZip zipFile( filename ); if ( !zipFile.open(QIODevice::WriteOnly) ) { KMessageBox::error( worksheetView(), i18n( "Cannot write file %1." , filename ), i18n( "Error - Cantor" )); return; } QByteArray content = toXML(&zipFile).toByteArray(); kDebug()<<"content: "<backend(); if (backend->extensions().contains("ScriptExtension")) { Cantor::ScriptExtension* e=dynamic_cast(backend->extension("ScriptExtension")); cmdSep=e->commandSeparator(); commentStartingSeq = e->commentStartingSequence(); commentEndingSeq = e->commentEndingSequence(); } QTextStream stream(&file); for(WorksheetEntry * entry = firstEntry(); entry; entry = entry->next()) { const QString& str=entry->toPlain(cmdSep, commentStartingSeq, commentEndingSeq); if(!str.isEmpty()) stream << str + '\n'; } file.close(); } void Worksheet::saveLatex(const QString& filename, bool exportImages) { kDebug()<<"exporting to Latex: "<entry("content.xml"); if (!contentEntry->isFile()) { kDebug()<<"error"; } const KArchiveFile* content=static_cast(contentEntry); QByteArray data=content->data(); kDebug()<<"read: "<isEnabled()) { KMessageBox::information(worksheetView(), 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")); } //cleanup the worksheet and all it contains delete m_session; m_session=0; for(WorksheetEntry* entry = firstEntry(); entry; entry = entry->next()) delete entry; clear(); m_firstEntry = 0; m_lastEntry = 0; m_session=b->createSession(); m_loginFlag=true; kDebug()<<"loading entries"; QDomElement expressionChild = root.firstChildElement(); WorksheetEntry* entry; while (!expressionChild.isNull()) { QString tag = expressionChild.tagName(); if (tag == "Expression") { entry = appendCommandEntry(); entry->setContent(expressionChild, file); } else if (tag == "Text") { entry = appendTextEntry(); entry->setContent(expressionChild, file); } else if (tag == "Latex") { entry = appendLatexEntry(); entry->setContent(expressionChild, file); } else if (tag == "PageBreak") { entry = appendPageBreakEntry(); entry->setContent(expressionChild, file); } else if (tag == "Image") { entry = appendImageEntry(); entry->setContent(expressionChild, file); } expressionChild = expressionChild.nextSiblingElement(); } //login to the session, but let Qt process all the events in its pipeline //first. QTimer::singleShot(0, this, SLOT(loginToSession())); //Set the Highlighting, depending on the current state //If the session isn't logged in, use the default enableHighlighting( m_highlighter!=0 || (m_loginFlag && Settings::highlightDefault()) ); emit sessionChanged(); } void Worksheet::gotResult(Cantor::Expression* expr) { if(expr==0) expr=qobject_cast(sender()); if(expr==0) return; //We're only interested in help results, others are handled by the WorksheetEntry if(expr->result()->type()==Cantor::HelpResult::Type) { QString help=expr->result()->toHtml(); //Do some basic LaTeX replacing help.replace(QRegExp("\\\\code\\{([^\\}]*)\\}"), "\\1"); help.replace(QRegExp("\\$([^\\$])\\$"), "\\1"); emit showHelp(help); } } void Worksheet::removeCurrentEntry() { kDebug()<<"removing current entry"; WorksheetEntry* entry=currentEntry(); if(!entry) return; m_currentEntry = 0; entry->startRemoving(); } EpsRenderer* Worksheet::epsRenderer() { return &m_epsRenderer; } KMenu* Worksheet::createContextMenu() { KMenu *menu = new KMenu(worksheetView()); connect(menu, SIGNAL(aboutToHide()), menu, SLOT(deleteLater())); return menu; } void Worksheet::populateMenu(KMenu *menu, const QPointF& pos) { Q_UNUSED(pos); WorksheetEntry* entry = entryAt(pos.x(), pos.y()); m_currentEntry = entry; if (!isRunning()) menu->addAction(KIcon("system-run"), i18n("Evaluate Worksheet"), this, SLOT(evaluate()), 0); else menu->addAction(KIcon("process-stop"), i18n("Interrupt"), this, SLOT(interrupt()), 0); menu->addSeparator(); if (entry) { KMenu* insert = new KMenu(menu); KMenu* insertBefore = new KMenu(menu); insert->addAction(i18n("Command Entry"), this, SLOT(insertCommandEntry())); insert->addAction(i18n("Text Entry"), this, SLOT(insertTextEntry())); insert->addAction(i18n("LaTeX Entry"), this, SLOT(insertLatexEntry())); insert->addAction(i18n("Image"), this, SLOT(insertImageEntry())); insert->addAction(i18n("Page Break"), this, SLOT(insertPageBreakEntry())); insertBefore->addAction(i18n("Command Entry"), this, SLOT(insertCommandEntryBefore())); insertBefore->addAction(i18n("Text Entry"), this, SLOT(insertTextEntryBefore())); insertBefore->addAction(i18n("LaTeX Entry"), this, SLOT(insertLatexEntryBefore())); insertBefore->addAction(i18n("Image"), this, SLOT(insertImageEntryBefore())); insertBefore->addAction(i18n("Page Break"), this, SLOT(insertPageBreakEntryBefore())); insert->setTitle(i18n("Insert")); insertBefore->setTitle(i18n("Insert Before")); menu->addMenu(insert); menu->addMenu(insertBefore); } else { menu->addAction(i18n("Insert Command Entry"), this, SLOT(appendCommandEntry())); menu->addAction(i18n("Insert Text Entry"), this, SLOT(appendTextEntry())); menu->addAction(i18n("Insert LaTeX Entry"), this, SLOT(appendLatexEntry())); menu->addAction(i18n("Insert Image"), this, SLOT(appendImageEntry())); menu->addAction(i18n("Insert Page Break"), this, SLOT(appendPageBreakEntry())); } } void Worksheet::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { // forward the event to the items QGraphicsScene::contextMenuEvent(event); if (!event->isAccepted()) { event->accept(); KMenu *menu = createContextMenu(); populateMenu(menu, event->scenePos()); menu->popup(event->screenPos()); } } void Worksheet::focusOutEvent(QFocusEvent* focusEvent) { currentEntry(); QGraphicsScene::focusOutEvent(focusEvent); } #include "worksheet.moc" diff --git a/src/worksheet.h b/src/worksheet.h index 573fc9f5..ff16cac8 100644 --- a/src/worksheet.h +++ b/src/worksheet.h @@ -1,170 +1,173 @@ /* 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) 2012 Martin Kuettler */ #ifndef WORKSHEET_H #define WORKSHEET_H #include #include #include #include #include #include #include "worksheetview.h" #include "epsrenderer.h" namespace Cantor { class Backend; class Session; class Expression; } class WorksheetEntry; class WorksheetTextItem; class Worksheet : public QGraphicsScene { Q_OBJECT public: Worksheet(Cantor::Backend* backend, QWidget* parent); ~Worksheet(); Cantor::Session* session(); bool isRunning(); bool showExpressionIds(); + bool animationsEnabled(); void print(QPrinter* printer); bool isPrinting(); void setViewSize(qreal w, qreal h, qreal s, bool forceUpdate = false); WorksheetView* worksheetView(); void setModified(); KMenu* createContextMenu(); void populateMenu(KMenu* menu, const QPointF& pos); EpsRenderer* epsRenderer(); qreal contentsWidth(); bool isEmpty(); public slots: WorksheetEntry* appendCommandEntry(); void appendCommandEntry(const QString& text); WorksheetEntry* appendTextEntry(); WorksheetEntry* appendImageEntry(); WorksheetEntry* appendPageBreakEntry(); WorksheetEntry* appendLatexEntry(); WorksheetEntry* insertCommandEntry(); void insertCommandEntry(const QString& text); WorksheetEntry* insertTextEntry(); WorksheetEntry* insertImageEntry(); WorksheetEntry* insertPageBreakEntry(); WorksheetEntry* insertLatexEntry(); WorksheetEntry* insertCommandEntryBefore(); WorksheetEntry* insertTextEntryBefore(); WorksheetEntry* insertImageEntryBefore(); WorksheetEntry* insertPageBreakEntryBefore(); WorksheetEntry* insertLatexEntryBefore(); void updateLayout(); void updateEntrySize(WorksheetEntry* entry); void focusEntry(WorksheetEntry * entry); void evaluate(); void evaluateCurrentEntry(); void interrupt(); void interruptCurrentEntryEvaluation(); bool completionEnabled(); //void showCompletion(); void highlightItem(WorksheetTextItem*); void enableHighlighting(bool highlight); void enableCompletion(bool enable); void enableExpressionNumbering(bool enable); + void enableAnimations(bool enable); QDomDocument toXML(KZip* archive=0); void save(const QString& filename); void savePlain(const QString& filename); void saveLatex(const QString& filename, bool exportImages); void load(const QString& filename); void gotResult(Cantor::Expression* expr=0); void removeCurrentEntry(); void setFirstEntry(WorksheetEntry* entry); void setLastEntry(WorksheetEntry* entry); signals: void modified(); void sessionChanged(); void showHelp(const QString& help); void updatePrompt(); protected: void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); void focusOutEvent(QFocusEvent* focusEvent); private slots: void loginToSession(); void showCompletion(); //void checkEntriesForSanity(); WorksheetEntry* appendEntry(int type); WorksheetEntry* insertEntry(int type); WorksheetEntry* insertEntryBefore(int type); private: WorksheetEntry* currentEntry(); WorksheetEntry* firstEntry(); WorksheetEntry* lastEntry(); WorksheetEntry* entryAt(qreal x, qreal y); WorksheetEntry* entryAt(int row); int entryCount(); private: static const double LeftMargin; static const double RightMargin; static const double TopMargin; Cantor::Session *m_session; QSyntaxHighlighter* m_highlighter; EpsRenderer m_epsRenderer; WorksheetEntry* m_firstEntry; WorksheetEntry* m_lastEntry; WorksheetEntry* m_currentEntry; bool m_completionEnabled; bool m_showExpressionIds; + bool m_animationsEnabled; bool m_loginFlag; bool m_isPrinting; double m_width; }; #endif // WORKSHEET_H diff --git a/src/worksheetentry.cpp b/src/worksheetentry.cpp index 3421cb56..889002f3 100644 --- a/src/worksheetentry.cpp +++ b/src/worksheetentry.cpp @@ -1,465 +1,471 @@ /* 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) 2012 Martin Kuettler */ #include "worksheetentry.h" #include "commandentry.h" #include "textentry.h" #include "latexentry.h" #include "imageentry.h" #include "pagebreakentry.h" #include #include #include #include #include #include struct AnimationData { QAnimationGroup* animation; const char* slot; QGraphicsObject* item; }; const qreal WorksheetEntry::VerticalMargin = 4; WorksheetEntry::WorksheetEntry(Worksheet* worksheet) : QGraphicsObject() { Q_UNUSED(worksheet) m_next = 0; m_prev = 0; m_animation = 0; m_aboutToBeRemoved = false; } WorksheetEntry::~WorksheetEntry() { if (next()) next()->setPrevious(previous()); if (previous()) previous()->setNext(next()); } int WorksheetEntry::type() const { return Type; } WorksheetEntry* WorksheetEntry::create(int t, Worksheet* worksheet) { switch(t) { case TextEntry::Type: return new TextEntry(worksheet); case CommandEntry::Type: return new CommandEntry(worksheet); case ImageEntry::Type: return new ImageEntry(worksheet); case PageBreakEntry::Type: return new PageBreakEntry(worksheet); case LatexEntry::Type: return new LatexEntry(worksheet); default: return 0; } } void WorksheetEntry::showCompletion() { } WorksheetEntry* WorksheetEntry::next() const { return m_next; } WorksheetEntry* WorksheetEntry::previous() const { return m_prev; } void WorksheetEntry::setNext(WorksheetEntry* n) { m_next = n; } void WorksheetEntry::setPrevious(WorksheetEntry* p) { m_prev = p; } QRectF WorksheetEntry::boundingRect() const { return QRectF(QPointF(0,0), m_size); } void WorksheetEntry::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(painter); Q_UNUSED(option); Q_UNUSED(widget); } bool WorksheetEntry::focusEntry(int pos, qreal xCoord) { Q_UNUSED(pos); Q_UNUSED(xCoord); if (flags() & QGraphicsItem::ItemIsFocusable) { setFocus(); return true; } return false; } void WorksheetEntry::moveToPreviousEntry(int pos, qreal x) { WorksheetEntry* entry = previous(); while (entry && !(entry->wantFocus() && entry->focusEntry(pos, x))) entry = entry->previous(); } void WorksheetEntry::moveToNextEntry(int pos, qreal x) { WorksheetEntry* entry = next(); while (entry && !(entry->wantFocus() && entry->focusEntry(pos, x))) entry = entry->next(); } Worksheet* WorksheetEntry::worksheet() { return qobject_cast(scene()); } void WorksheetEntry::keyPressEvent(QKeyEvent* event) { // This event is used in Entries that set the ItemIsFocusable flag switch(event->key()) { case Qt::Key_Left: case Qt::Key_Up: if (event->modifiers() == Qt::NoModifier) moveToPreviousEntry(WorksheetTextItem::BottomRight, 0); break; case Qt::Key_Right: case Qt::Key_Down: if (event->modifiers() == Qt::NoModifier) moveToNextEntry(WorksheetTextItem::TopLeft, 0); break; case Qt::Key_Enter: case Qt::Key_Return: if (event->modifiers() == Qt::ShiftModifier) evaluate(); else if (event->modifiers() == Qt::ControlModifier) worksheet()->insertCommandEntry(); break; case Qt::Key_Delete: if (event->modifiers() == Qt::ShiftModifier) startRemoving(); break; default: event->ignore(); } } void WorksheetEntry::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { KMenu *menu = worksheet()->createContextMenu(); populateMenu(menu, event->pos()); menu->popup(event->screenPos()); } void WorksheetEntry::populateMenu(KMenu *menu, const QPointF& pos) { if (!worksheet()->isRunning() && wantToEvaluate()) menu->addAction(i18n("Evaluate Entry"), this, SLOT(evaluate()), 0); menu->addAction(i18n("Remove Entry"), this, SLOT(startRemoving()), 0); worksheet()->populateMenu(menu, mapToScene(pos)); } void WorksheetEntry::evaluateNext(int opt) { WorksheetEntry* entry = next(); while (entry && !entry->wantFocus()) entry = entry->next(); if (entry) { if (opt & EvaluateNextEntries) { entry->evaluate(EvaluateNextEntries); } else { worksheet()->setModified(); entry->focusEntry(WorksheetTextItem::BottomRight); } } else { if (!isEmpty() || type() != CommandEntry::Type) worksheet()->appendCommandEntry(); else focusEntry(); worksheet()->setModified(); } } qreal WorksheetEntry::setGeometry(qreal x, qreal y, qreal w) { setPos(x, y); layOutForWidth(w); return size().height(); } void WorksheetEntry::recalculateSize() { qreal height = size().height(); layOutForWidth(size().width(), true); if (height != size().height()) worksheet()->updateEntrySize(this); } QPropertyAnimation* WorksheetEntry::sizeChangeAnimation() { QSizeF oldSize = size(); layOutForWidth(size().width(), true); QSizeF newSize = size(); kDebug() << oldSize << newSize; QPropertyAnimation* sizeAn = new QPropertyAnimation(this, "m_size", this); sizeAn->setDuration(200); sizeAn->setStartValue(oldSize); sizeAn->setEndValue(newSize); sizeAn->setEasingCurve(QEasingCurve::InOutQuad); connect(sizeAn, SIGNAL(valueChanged(const QVariant&)), this, SLOT(sizeAnimated())); return sizeAn; } void WorksheetEntry::sizeAnimated() { worksheet()->updateEntrySize(this); } void WorksheetEntry::animateSizeChange() { - if (m_animation) { + if (m_animation || !worksheet()->animationsEnabled()) { layOutForWidth(size().width(), true); return; } QPropertyAnimation* sizeAn = sizeChangeAnimation(); sizeAn->setEasingCurve(QEasingCurve::OutCubic); m_animation = new AnimationData; m_animation->item = 0; m_animation->slot = 0; m_animation->animation = new QParallelAnimationGroup(this); m_animation->animation->addAnimation(sizeAn); connect(m_animation->animation, SIGNAL(finished()), this, SLOT(endAnimation())); m_animation->animation->start(); } void WorksheetEntry::fadeInItem(QGraphicsObject* item, const char* slot) { - if (m_animation) { + if (m_animation || !worksheet()->animationsEnabled()) { layOutForWidth(size().width(), true); return; } QPropertyAnimation* sizeAn = sizeChangeAnimation(); sizeAn->setEasingCurve(QEasingCurve::OutCubic); if (slot) connect(sizeAn, SIGNAL(finished()), item, slot); QPropertyAnimation* opacAn = new QPropertyAnimation(item, "opacity", this); opacAn->setDuration(200); opacAn->setStartValue(0); opacAn->setEndValue(1); opacAn->setEasingCurve(QEasingCurve::OutCubic); m_animation = new AnimationData; m_animation->animation = new QParallelAnimationGroup(this); m_animation->item = item; m_animation->slot = slot; m_animation->animation->addAnimation(sizeAn); m_animation->animation->addAnimation(opacAn); connect(m_animation->animation, SIGNAL(finished()), this, SLOT(endAnimation())); m_animation->animation->start(); } void WorksheetEntry::fadeOutItem(QGraphicsObject* item, const char* slot) { - if (m_animation) { + if (m_animation || !worksheet()->animationsEnabled()) { layOutForWidth(size().width(), true); return; } QPropertyAnimation* sizeAn = sizeChangeAnimation(); if (slot) connect(sizeAn, SIGNAL(finished()), item, slot); QPropertyAnimation* opacAn = new QPropertyAnimation(item, "opacity", this); opacAn->setDuration(200); opacAn->setStartValue(1); opacAn->setEndValue(0); opacAn->setEasingCurve(QEasingCurve::OutCubic); m_animation = new AnimationData; m_animation->animation = new QParallelAnimationGroup(this); m_animation->item = item; m_animation->slot = slot; m_animation->animation->addAnimation(sizeAn); m_animation->animation->addAnimation(opacAn); + connect(m_animation->animation, SIGNAL(finished()), + item, SLOT(deleteLater())); connect(m_animation->animation, SIGNAL(finished()), this, SLOT(endAnimation())); m_animation->animation->start(); } void WorksheetEntry::endAnimation() { if (!m_animation) return; QAnimationGroup* anim = m_animation->animation; if (anim->state() == QAbstractAnimation::Running) { anim->stop(); QPropertyAnimation* sizeAn = qobject_cast(anim->animationAt(0)); setSize(sizeAn->endValue().value()); if (anim->animationCount() == 2) { QPropertyAnimation* opacAn = qobject_cast(anim->animationAt(1)); m_animation->item->setOpacity(opacAn->endValue().value()); } // If the animation was connected to a slot, call it if (m_animation->slot) { const QMetaObject* metaObj = m_animation->item->metaObject(); int slotIndex = metaObj->indexOfSlot(m_animation->slot); QMetaMethod method = metaObj->method(slotIndex); method.invoke(m_animation->item, Qt::DirectConnection); } } m_animation->animation->deleteLater(); delete m_animation; m_animation = 0; } bool WorksheetEntry::animationActive() { return m_animation; } void WorksheetEntry::updateAnimation(const QSizeF& size) { // Update the current animation, so that the new ending will be size if (m_aboutToBeRemoved) // do not modify the remove-animation return; QPropertyAnimation* sizeAn = qobject_cast(m_animation->animation->animationAt(0)); qreal progress = static_cast(sizeAn->currentTime()) / sizeAn->totalDuration(); QEasingCurve curve = sizeAn->easingCurve(); qreal value = curve.valueForProgress(progress); sizeAn->setEndValue(size); QSizeF newStart = 1/(1-value) * (sizeAn->currentValue().value() - value * size); sizeAn->setStartValue(newStart); } bool WorksheetEntry::aboutToBeRemoved() { return m_aboutToBeRemoved; } void WorksheetEntry::startRemoving() { + if (!worksheet()->animationsEnabled()) { + remove(); + return; + } if (m_aboutToBeRemoved) return; if (!next()) { if (previous() && previous()->isEmpty()) { previous()->focusEntry(); } else { WorksheetEntry* next = worksheet()->appendCommandEntry(); setNext(next); next->focusEntry(); } } else { next()->focusEntry(); } if (m_animation) { endAnimation(); } m_aboutToBeRemoved = true; QPropertyAnimation* sizeAn = new QPropertyAnimation(this, "m_size", this); sizeAn->setDuration(300); sizeAn->setEndValue(QSizeF(size().width(), 0)); sizeAn->setEasingCurve(QEasingCurve::InOutQuad); connect(sizeAn, SIGNAL(valueChanged(const QVariant&)), this, SLOT(sizeAnimated())); connect(sizeAn, SIGNAL(finished()), this, SLOT(remove())); QPropertyAnimation* opacAn = new QPropertyAnimation(this, "opacity", this); opacAn->setDuration(300); opacAn->setEndValue(0); opacAn->setEasingCurve(QEasingCurve::OutCubic); m_animation = new AnimationData; m_animation->animation = new QParallelAnimationGroup(this); m_animation->animation->addAnimation(sizeAn); m_animation->animation->addAnimation(opacAn); m_animation->animation->start(); } void WorksheetEntry::remove() { if (previous()) previous()->setNext(next()); else worksheet()->setFirstEntry(next()); if (next()) next()->setPrevious(previous()); else worksheet()->setLastEntry(previous()); worksheet()->updateLayout(); deleteLater(); } void WorksheetEntry::setSize(QSizeF size) { prepareGeometryChange(); m_size = size; } QSizeF WorksheetEntry::size() { return m_size; } bool WorksheetEntry::wantFocus() { return true; } #include "worksheetentry.moc"