diff --git a/src/cantor_part.cpp b/src/cantor_part.cpp index 4c6a9951..5d0c60d5 100644 --- a/src/cantor_part.cpp +++ b/src/cantor_part.cpp @@ -1,671 +1,672 @@ /* 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* 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))); 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()); } 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) saveAs(file_name); updateCaption(); } 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() { - m_worksheet->appendCommandEntry(); + 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/imageentry.cpp b/src/imageentry.cpp index ce5c91bd..f71c4525 100644 --- a/src/imageentry.cpp +++ b/src/imageentry.cpp @@ -1,253 +1,330 @@ /* 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 "imageentry.h" #include "worksheetimageitem.h" #include #include ImageEntry::ImageEntry(Worksheet* worksheet) : WorksheetEntry(worksheet) { m_imageItem = 0; m_textItem = new WorksheetTextItem(this); m_imageWatcher = new QFileSystemWatcher(this); m_displaySize.width = -1; m_displaySize.height = -1; m_printSize.width = -1; m_printSize.height = -1; - m_displaySize.widthUnit = ImageSize::AutoSize; - m_displaySize.heightUnit = ImageSize::AutoSize; - m_printSize.widthUnit = ImageSize::AutoSize; - m_printSize.heightUnit = ImageSize::AutoSize; + m_displaySize.widthUnit = ImageSize::Auto; + m_displaySize.heightUnit = ImageSize::Auto; + m_printSize.widthUnit = ImageSize::Auto; + m_printSize.heightUnit = ImageSize::Auto; m_useDisplaySizeForPrinting = true; connect(m_imageWatcher, SIGNAL(fileChanged(const QString&)), this, SLOT(updateEntry())); setFlag(QGraphicsItem::ItemIsFocusable); updateEntry(); } ImageEntry::~ImageEntry() { } void ImageEntry::populateMenu(KMenu *menu, const QPointF& pos) { menu->addAction(i18n("Configure Image"), this, SLOT(startConfigDialog())); menu->addSeparator(); WorksheetEntry::populateMenu(menu, pos); } bool ImageEntry::isEmpty() { return false; } int ImageEntry::type() const { return Type; } bool ImageEntry::acceptRichText() { return false; } void ImageEntry::setContent(const QString& content) { Q_UNUSED(content); return; } void ImageEntry::setContent(const QDomElement& content, const KZip& file) { - Q_UNUSED(content); Q_UNUSED(file); - // ... + static QStringList unitNames; + if (unitNames.isEmpty()) + unitNames << "(auto)" << "px" << "%"; + + QDomElement pathElement = content.firstChildElement("Path"); + QDomElement displayElement = content.firstChildElement("Display"); + QDomElement printElement = content.firstChildElement("Print"); + m_imagePath = pathElement.text(); + m_displaySize.width = displayElement.attribute("width").toDouble(); + m_displaySize.height = displayElement.attribute("height").toDouble(); + m_displaySize.widthUnit = unitNames.indexOf(displayElement.attribute("widthUnit")); + m_displaySize.heightUnit = unitNames.indexOf(displayElement.attribute("heightUnit")); + m_useDisplaySizeForPrinting = printElement.attribute("useDisplaySize").toInt(); + m_printSize.width = printElement.attribute("width").toDouble(); + m_printSize.height = printElement.attribute("height").toDouble(); + m_printSize.widthUnit = unitNames.indexOf(printElement.attribute("widthUnit")); + m_printSize.heightUnit = unitNames.indexOf(printElement.attribute("heightUnit")); + updateEntry(); } QDomElement ImageEntry::toXml(QDomDocument& doc, KZip* archive) { - Q_UNUSED(doc); Q_UNUSED(archive); + static QStringList unitNames; + if (unitNames.isEmpty()) + unitNames << "(auto)" << "px" << "%"; + QDomElement image = doc.createElement("Image"); - // ... + QDomElement path = doc.createElement("Path"); + QDomText pathText = doc.createTextNode(m_imagePath); + path.appendChild(pathText); + image.appendChild(path); + QDomElement display = doc.createElement("Display"); + display.setAttribute("width", m_displaySize.width); + display.setAttribute("widthUnit", unitNames[m_displaySize.widthUnit]); + display.setAttribute("height", m_displaySize.height); + display.setAttribute("heightUnit", unitNames[m_displaySize.heightUnit]); + image.appendChild(display); + QDomElement print = doc.createElement("Print"); + print.setAttribute("useDisplaySize", m_useDisplaySizeForPrinting); + print.setAttribute("width", m_printSize.width); + print.setAttribute("widthUnit", unitNames[m_printSize.widthUnit]); + print.setAttribute("height", m_printSize.height); + print.setAttribute("heightUnit", unitNames[m_printSize.heightUnit]); + image.appendChild(print); + + // For the conversion to latex + QDomElement latexSize = doc.createElement("LatexSizeString"); + QString sizeString; + if (m_useDisplaySizeForPrinting) + sizeString = latexSizeString(m_displaySize); + else + sizeString = latexSizeString(m_printSize); + QDomText latexSizeString = doc.createTextNode(sizeString); + latexSize.appendChild(latexSizeString); + image.appendChild(latexSize); + return image; } QString ImageEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) { Q_UNUSED(commandSep); return commentStartingSeq + "image: " + m_imagePath + commentEndingSeq; } +QString ImageEntry::latexSizeString(const ImageSize& imgSize) +{ + // We use the transformation 1 px = 1/72 in ( = 1 pt in Latex) + + QString sizeString=""; + if (imgSize.widthUnit == ImageSize::Auto && + imgSize.heightUnit == ImageSize::Auto) + return QString(""); + + if (imgSize.widthUnit == ImageSize::Percent) { + if (imgSize.heightUnit == ImageSize::Auto || + (imgSize.heightUnit == ImageSize::Percent && + imgSize.width == imgSize.height)) + return "[scale=" + QString::number(imgSize.width / 100) + "]"; + // else? We could set the size based on the actual image size + } else if (imgSize.widthUnit == ImageSize::Auto && + imgSize.heightUnit == ImageSize::Percent) { + return "[scale=" + QString::number(imgSize.height / 100) + "]"; + } + + if (imgSize.heightUnit == ImageSize::Pixel) + sizeString = "height=" + QString::number(imgSize.height) + "pt"; + if (imgSize.widthUnit == ImageSize::Pixel) { + if (!sizeString.isEmpty()) + sizeString += ","; + sizeString += "width=" + QString::number(imgSize.width) + "pt"; + } + return "[" + sizeString + "]"; +} + void ImageEntry::interruptEvaluation() { } bool ImageEntry::evaluate(int evalOp) { evaluateNext(evalOp); return true; } qreal ImageEntry::height() { if (m_imageItem && m_imageItem->isVisible()) return m_imageItem->height(); else return m_textItem->height(); } void ImageEntry::updateEntry() { qreal oldHeight = height(); if (m_imagePath.isEmpty()) { m_textItem->setPlainText(i18n("Right click here to insert image")); m_textItem->setVisible(true); if (m_imageItem) m_imageItem->setVisible(false); } else { if (!m_imageItem) m_imageItem = new WorksheetImageItem(this); if (m_imagePath.toLower().endsWith(".eps")) { m_imageItem->setEps(m_imagePath); } else { QImage img(m_imagePath); m_imageItem->setImage(img); } if (!m_imageItem->imageIsValid()) { const QString msg = i18n("Cannot load image %1").arg(m_imagePath); m_textItem->setPlainText(msg); m_textItem->setVisible(true); m_imageItem->setVisible(false); } else { QSizeF size; if (worksheet()->isPrinting() && ! m_useDisplaySizeForPrinting) size = imageSize(m_printSize); else size = imageSize(m_displaySize); // Hack: Eps images need to be scaled if (m_imagePath.toLower().endsWith(".eps")) size /= worksheet()->epsRenderer()->scale(); m_imageItem->setSize(size); kDebug() << size; m_textItem->setVisible(false); m_imageItem->setVisible(true); } } kDebug() << oldHeight << height(); if (oldHeight != height()) recalculateSize(); } QSizeF ImageEntry::imageSize(const ImageSize& imgSize) { const QSize& srcSize = m_imageItem->imageSize(); qreal w, h; - if (imgSize.heightUnit == ImageSize::PercentSize) + if (imgSize.heightUnit == ImageSize::Percent) h = srcSize.height() * imgSize.height / 100; - else if (imgSize.heightUnit == ImageSize::PixelSize) + else if (imgSize.heightUnit == ImageSize::Pixel) h = imgSize.height; - if (imgSize.widthUnit == ImageSize::PercentSize) + if (imgSize.widthUnit == ImageSize::Percent) w = srcSize.width() * imgSize.width / 100; - else if (imgSize.widthUnit == ImageSize::PixelSize) + else if (imgSize.widthUnit == ImageSize::Pixel) w = imgSize.width; - if (imgSize.widthUnit == ImageSize::AutoSize) { - if (imgSize.heightUnit == ImageSize::AutoSize) + if (imgSize.widthUnit == ImageSize::Auto) { + if (imgSize.heightUnit == ImageSize::Auto) return QSizeF(srcSize.width(), srcSize.height()); else if (h == 0) w = 0; else w = h / srcSize.height() * srcSize.width(); - } else if (imgSize.heightUnit == ImageSize::AutoSize) { + } else if (imgSize.heightUnit == ImageSize::Auto) { if (w == 0) h = 0; else h = w / srcSize.width() * srcSize.height(); } return QSizeF(w,h); } void ImageEntry::startConfigDialog() { ImageSettingsDialog* dialog = new ImageSettingsDialog(worksheet()->worksheetView()); dialog->setData(m_imagePath, m_displaySize, m_printSize, m_useDisplaySizeForPrinting); connect(dialog, SIGNAL(dataChanged(const QString&, const ImageSize&, const ImageSize&, bool)), this, SLOT(setImageData(const QString&, const ImageSize&, const ImageSize&, bool))); dialog->show(); } void ImageEntry::setImageData(const QString& path, const ImageSize& displaySize, const ImageSize& printSize, bool useDisplaySizeForPrinting) { if (path != m_imagePath) { m_imageWatcher->removePath(m_imagePath); m_imageWatcher->addPath(path); m_imagePath = path; } m_displaySize = displaySize; m_printSize = printSize; m_useDisplaySizeForPrinting = useDisplaySizeForPrinting; updateEntry(); } void ImageEntry::layOutForWidth(double w, bool force) { if (size().width() == w && !force) return; if (m_imageItem && m_imageItem->isVisible()) m_imageItem->setPos(w/2 - m_imageItem->width()/2, 0); else m_textItem->setPos(w/2 - m_textItem->width()/2, 0); setSize(QSizeF(w, height() + VerticalMargin)); } bool ImageEntry::wantToEvaluate() { return false; } bool ImageEntry::wantFocus() { return false; } #include "imageentry.moc" diff --git a/src/imageentry.h b/src/imageentry.h index f06ea3ae..11e0b879 100644 --- a/src/imageentry.h +++ b/src/imageentry.h @@ -1,81 +1,83 @@ /* 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 */ #ifndef IMAGEENTRY_H #define IMAGEENTRY_H #include "worksheetentry.h" #include "imagesettingsdialog.h" #include #include class Worksheet; class WorksheetImageItem; class ImageEntry : public WorksheetEntry { Q_OBJECT; public: ImageEntry(Worksheet* worksheet); ~ImageEntry(); enum {Type = UserType + 4}; int type() const; bool isEmpty(); bool acceptRichText(); void setContent(const QString& content); void setContent(const QDomElement& content, const KZip& file); QDomElement toXml(QDomDocument& doc, KZip* archive); QString toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq); QSizeF imageSize(const ImageSize& imgSize); void interruptEvaluation(); void layOutForWidth(qreal w, bool force = false); public slots: bool evaluate(int evalOp = 0); void updateEntry(); void populateMenu(KMenu *menu, const QPointF& pos); void startConfigDialog(); void setImageData(const QString& path, const ImageSize& displaySize, const ImageSize& printSize, bool useDisplaySizeForPrinting); protected: bool wantToEvaluate(); bool wantFocus(); qreal height(); + QString latexSizeString(const ImageSize& imgSize); + private: QString m_imagePath; ImageSize m_displaySize; ImageSize m_printSize; bool m_useDisplaySizeForPrinting; WorksheetImageItem* m_imageItem; WorksheetTextItem* m_textItem; QFileSystemWatcher* m_imageWatcher; }; #endif /* IMAGEENTRY_H */ diff --git a/src/imagesettingsdialog.h b/src/imagesettingsdialog.h index 5063ae4c..73e9fd68 100644 --- a/src/imagesettingsdialog.h +++ b/src/imagesettingsdialog.h @@ -1,64 +1,64 @@ /* 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) 2011 Martin Kuettler */ #ifndef IMAGESETTINGSDIALOG_H #define IMAGESETTINGSDIALOG_H #include #include struct ImageSize { - enum {AutoSize = 0, PixelSize = 1, PercentSize = 2}; + enum {Auto = 0, Pixel = 1, Percent = 2}; double width; double height; int widthUnit; int heightUnit; }; class ImageSettingsDialog : public KDialog { Q_OBJECT public: ImageSettingsDialog(QWidget* parent); ~ImageSettingsDialog(); void setData(const QString& file, const ImageSize& displaySize, const ImageSize& printSize, bool useDisplaySizeForPrinting); signals: void dataChanged(const QString& file, const ImageSize& displaySize, const ImageSize& printSize, bool useDisplaySizeForPrinting); private slots: void sendChangesAndClose(); void sendChanges(); void openDialog(); void updatePreview(); void updateInputWidgets(); void updatePrintingGroup(int b); private: QList m_unitNames; Ui_ImageSettingsBase m_ui; }; #endif //IMAGESETTINGSDIALOG_H diff --git a/src/worksheet.cpp b/src/worksheet.cpp index c0581c1e..85f1d6e4 100644 --- a/src/worksheet.cpp +++ b/src/worksheet.cpp @@ -1,840 +1,847 @@ /* 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()); #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; } 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 eb015e4c..573fc9f5 100644 --- a/src/worksheet.h +++ b/src/worksheet.h @@ -1,169 +1,170 @@ /* 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(); 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); 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_loginFlag; bool m_isPrinting; double m_width; }; #endif // WORKSHEET_H diff --git a/src/worksheetentry.cpp b/src/worksheetentry.cpp index 120b933e..3421cb56 100644 --- a/src/worksheetentry.cpp +++ b/src/worksheetentry.cpp @@ -1,460 +1,465 @@ /* 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) { - if (next()) { + WorksheetEntry* entry = next(); + + while (entry && !entry->wantFocus()) + entry = entry->next(); + + if (entry) { if (opt & EvaluateNextEntries) { - next()->evaluate(EvaluateNextEntries); + entry->evaluate(EvaluateNextEntries); } else { worksheet()->setModified(); - next()->focusEntry(WorksheetTextItem::BottomRight); + 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) { 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) { 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) { 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()), 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 (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"