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 @@
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
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"