diff --git a/src/cantor_part.cpp b/src/cantor_part.cpp index a7e06219..98e4e4d8 100644 --- a/src/cantor_part.cpp +++ b/src/cantor_part.cpp @@ -1,760 +1,762 @@ /* 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 #include "worksheet.h" #include "worksheetview.h" #include "searchbar.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(); QWidget* widget = new QWidget(parentWidget); QVBoxLayout* layout = new QVBoxLayout(widget); m_worksheet=new Worksheet(b, widget); m_worksheetview=new WorksheetView(m_worksheet, widget); m_worksheetview->setEnabled(false); //disable input until the session has successfully logged in and emits the ready signal connect(m_worksheet, SIGNAL(modified()), this, SLOT(setModified())); connect(m_worksheet, SIGNAL(showHelp(const QString&)), this, SIGNAL(showHelp(const QString&))); connect(m_worksheet, SIGNAL(sessionChanged()), this, SLOT(worksheetSessionChanged())); m_searchBar = 0; layout->addWidget(m_worksheetview); // notify the part that this is our internal widget setWidget(widget); // create our actions m_worksheet->createActions(actionCollection()); KStandardAction::saveAs(this, SLOT(fileSaveAs()), actionCollection()); m_save = KStandardAction::save(this, SLOT(save()), actionCollection()); m_save->setPriority(QAction::LowPriority); 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* find=KStandardAction::find(this, SLOT(showSearchBar()), actionCollection()); find->setPriority(QAction::LowPriority); KAction* replace=KStandardAction::replace(this, SLOT(showExtendedSearchBar()), actionCollection()); replace->setPriority(QAction::LowPriority); m_findNext = KStandardAction::findNext(this, SLOT(findNext()), actionCollection()); m_findNext->setEnabled(false); m_findPrev = KStandardAction::findPrev(this, SLOT(findPrev()), actionCollection()); m_findPrev->setEnabled(false); 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())); KAction* print = KStandardAction::print(this, SLOT(print()), actionCollection()); print->setPriority(QAction::LowPriority); 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; } + if (m_searchBar) + delete m_searchBar; } void CantorPart::setReadWrite(bool rw) { // notify your internal widget of the read-write state m_worksheetview->setInteractive(rw); ReadWritePart::setReadWrite(rw); } void CantorPart::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() ); 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()) { 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::showSearchBar() { if (!m_searchBar) { m_searchBar = new SearchBar(widget(), m_worksheet); widget()->layout()->addWidget(m_searchBar); connect(m_searchBar, SIGNAL(destroyed(QObject*)), this, SLOT(searchBarDeleted())); } m_findNext->setEnabled(true); m_findPrev->setEnabled(true); m_searchBar->showStandard(); m_searchBar->setFocus(); } void CantorPart::showExtendedSearchBar() { if (!m_searchBar) { m_searchBar = new SearchBar(widget(), m_worksheet); widget()->layout()->addWidget(m_searchBar); connect(m_searchBar, SIGNAL(destroyed(QObject*)), this, SLOT(searchBarDeleted())); } m_findNext->setEnabled(true); m_findPrev->setEnabled(true); m_searchBar->showExtended(); m_searchBar->setFocus(); } void CantorPart::findNext() { if (m_searchBar) m_searchBar->next(); } void CantorPart::findPrev() { if (m_searchBar) m_searchBar->prev(); } void CantorPart::searchBarDeleted() { m_searchBar = 0; m_findNext->setEnabled(false); m_findPrev->setEnabled(false); } 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/searchbar.cpp b/src/searchbar.cpp index 11376684..8f3058a1 100644 --- a/src/searchbar.cpp +++ b/src/searchbar.cpp @@ -1,455 +1,494 @@ /* 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 "searchbar.h" #include "worksheet.h" #include "worksheetentry.h" #include "worksheettextitem.h" #include #include #include SearchBar::SearchBar(QWidget* parent, Worksheet* worksheet) : QWidget(parent) { m_worksheet = worksheet; m_stdUi = new Ui::StandardSearchBar(); m_extUi = 0; setupStdUi(); m_qtFlags = 0; setStartCursor(worksheet->worksheetCursor()); setCurrentCursor(m_startCursor); m_atBeginning = false; m_atEnd = false; m_notFound = false; m_searchFlags = WorksheetEntry::SearchAll; } SearchBar::~SearchBar() { if (m_stdUi) delete m_stdUi; else delete m_extUi; } void SearchBar::showStandard() { if (m_stdUi) return; delete m_extUi; m_extUi = 0; foreach(QObject* child, children()) { child->deleteLater(); } delete layout(); m_stdUi = new Ui::StandardSearchBar(); setupStdUi(); } void SearchBar::showExtended() { if (m_extUi) return; delete m_stdUi; m_stdUi = 0; foreach(QObject* child, children()) { delete child; } delete layout(); m_extUi = new Ui::ExtendedSearchBar(); setupExtUi(); } void SearchBar::next() { if (!m_currentCursor.isValid() && !m_currentCursor.entry() && !m_atEnd) return; searchForward(true); } void SearchBar::prev() { - if (!m_currentCursor.isValid() && !m_currentCursor.entry() && + if (!m_currentCursor.isValid() && !m_currentCursor.entry() && !m_atBeginning) return; searchBackward(true); } void SearchBar::searchBackward(bool skipFirstChar) { WorksheetCursor result; WorksheetEntry* entry; worksheet()->setWorksheetCursor(WorksheetCursor()); QTextDocument::FindFlags f = m_qtFlags | QTextDocument::FindBackward; if (m_currentCursor.isValid()) { bool atBeginningOfEntry = false; if (skipFirstChar) { QTextCursor c = m_currentCursor.textCursor(); c.movePosition(QTextCursor::PreviousCharacter); atBeginningOfEntry = (c == m_currentCursor.textCursor()); setCurrentCursor(WorksheetCursor(m_currentCursor.entry(), m_currentCursor.textItem(), c)); } if (!atBeginningOfEntry) result = m_currentCursor.entry()->search(m_pattern, m_searchFlags, f, m_currentCursor); entry = m_currentCursor.entry()->previous(); } else if (m_currentCursor.entry() && m_currentCursor.entry()->previous()) { entry = m_currentCursor.entry()->previous(); } else { entry = worksheet()->lastEntry(); } setCurrentCursor(WorksheetCursor()); while (!result.isValid() && entry) { result = entry->search(m_pattern, m_searchFlags, f); entry = entry->previous(); } if (result.isValid()) { m_atBeginning = false; QTextCursor c = result.textCursor(); if (result.textCursor().hasSelection()) c.setPosition(result.textCursor().selectionStart()); setCurrentCursor(WorksheetCursor(result.entry(), result.textItem(), c)); worksheet()->makeVisible(m_currentCursor); clearStatus(); worksheet()->setWorksheetCursor(result); } else { if (m_atBeginning) { m_notFound = true; setStatus(i18n("Not found")); } else { m_atBeginning = true; setStatus(i18n("Reached beginning")); } worksheet()->setWorksheetCursor(m_startCursor); } } void SearchBar::searchForward(bool skipFirstChar) { WorksheetCursor result; WorksheetEntry* entry; worksheet()->setWorksheetCursor(WorksheetCursor()); if (m_currentCursor.isValid()) { if (skipFirstChar) { QTextCursor c = m_currentCursor.textCursor(); c.movePosition(QTextCursor::NextCharacter); kDebug() << c.position(); setCurrentCursor(WorksheetCursor(m_currentCursor.entry(), m_currentCursor.textItem(), c)); } result = m_currentCursor.entry()->search(m_pattern, m_searchFlags, m_qtFlags, m_currentCursor); entry = m_currentCursor.entry()->next(); } else if (m_currentCursor.entry()) { entry = m_currentCursor.entry(); } else { entry = worksheet()->firstEntry(); } setCurrentCursor(WorksheetCursor()); while (!result.isValid() && entry) { result = entry->search(m_pattern, m_searchFlags, m_qtFlags); entry = entry->next(); } if (result.isValid()) { m_atEnd = false; QTextCursor c = result.textCursor(); if (result.textCursor().hasSelection()) c.setPosition(result.textCursor().selectionStart()); setCurrentCursor(WorksheetCursor(result.entry(), result.textItem(), c)); worksheet()->makeVisible(m_currentCursor); clearStatus(); worksheet()->setWorksheetCursor(result); } else { if (m_atEnd) { m_notFound = true; setStatus(i18n("Not found")); } else { m_atEnd = true; setStatus(i18n("Reached end")); } worksheet()->setWorksheetCursor(m_startCursor); } } void SearchBar::on_close_clicked() { deleteLater(); } void SearchBar::on_openExtended_clicked() { showExtended(); } void SearchBar::on_openStandard_clicked() { showStandard(); } void SearchBar::on_next_clicked() { next(); } void SearchBar::on_previous_clicked() { prev(); } void SearchBar::on_replace_clicked() { if (!m_currentCursor.isValid()) return; QTextCursor cursor = m_currentCursor.textCursor(); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m_pattern.length()); cursor.insertText(m_replacement); next(); } void SearchBar::on_replaceAll_clicked() { int count = 0; WorksheetEntry* entry = worksheet()->firstEntry(); WorksheetCursor cursor; for (; entry; entry = entry->next()) { cursor = entry->search(m_pattern, m_searchFlags, m_qtFlags); while (cursor.isValid()) { cursor.textCursor().insertText(m_replacement); cursor = entry->search(m_pattern, m_searchFlags, m_qtFlags, cursor); ++count; } } setStatus(i18n("Replaced %1 instances").arg(count)); } void SearchBar::on_pattern_textChanged(const QString& p) { worksheet()->setWorksheetCursor(WorksheetCursor()); m_atBeginning = m_atEnd = m_notFound = false; if (!p.startsWith(m_pattern)) setCurrentCursor(m_startCursor); m_pattern = p; - if (!m_pattern.isEmpty()) + if (!m_pattern.isEmpty()) { searchForward(); - else + nextButton()->setEnabled(true); + previousButton()->setEnabled(true); + if (m_extUi) { + m_extUi->replace->setEnabled(true); + m_extUi->replaceAll->setEnabled(true); + } + } else { worksheet()->setWorksheetCursor(m_startCursor); + nextButton()->setEnabled(false); + previousButton()->setEnabled(false); + if (m_extUi) { + m_extUi->replace->setEnabled(false); + m_extUi->replaceAll->setEnabled(false); + } + } } void SearchBar::on_replacement_textChanged(const QString& r) { m_replacement = r; } void SearchBar::on_removeFlag_clicked() { KMenu* menu = new KMenu(this); fillLocationsMenu(menu, m_searchFlags); connect(menu, SIGNAL("aboutToHide()"), menu, SLOT("deleteLater()")); menu->exec(mapToGlobal(m_extUi->removeFlag->geometry().topLeft())); } void SearchBar::on_addFlag_clicked() { KMenu* menu = new KMenu(this); fillLocationsMenu(menu, WorksheetEntry::SearchAll ^ m_searchFlags); connect(menu, SIGNAL("aboutToHide()"), menu, SLOT("deleteLater()")); menu->exec(mapToGlobal(m_extUi->addFlag->geometry().topLeft())); } void SearchBar::invalidateStartCursor() { if (!m_startCursor.isValid()) return; WorksheetEntry* entry = m_startCursor.entry()->next(); - if (!entry) + if (!entry && worksheet()->firstEntry() != m_startCursor.entry()) entry = worksheet()->firstEntry(); - + setStartCursor(WorksheetCursor(entry, 0, QTextCursor())); } void SearchBar::invalidateCurrentCursor() { if (!m_currentCursor.isValid()) return; WorksheetEntry* entry = m_currentCursor.entry()->next(); if (!entry) entry = worksheet()->firstEntry(); setCurrentCursor(WorksheetCursor(entry, 0, QTextCursor())); } void SearchBar::toggleFlag() { if (!sender()) return; int flag = sender()->property("searchFlag").toInt(); m_searchFlags ^= flag; updateSearchLocations(); } void SearchBar::on_matchCase_toggled(bool b) { m_qtFlags &= ~QTextDocument::FindCaseSensitively; if (b) m_qtFlags |= QTextDocument::FindCaseSensitively; searchForward(); } void SearchBar::updateSearchLocations() { static QList names; if (names.empty()) names << i18n("Commands") << i18n("Results") << i18n("Errors") << i18n("Text") << i18n("LaTeX Code"); QString text = ""; int flag = 1; for (int i = 0; flag < WorksheetEntry::SearchAll; flag = (1<<(++i))) { if (m_searchFlags & flag) { if (!text.isEmpty()) text += ", "; text += names.at(i); } } m_extUi->searchFlagsList->setText(text); if (m_searchFlags == 0) { m_extUi->removeFlag->setEnabled(false); m_extUi->addFlag->setEnabled(true); } else if (m_searchFlags == WorksheetEntry::SearchAll) { m_extUi->removeFlag->setEnabled(true); m_extUi->addFlag->setEnabled(false); } else { m_extUi->addFlag->setEnabled(true); m_extUi->removeFlag->setEnabled(true); } } void SearchBar::fillLocationsMenu(KMenu* menu, int flags) { static QList names; if (names.empty()) names << i18n("Commands") << i18n("Results") << i18n("Errors") << i18n("Text") << i18n("LaTeX Code"); int flag = 1; for (int i = 0; flag < WorksheetEntry::SearchAll; flag = (1<<(++i))) { if (flags & flag) { QAction* a = menu->addAction(names.at(i), this, SLOT(toggleFlag())); a->setProperty("searchFlag", flag); } } } void SearchBar::setStartCursor(WorksheetCursor cursor) { if (m_startCursor.entry()) - disconnect(m_startCursor.entry(), SIGNAL(destroyed(QObject*)), this, + disconnect(m_startCursor.entry(), SIGNAL(aboutToBeDeleted()), this, SLOT(invalidateStartCursor())); if (cursor.entry()) - connect(cursor.entry(), SIGNAL(destroyed(QObject*)), this, - SLOT(invalidateStartCursor())); + connect(cursor.entry(), SIGNAL(aboutToBeDeleted()), this, + SLOT(invalidateStartCursor()), Qt::DirectConnection); m_startCursor = cursor; } void SearchBar::setCurrentCursor(WorksheetCursor cursor) { if (m_currentCursor.entry()) - disconnect(m_currentCursor.entry(), SIGNAL(destroyed(QObject*)), this, + disconnect(m_currentCursor.entry(), SIGNAL(aboutToBeDeleted()), this, SLOT(invalidateCurrentCursor())); if (cursor.entry()) - connect(cursor.entry(), SIGNAL(destroyed(QObject*)), this, - SLOT(invalidateCurrentCursor())); + connect(cursor.entry(), SIGNAL(aboutToBeDeleted()), this, + SLOT(invalidateCurrentCursor()), Qt::DirectConnection); m_currentCursor = cursor; } void SearchBar::setStatus(QString message) { KSqueezedTextLabel* status; if (m_stdUi) status = m_stdUi->status; else status = m_extUi->status; status->setText(message); } void SearchBar::clearStatus() { setStatus(""); } void SearchBar::setupStdUi() { if (!m_stdUi) return; m_stdUi->setupUi(this); m_stdUi->close->setIcon(KIcon("dialog-close")); m_stdUi->openExtended->setIcon(KIcon("arrow-up-double")); m_stdUi->pattern->setText(m_pattern); m_stdUi->matchCase->setChecked(m_qtFlags & QTextDocument::FindCaseSensitively); m_stdUi->next->setIcon(KIcon("go-down-search")); m_stdUi->previous->setIcon(KIcon("go-up-search")); + if (m_pattern.isEmpty()) { + m_stdUi->next->setEnabled(false); + m_stdUi->previous->setEnabled(false); + } m_stdUi->close->setShortcut(Qt::Key_Escape); setFocusProxy(m_stdUi->pattern); } void SearchBar::setupExtUi() { if (!m_extUi) return; m_extUi->setupUi(this); m_extUi->close->setIcon(KIcon("dialog-close")); m_extUi->openStandard->setIcon(KIcon("arrow-down-double")); m_extUi->pattern->setText(m_pattern); m_extUi->replacement->setText(m_replacement); m_extUi->matchCase->setChecked(m_qtFlags & QTextDocument::FindCaseSensitively); m_extUi->next->setIcon(KIcon("go-down-search")); m_extUi->previous->setIcon(KIcon("go-up-search")); + if (m_pattern.isEmpty()) { + m_extUi->next->setEnabled(false); + m_extUi->previous->setEnabled(false); + m_extUi->replace->setEnabled(false); + m_extUi->replaceAll->setEnabled(false); + } m_extUi->addFlag->setIcon(KIcon("list-add")); m_extUi->removeFlag->setIcon(KIcon("list-remove")); m_extUi->close->setShortcut(Qt::Key_Escape); setFocusProxy(m_extUi->pattern); updateSearchLocations(); } +QPushButton* SearchBar::previousButton() +{ + if (m_stdUi) + return m_stdUi->previous; + else + return m_extUi->previous; +} + +QPushButton* SearchBar::nextButton() +{ + if (m_stdUi) + return m_stdUi->next; + else + return m_extUi->next; +} + Worksheet* SearchBar::worksheet() { return m_worksheet; } #include "searchbar.moc" diff --git a/src/searchbar.h b/src/searchbar.h index ded5aba7..7e0bf3de 100644 --- a/src/searchbar.h +++ b/src/searchbar.h @@ -1,110 +1,113 @@ /* 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 SEARCHBAR_H #define SEARCHBAR_H #include #include #include #include "ui_standardsearchbar.h" #include "ui_extendedsearchbar.h" #include "worksheetcursor.h" class Worksheet; class WorksheetEntry; class WorksheetTextItem; class KMenu; class SearchBar : public QWidget { Q_OBJECT; public: SearchBar(QWidget* parent, Worksheet* worksheet); ~SearchBar(); void showStandard(); void showExtended(); void next(); void prev(); void searchForward(bool skipFirstChar = false); void searchBackward(bool skipFirstChar = false); public slots: void on_close_clicked(); void on_openExtended_clicked(); void on_openStandard_clicked(); void on_next_clicked(); void on_previous_clicked(); void on_replace_clicked(); void on_replaceAll_clicked(); void on_pattern_textChanged(const QString& p); void on_replacement_textChanged(const QString& r); void on_addFlag_clicked(); void on_removeFlag_clicked(); void on_matchCase_toggled(bool b); void invalidateStartCursor(); void invalidateCurrentCursor(); protected slots: void toggleFlag(); private: void updateSearchLocations(); void fillLocationsMenu(KMenu* menu, int flags); void setupStdUi(); void setupExtUi(); void setStatus(QString); void clearStatus(); void setStartCursor(WorksheetCursor cursor); void setCurrentCursor(WorksheetCursor cursor); Worksheet* worksheet(); + QPushButton* nextButton(); + QPushButton* previousButton(); + private: Ui::StandardSearchBar* m_stdUi; Ui::ExtendedSearchBar* m_extUi; WorksheetCursor m_startCursor; WorksheetCursor m_currentCursor; Worksheet* m_worksheet; QString m_pattern; QString m_replacement; QTextDocument::FindFlags m_qtFlags; unsigned int m_searchFlags; bool m_atBeginning; bool m_atEnd; bool m_notFound; }; #endif // SEARCHBAR_H diff --git a/src/worksheet.cpp b/src/worksheet.cpp index 579fb6a1..3085c8b8 100644 --- a/src/worksheet.cpp +++ b/src/worksheet.cpp @@ -1,1380 +1,1408 @@ /* 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 #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_focusItem = 0; m_dragEntry = 0; m_viewWidth = 0; m_protrusion = 0; m_isPrinting = false; m_loginFlag = true; QTimer::singleShot(0, this, SLOT(loginToSession())); } Worksheet::~Worksheet() { + // This is necessary, because a SeachBar might access firstEntry() + // while the scene is deleted. Maybe there is a better solution to + // this problem, but I can't seem to find it. + m_firstEntry = 0; 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_viewWidth = 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() { bool cursorRectVisible = false; bool atEnd = worksheetView()->isAtEnd(); if (currentTextItem()) { QRectF cursorRect = currentTextItem()->cursorRect(); cursorRectVisible = worksheetView()->isVisible(cursorRect); } const qreal w = m_viewWidth - 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_viewWidth + m_protrusion, y)); if (cursorRectVisible) makeVisible(worksheetCursor()); else if (atEnd) worksheetView()->scrollToEnd(); } void Worksheet::updateEntrySize(WorksheetEntry* entry) { bool cursorRectVisible = false; bool atEnd = worksheetView()->isAtEnd(); if (currentTextItem()) { QRectF cursorRect = currentTextItem()->cursorRect(); cursorRectVisible = worksheetView()->isVisible(cursorRect); } 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_viewWidth + m_protrusion, y)); if (cursorRectVisible) makeVisible(worksheetCursor()); else if (atEnd) worksheetView()->scrollToEnd(); } void Worksheet::addProtrusion(qreal width) { if (m_itemProtrusions.contains(width)) ++m_itemProtrusions[width]; else m_itemProtrusions.insert(width, 1); if (width > m_protrusion) { m_protrusion = width; qreal y = lastEntry()->size().height() + lastEntry()->y(); setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y)); } } void Worksheet::updateProtrusion(qreal oldWidth, qreal newWidth) { removeProtrusion(oldWidth); addProtrusion(newWidth); } void Worksheet::removeProtrusion(qreal width) { if (--m_itemProtrusions[width] == 0) { m_itemProtrusions.remove(width); if (width == m_protrusion) { qreal max = -1; foreach (qreal p, m_itemProtrusions.keys()) { if (p > max) max = p; } m_protrusion = max; qreal y = lastEntry()->size().height() + lastEntry()->y(); setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y)); } } } bool Worksheet::isEmpty() { return !m_firstEntry; } void Worksheet::makeVisible(WorksheetEntry* entry) { QRectF r = entry->boundingRect(); r = entry->mapRectToScene(r); r.adjust(0, -10, 0, 10); worksheetView()->makeVisible(r); } void Worksheet::makeVisible(const WorksheetCursor& cursor) { if (cursor.textCursor().isNull()) { makeVisible(cursor.entry()); return; } QRectF r = cursor.textItem()->cursorRect(cursor.textCursor()); QRectF er = cursor.entry()->boundingRect(); er = cursor.entry()->mapRectToScene(er); er.adjust(0, -10, 0, 10); r.adjust(0, qMax(-100.0, er.top() - r.top()), 0, qMin(100.0, er.bottom() - r.bottom())); worksheetView()->makeVisible(r); } WorksheetView* Worksheet::worksheetView() { return qobject_cast(views()[0]); } void Worksheet::setModified() { emit modified(); } WorksheetCursor Worksheet::worksheetCursor() { WorksheetEntry* entry = currentEntry(); WorksheetTextItem* item = currentTextItem(); if (!entry || !item) return WorksheetCursor(); return WorksheetCursor(entry, item, item->textCursor()); } void Worksheet::setWorksheetCursor(const WorksheetCursor& cursor) { if (!cursor.isValid()) return; QGraphicsItem* item = m_focusItem; while (item && item->type() != WorksheetTextItem::Type) item = item->parentItem(); WorksheetTextItem* oldItem = qgraphicsitem_cast(item); if (oldItem) oldItem->clearSelection(); m_focusItem = cursor.textItem(); cursor.textItem()->setTextCursor(cursor.textCursor()); } WorksheetEntry* Worksheet::currentEntry() { QGraphicsItem* item = focusItem(); if (!item /*&& !hasFocus()*/) item = m_focusItem; else m_focusItem = item; while (item && (item->type() < QGraphicsItem::UserType || item->type() >= QGraphicsItem::UserType + 100)) item = item->parentItem(); if (item) { WorksheetEntry* entry = qobject_cast(item->toGraphicsObject()); if (entry && entry->aboutToBeRemoved()) { m_focusItem = 0; return 0; } return entry; } return 0; } WorksheetEntry* Worksheet::firstEntry() { return m_firstEntry; } WorksheetEntry* Worksheet::lastEntry() { return m_lastEntry; } void Worksheet::setFirstEntry(WorksheetEntry* entry) { + if (m_firstEntry) + disconnect(m_firstEntry, SIGNAL(aboutToBeDeleted()), + this, SLOT(invalidateFirstEntry())); m_firstEntry = entry; + if (m_firstEntry) + connect(m_firstEntry, SIGNAL(aboutToBeDeleted()), + this, SLOT(invalidateFirstEntry()), Qt::DirectConnection); } void Worksheet::setLastEntry(WorksheetEntry* entry) { + if (m_lastEntry) + disconnect(m_lastEntry, SIGNAL(aboutToBeDeleted()), + this, SLOT(invalidateLastEntry())); m_lastEntry = entry; + if (m_lastEntry) + connect(m_lastEntry, SIGNAL(aboutToBeDeleted()), + this, SLOT(invalidateLastEntry()), Qt::DirectConnection); +} + +void Worksheet::invalidateFirstEntry() +{ + if (m_firstEntry) + setFirstEntry(m_firstEntry->next()); +} + +void Worksheet::invalidateLastEntry() +{ + if (m_lastEntry) + setLastEntry(m_lastEntry->previous()); } 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::startDrag(WorksheetEntry* entry, QDrag* drag) { entry->setOpacity(0); m_dragEntry = entry; drag->exec(); } void Worksheet::evaluate() { kDebug()<<"evaluate worksheet"; firstEntry()->evaluate(WorksheetEntry::EvaluateNext); emit modified(); } void Worksheet::evaluateCurrentEntry() { kDebug() << "evaluation requested..."; WorksheetEntry* entry = currentEntry(); if(!entry) return; entry->evaluateCurrentItem(); } 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; + setFirstEntry(entry); + setLastEntry(entry); updateLayout(); makeVisible(entry); 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 appendEntry(type); 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; + setLastEntry(entry); updateLayout(); } focusEntry(entry); makeVisible(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; + setFirstEntry(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); // highlight every entry WorksheetEntry* entry; for (entry = firstEntry(); entry; entry = entry->next()) { WorksheetTextItem* item = entry->highlightItem(); if (!item) continue; highlightItem(item); m_highlighter->rehighlight(); } entry = currentEntry(); WorksheetTextItem* textitem = entry ? entry->highlightItem() : 0; if (textitem && textitem->hasFocus()) highlightItem(textitem); } else { if(m_highlighter) m_highlighter->deleteLater(); m_highlighter=0; // remove highlighting from entries WorksheetEntry* entry; for (entry = firstEntry(); entry; entry = entry->next()) { WorksheetTextItem* item = entry->highlightItem(); if (!item) continue; for (QTextBlock b = item->document()->firstBlock(); b.isValid(); b = b.next()) b.layout()->clearAdditionalFormats(); } update(); } } 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; + setFirstEntry(0); + setLastEntry(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; // In case we just removed this m_focusItem = 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()); if (entry && !entry->isAncestorOf(m_focusItem)) m_focusItem = itemAt(pos); //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) { QGraphicsItem* item = focusItem(); if (item) m_focusItem = item; QGraphicsScene::focusOutEvent(focusEvent); } void Worksheet::mousePressEvent(QGraphicsSceneMouseEvent* event) { QGraphicsScene::mousePressEvent(event); if (event->button() == Qt::LeftButton && !focusItem() && lastEntry() && event->scenePos().y() > lastEntry()->y() + lastEntry()->size().height()) lastEntry()->focusEntry(WorksheetTextItem::BottomRight); } void Worksheet::createActions(KActionCollection* collection) { // Mostly copied from KRichTextWidget::createActions(KActionCollection*) // It would be great if this wasn't necessary. // Text color KAction* action; /* This is "format-stroke-color" in KRichTextWidget */ action = new KAction(KIcon("format-text-color"), i18nc("@action", "Text &Color..."), collection); action->setIconText(i18nc("@label text color", "Color")); action->setPriority(QAction::LowPriority); m_richTextActionList.append(action); collection->addAction("format_text_foreground_color", action); connect(action, SIGNAL(triggered()), this, SLOT(setTextForegroundColor())); // Text color action = new KAction(KIcon("format-fill-color"), i18nc("@action", "Text &Highlight..."), collection); action->setPriority(QAction::LowPriority); m_richTextActionList.append(action); collection->addAction("format_text_background_color", action); connect(action, SIGNAL(triggered()), this, SLOT(setTextBackgroundColor())); // Font Family m_fontAction = new KFontAction(i18nc("@action", "&Font"), collection); m_richTextActionList.append(m_fontAction); collection->addAction("format_font_family", m_fontAction); connect(m_fontAction, SIGNAL(triggered(QString)), this, SLOT(setFontFamily(QString))); // Font Size m_fontSizeAction = new KFontSizeAction(i18nc("@action", "Font &Size"), collection); m_richTextActionList.append(m_fontSizeAction); collection->addAction("format_font_size", m_fontSizeAction); connect(m_fontSizeAction, SIGNAL(fontSizeChanged(int)), this, SLOT(setFontSize(int))); // Bold m_boldAction = new KToggleAction(KIcon("format-text-bold"), i18nc("@action boldify selected text", "&Bold"), collection); m_boldAction->setPriority(QAction::LowPriority); QFont bold; bold.setBold(true); m_boldAction->setFont(bold); m_richTextActionList.append(m_boldAction); collection->addAction("format_text_bold", m_boldAction); m_boldAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_B)); connect(m_boldAction, SIGNAL(triggered(bool)), this, SLOT(setTextBold(bool))); // Italic m_italicAction = new KToggleAction(KIcon("format-text-italic"), i18nc("@action italicize selected text", "&Italic"), collection); m_italicAction->setPriority(QAction::LowPriority); QFont italic; italic.setItalic(true); m_italicAction->setFont(italic); m_richTextActionList.append(m_italicAction); collection->addAction("format_text_italic", m_italicAction); m_italicAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_I)); connect(m_italicAction, SIGNAL(triggered(bool)), this, SLOT(setTextItalic(bool))); // Underline m_underlineAction = new KToggleAction(KIcon("format-text-underline"), i18nc("@action underline selected text", "&Underline"), collection); m_underlineAction->setPriority(QAction::LowPriority); QFont underline; underline.setUnderline(true); m_underlineAction->setFont(underline); m_richTextActionList.append(m_underlineAction); collection->addAction("format_text_underline", m_underlineAction); m_underlineAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_U)); connect(m_underlineAction, SIGNAL(triggered(bool)), this, SLOT(setTextUnderline(bool))); // Strike m_strikeOutAction = new KToggleAction(KIcon("format-text-strikethrough"), i18nc("@action", "&Strike Out"), collection); m_strikeOutAction->setPriority(QAction::LowPriority); m_richTextActionList.append(m_strikeOutAction); collection->addAction("format_text_strikeout", m_strikeOutAction); m_strikeOutAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_L)); connect(m_strikeOutAction, SIGNAL(triggered(bool)), this, SLOT(setTextStrikeOut(bool))); // Alignment QActionGroup *alignmentGroup = new QActionGroup(this); // Align left m_alignLeftAction = new KToggleAction(KIcon("format-justify-left"), i18nc("@action", "Align &Left"), collection); m_alignLeftAction->setPriority(QAction::LowPriority); m_alignLeftAction->setIconText(i18nc("@label left justify", "Left")); m_richTextActionList.append(m_alignLeftAction); collection->addAction("format_align_left", m_alignLeftAction); connect(m_alignLeftAction, SIGNAL(triggered()), this, SLOT(setAlignLeft())); alignmentGroup->addAction(m_alignLeftAction); // Align center m_alignCenterAction = new KToggleAction(KIcon("format-justify-center"), i18nc("@action", "Align &Center"), collection); m_alignCenterAction->setPriority(QAction::LowPriority); m_alignCenterAction->setIconText(i18nc("@label center justify", "Center")); m_richTextActionList.append(m_alignCenterAction); collection->addAction("format_align_center", m_alignCenterAction); connect(m_alignCenterAction, SIGNAL(triggered()), this, SLOT(setAlignCenter())); alignmentGroup->addAction(m_alignCenterAction); // Align right m_alignRightAction = new KToggleAction(KIcon("format-justify-right"), i18nc("@action", "Align &Right"), collection); m_alignRightAction->setPriority(QAction::LowPriority); m_alignRightAction->setIconText(i18nc("@label right justify", "Right")); m_richTextActionList.append(m_alignRightAction); collection->addAction("format_align_right", m_alignRightAction); connect(m_alignRightAction, SIGNAL(triggered()), this, SLOT(setAlignRight())); alignmentGroup->addAction(m_alignRightAction); // Align justify m_alignJustifyAction = new KToggleAction(KIcon("format-justify-fill"), i18nc("@action", "&Justify"), collection); m_alignJustifyAction->setPriority(QAction::LowPriority); m_alignJustifyAction->setIconText(i18nc("@label justify fill", "Justify")); m_richTextActionList.append(m_alignJustifyAction); collection->addAction("format_align_justify", m_alignJustifyAction); connect(m_alignJustifyAction, SIGNAL(triggered()), this, SLOT(setAlignJustify())); alignmentGroup->addAction(m_alignJustifyAction); /* // List style KSelectAction* selAction; selAction = new KSelectAction(KIcon("format-list-unordered"), i18nc("@title:menu", "List Style"), collection); QStringList listStyles; listStyles << i18nc("@item:inmenu no list style", "None") << i18nc("@item:inmenu disc list style", "Disc") << i18nc("@item:inmenu circle list style", "Circle") << i18nc("@item:inmenu square list style", "Square") << i18nc("@item:inmenu numbered lists", "123") << i18nc("@item:inmenu lowercase abc lists", "abc") << i18nc("@item:inmenu uppercase abc lists", "ABC"); selAction->setItems(listStyles); selAction->setCurrentItem(0); action = selAction; m_richTextActionList.append(action); collection->addAction("format_list_style", action); connect(action, SIGNAL(triggered(int)), this, SLOT(_k_setListStyle(int))); connect(action, SIGNAL(triggered()), this, SLOT(_k_updateMiscActions())); // Indent action = new KAction(KIcon("format-indent-more"), i18nc("@action", "Increase Indent"), collection); action->setPriority(QAction::LowPriority); m_richTextActionList.append(action); collection->addAction("format_list_indent_more", action); connect(action, SIGNAL(triggered()), this, SLOT(indentListMore())); connect(action, SIGNAL(triggered()), this, SLOT(_k_updateMiscActions())); // Dedent action = new KAction(KIcon("format-indent-less"), i18nc("@action", "Decrease Indent"), collection); action->setPriority(QAction::LowPriority); m_richTextActionList.append(action); collection->addAction("format_list_indent_less", action); connect(action, SIGNAL(triggered()), this, SLOT(indentListLess())); connect(action, SIGNAL(triggered()), this, SLOT(_k_updateMiscActions())); */ } void Worksheet::updateFocusedTextItem(WorksheetTextItem* newItem) { QGraphicsItem* item = m_focusItem; while (item && item->type() != WorksheetTextItem::Type) item = item->parentItem(); WorksheetTextItem* oldItem = qgraphicsitem_cast(item); if (oldItem && oldItem != newItem) oldItem->clearSelection(); m_focusItem = newItem; } void Worksheet::setRichTextInformation(const RichTextInfo& info) { m_boldAction->setChecked(info.bold); m_italicAction->setChecked(info.italic); m_underlineAction->setChecked(info.underline); m_strikeOutAction->setChecked(info.strikeOut); m_fontAction->setFont(info.font); if (info.fontSize > 0) m_fontSizeAction->setFontSize(info.fontSize); if (info.align & Qt::AlignLeft) m_alignLeftAction->setChecked(true); else if (info.align & Qt::AlignCenter) m_alignCenterAction->setChecked(true); else if (info.align & Qt::AlignRight) m_alignRightAction->setChecked(true); else if (info.align & Qt::AlignJustify) m_alignJustifyAction->setChecked(true); } void Worksheet::setAcceptRichText(bool b) { foreach(KAction* action, m_richTextActionList) { action->setEnabled(b); } /* foreach(QWidget* widget, m_fontAction->createdWidgets()) { widget->setEnabled(b); } foreach(QWidget* widget, m_fontSizeAction->createdWidgets()) { widget->setEnabled(b); } */ } WorksheetTextItem* Worksheet::currentTextItem() { QGraphicsItem* item = focusItem(); if (!item) item = m_focusItem; while (item && item->type() != WorksheetTextItem::Type) item = item->parentItem(); return qgraphicsitem_cast(item); } void Worksheet::setTextForegroundColor() { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextForegroundColor(); } void Worksheet::setTextBackgroundColor() { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextBackgroundColor(); } void Worksheet::setTextBold(bool b) { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextBold(b); } void Worksheet::setTextItalic(bool b) { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextItalic(b); } void Worksheet::setTextUnderline(bool b) { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextUnderline(b); } void Worksheet::setTextStrikeOut(bool b) { WorksheetTextItem* item = currentTextItem(); if (item) item->setTextStrikeOut(b); } void Worksheet::setAlignLeft() { WorksheetTextItem* item = currentTextItem(); if (item) item->setAlignment(Qt::AlignLeft); } void Worksheet::setAlignRight() { WorksheetTextItem* item = currentTextItem(); if (item) item->setAlignment(Qt::AlignRight); } void Worksheet::setAlignCenter() { WorksheetTextItem* item = currentTextItem(); if (item) item->setAlignment(Qt::AlignCenter); } void Worksheet::setAlignJustify() { WorksheetTextItem* item = currentTextItem(); if (item) item->setAlignment(Qt::AlignJustify); } void Worksheet::setFontFamily(QString font) { WorksheetTextItem* item = currentTextItem(); if (item) item->setFontFamily(font); } void Worksheet::setFontSize(int size) { WorksheetTextItem* item = currentTextItem(); if (item) item->setFontSize(size); } /* void Worksheet::dragEnterEvent(QGraphicsSceneDragDropEvent* event) { } void Worksheet::dragLeaveEvent(QGraphicsSceneDragDropEvent* event) { } void Worksheet::dragMoveEvent(QGraphicsSceneDragDropEvent* event) { } void Worksheet::dropEvent(QGraphicsSceneDragDropEvent* event) { } */ #include "worksheet.moc" diff --git a/src/worksheet.h b/src/worksheet.h index 30e4caa0..a22e3e98 100644 --- a/src/worksheet.h +++ b/src/worksheet.h @@ -1,248 +1,250 @@ /* 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" #include "worksheetcursor.h" namespace Cantor { class Backend; class Session; class Expression; } class WorksheetEntry; class WorksheetTextItem; class QDrag; class KAction; class KActionCollection; class KToggleAction; class KFontAction; class KFontSizeAction; 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 makeVisible(WorksheetEntry* entry); void makeVisible(const WorksheetCursor& cursor); void setModified(); void startDrag(WorksheetEntry* entry, QDrag* drag); void createActions(KActionCollection* collection); KMenu* createContextMenu(); void populateMenu(KMenu* menu, const QPointF& pos); EpsRenderer* epsRenderer(); bool isEmpty(); WorksheetEntry* currentEntry(); WorksheetEntry* firstEntry(); WorksheetEntry* lastEntry(); WorksheetCursor worksheetCursor(); void setWorksheetCursor(const WorksheetCursor&); void addProtrusion(qreal width); void updateProtrusion(qreal oldWidth, qreal newWidth); void removeProtrusion(qreal width); // richtext struct RichTextInfo { bool bold; bool italic; bool underline; bool strikeOut; QString font; qreal fontSize; Qt::Alignment align; }; 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); + void invalidateFirstEntry(); + void invalidateLastEntry(); void updateFocusedTextItem(WorksheetTextItem* item); // richtext void setRichTextInformation(const RichTextInfo&); void setAcceptRichText(bool b); void setTextForegroundColor(); void setTextBackgroundColor(); void setTextBold(bool b); void setTextItalic(bool b); void setTextUnderline(bool b); void setTextStrikeOut(bool b); void setAlignLeft(); void setAlignRight(); void setAlignCenter(); void setAlignJustify(); void setFontFamily(QString font); void setFontSize(int size); signals: void modified(); void sessionChanged(); void showHelp(const QString& help); void updatePrompt(); protected: void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); void focusOutEvent(QFocusEvent* focusEvent); void mousePressEvent(QGraphicsSceneMouseEvent* event); /* void dragEnterEvent(QGraphicsSceneDragDropEvent* event); void dragLeaveEvent(QGraphicsSceneDragDropEvent* event); void dragMoveEvent(QGraphicsSceneDragDropEvent* event); void dropEvent(QGraphicsSceneDragDropEvent* event); */ private slots: void loginToSession(); void showCompletion(); //void checkEntriesForSanity(); WorksheetEntry* appendEntry(int type); WorksheetEntry* insertEntry(int type); WorksheetEntry* insertEntryBefore(int type); private: WorksheetEntry* entryAt(qreal x, qreal y); WorksheetEntry* entryAt(int row); int entryCount(); WorksheetTextItem* currentTextItem(); 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_dragEntry; QGraphicsItem* m_focusItem; double m_viewWidth; double m_protrusion; QMap m_itemProtrusions; QList m_richTextActionList; KToggleAction* m_boldAction; KToggleAction* m_italicAction; KToggleAction* m_underlineAction; KToggleAction* m_strikeOutAction; KFontAction* m_fontAction; KFontSizeAction* m_fontSizeAction; KToggleAction* m_alignLeftAction; KToggleAction* m_alignCenterAction; KToggleAction* m_alignRightAction; KToggleAction* m_alignJustifyAction; bool m_completionEnabled; bool m_showExpressionIds; bool m_animationsEnabled; bool m_loginFlag; bool m_isPrinting; }; #endif // WORKSHEET_H diff --git a/src/worksheetentry.cpp b/src/worksheetentry.cpp index 8ed87fa6..d656731d 100644 --- a/src/worksheetentry.cpp +++ b/src/worksheetentry.cpp @@ -1,567 +1,568 @@ /* 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 "settings.h" #include #include #include #include #include #include struct AnimationData { QAnimationGroup* animation; QPropertyAnimation* sizeAnimation; QPropertyAnimation* opacAnimation; QPropertyAnimation* posAnimation; 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() { + emit aboutToBeDeleted(); if (next()) next()->setPrevious(previous()); if (previous()) previous()->setNext(next()); if (m_animation) { m_animation->animation->deleteLater(); delete m_animation; } } 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()); } WorksheetCursor WorksheetEntry::search(QString pattern, unsigned flags, QTextDocument::FindFlags qt_flags, const WorksheetCursor& pos) { Q_UNUSED(pattern); Q_UNUSED(flags); Q_UNUSED(qt_flags); Q_UNUSED(pos); return WorksheetCursor(); } 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(KIcon("edit-delete"), i18n("Remove Entry"), this, SLOT(startRemoving()), 0); worksheet()->populateMenu(menu, mapToScene(pos)); } bool WorksheetEntry::evaluateCurrentItem() { // A default implementation that works well for most entries, // because they have only one item. return evaluate(); } void WorksheetEntry::evaluateNext(EvaluationOption opt) { WorksheetEntry* entry = next(); while (entry && !entry->wantFocus()) entry = entry->next(); if (entry) { if (opt == EvaluateNext || Settings::self()->autoEval()) { entry->evaluate(EvaluateNext); } else if (opt == FocusNext) { worksheet()->setModified(); entry->focusEntry(WorksheetTextItem::BottomRight); } else { worksheet()->setModified(); } } else if (opt != DoNothing) { 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 s) { QSizeF oldSize; QSizeF newSize; if (s.isValid()) { oldSize = size(); newSize = s; } else { oldSize = size(); layOutForWidth(size().width(), true); 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 (!worksheet()->animationsEnabled()) { recalculateSize(); return; } if (m_animation) { layOutForWidth(size().width(), true); return; } QPropertyAnimation* sizeAn = sizeChangeAnimation(); m_animation = new AnimationData; m_animation->item = 0; m_animation->slot = 0; m_animation->opacAnimation = 0; m_animation->posAnimation = 0; m_animation->sizeAnimation = sizeAn; m_animation->sizeAnimation->setEasingCurve(QEasingCurve::OutCubic); m_animation->animation = new QParallelAnimationGroup(this); m_animation->animation->addAnimation(m_animation->sizeAnimation); connect(m_animation->animation, SIGNAL(finished()), this, SLOT(endAnimation())); m_animation->animation->start(); } void WorksheetEntry::fadeInItem(QGraphicsObject* item, const char* slot) { if (!worksheet()->animationsEnabled()) { recalculateSize(); if (slot) invokeSlotOnObject(slot, item); return; } if (m_animation) { // this calculates the new size and calls updateSizeAnimation layOutForWidth(size().width(), true); if (slot) invokeSlotOnObject(slot, item); return; } QPropertyAnimation* sizeAn = sizeChangeAnimation(); m_animation = new AnimationData; m_animation->sizeAnimation = sizeAn; m_animation->sizeAnimation->setEasingCurve(QEasingCurve::OutCubic); m_animation->opacAnimation = new QPropertyAnimation(item, "opacity", this); m_animation->opacAnimation->setDuration(200); m_animation->opacAnimation->setStartValue(0); m_animation->opacAnimation->setEndValue(1); m_animation->opacAnimation->setEasingCurve(QEasingCurve::OutCubic); m_animation->posAnimation = 0; m_animation->animation = new QParallelAnimationGroup(this); m_animation->item = item; m_animation->slot = slot; m_animation->animation->addAnimation(m_animation->sizeAnimation); m_animation->animation->addAnimation(m_animation->opacAnimation); connect(m_animation->animation, SIGNAL(finished()), this, SLOT(endAnimation())); m_animation->animation->start(); } void WorksheetEntry::fadeOutItem(QGraphicsObject* item, const char* slot) { // Note: The default value for slot is SLOT(deleteLater()), so item // will be deleted after the animation. if (!worksheet()->animationsEnabled()) { recalculateSize(); if (slot) invokeSlotOnObject(slot, item); return; } if (m_animation) { // this calculates the new size and calls updateSizeAnimation layOutForWidth(size().width(), true); if (slot) invokeSlotOnObject(slot, item); return; } QPropertyAnimation* sizeAn = sizeChangeAnimation(); m_animation = new AnimationData; m_animation->sizeAnimation = sizeAn; m_animation->opacAnimation = new QPropertyAnimation(item, "opacity", this); m_animation->opacAnimation->setDuration(200); m_animation->opacAnimation->setStartValue(1); m_animation->opacAnimation->setEndValue(0); m_animation->opacAnimation->setEasingCurve(QEasingCurve::OutCubic); m_animation->posAnimation = 0; m_animation->animation = new QParallelAnimationGroup(this); m_animation->item = item; m_animation->slot = slot; m_animation->animation->addAnimation(m_animation->sizeAnimation); m_animation->animation->addAnimation(m_animation->opacAnimation); 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(); if (m_animation->sizeAnimation) setSize(m_animation->sizeAnimation->endValue().value()); if (m_animation->opacAnimation) { qreal opac = m_animation->opacAnimation->endValue().value(); m_animation->item->setOpacity(opac); } if (m_animation->posAnimation) { const QPointF& pos = m_animation->posAnimation->endValue().value(); m_animation->item->setPos(pos); } // If the animation was connected to a slot, call it if (m_animation->slot) invokeSlotOnObject(m_animation->slot, m_animation->item); } m_animation->animation->deleteLater(); delete m_animation; m_animation = 0; } bool WorksheetEntry::animationActive() { return m_animation; } void WorksheetEntry::updateSizeAnimation(const QSizeF& size) { // Update the current animation, so that the new ending will be size if (!m_animation) return; if (m_aboutToBeRemoved) // do not modify the remove-animation return; if (m_animation->sizeAnimation) { QPropertyAnimation* sizeAn = m_animation->sizeAnimation; 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); } else { m_animation->sizeAnimation = sizeChangeAnimation(size); int d = m_animation->animation->duration() - m_animation->animation->currentTime(); m_animation->sizeAnimation->setDuration(d); m_animation->animation->addAnimation(m_animation->sizeAnimation); } } void WorksheetEntry::invokeSlotOnObject(const char* slot, QObject* obj) { kDebug() << slot << obj; const QMetaObject* metaObj = obj->metaObject(); const QByteArray normSlot = QMetaObject::normalizedSignature(slot); const int slotIndex = metaObj->indexOfSlot(normSlot); if (slotIndex == -1) kDebug() << "Warning: Tried to invoke an invalid slot:" << slot; const QMetaMethod method = metaObj->method(slotIndex); method.invoke(obj, Qt::DirectConnection); } 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()->aboutToBeRemoved()) { previous()->focusEntry(); } else { WorksheetEntry* next = worksheet()->appendCommandEntry(); setNext(next); next->focusEntry(); } } else { next()->focusEntry(); } if (m_animation) { endAnimation(); } m_aboutToBeRemoved = true; m_animation = new AnimationData; m_animation->sizeAnimation = new QPropertyAnimation(this, "m_size", this); m_animation->sizeAnimation->setDuration(300); m_animation->sizeAnimation->setEndValue(QSizeF(size().width(), 0)); m_animation->sizeAnimation->setEasingCurve(QEasingCurve::InOutQuad); connect(m_animation->sizeAnimation, SIGNAL(valueChanged(const QVariant&)), this, SLOT(sizeAnimated())); connect(m_animation->sizeAnimation, SIGNAL(finished()), this, SLOT(remove())); m_animation->opacAnimation = new QPropertyAnimation(this, "opacity", this); m_animation->opacAnimation->setDuration(300); m_animation->opacAnimation->setEndValue(0); m_animation->opacAnimation->setEasingCurve(QEasingCurve::OutCubic); m_animation->posAnimation = 0; m_animation->animation = new QParallelAnimationGroup(this); m_animation->animation->addAnimation(m_animation->sizeAnimation); m_animation->animation->addAnimation(m_animation->opacAnimation); 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; } WorksheetTextItem* WorksheetEntry::highlightItem() { return 0; } bool WorksheetEntry::wantFocus() { return true; } #include "worksheetentry.moc" diff --git a/src/worksheetentry.h b/src/worksheetentry.h index 8d5a9318..ff7137f7 100644 --- a/src/worksheetentry.h +++ b/src/worksheetentry.h @@ -1,151 +1,154 @@ /* 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 WORKSHEETENTRY_H #define WORKSHEETENTRY_H #include #include #include "worksheet.h" #include "worksheettextitem.h" #include "worksheetcursor.h" class TextEntry; class CommandEntry; class ImageEntry; class PageBreakEntry; class LaTeXEntry; class WorksheetTextItem; class QPainter; class QStykeOptionGraphicsItem; class QWidget; class QPropertyAnimation; struct AnimationData; class WorksheetEntry : public QGraphicsObject { Q_OBJECT public: WorksheetEntry(Worksheet* worksheet); virtual ~WorksheetEntry(); enum {Type = UserType}; virtual int type() const; virtual bool isEmpty()=0; static WorksheetEntry* create(int t, Worksheet* worksheet); WorksheetEntry* next() const; WorksheetEntry* previous() const; void setNext(WorksheetEntry*); void setPrevious(WorksheetEntry*); QRectF boundingRect() const; void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); virtual bool acceptRichText() = 0; virtual void setContent(const QString& content)=0; virtual void setContent(const QDomElement& content, const KZip& file)=0; virtual QDomElement toXml(QDomDocument& doc, KZip* archive)=0; virtual QString toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq)=0; virtual void interruptEvaluation()=0; virtual void showCompletion(); virtual bool focusEntry(int pos = WorksheetTextItem::TopLeft, qreal xCoord = 0); virtual qreal setGeometry(qreal x, qreal y, qreal w); virtual void layOutForWidth(qreal w, bool force = false) = 0; QPropertyAnimation* sizeChangeAnimation(QSizeF s = QSizeF()); virtual void populateMenu(KMenu *menu, const QPointF& pos); bool aboutToBeRemoved(); QSizeF size(); enum EvaluationOption { DoNothing, FocusNext, EvaluateNext }; virtual WorksheetTextItem* highlightItem(); enum SearchFlag {SearchCommand=1, SearchResult=2, SearchError=4, SearchText=8, SearchLaTeX=16, SearchAll=31}; virtual WorksheetCursor search(QString pattern, unsigned flags, QTextDocument::FindFlags qt_flags, const WorksheetCursor& pos = WorksheetCursor()); public slots: virtual bool evaluate(EvaluationOption evalOp = FocusNext) = 0; virtual bool evaluateCurrentItem(); virtual void updateEntry() = 0; virtual void sizeAnimated(); virtual void startRemoving(); virtual void remove(); void moveToPreviousEntry(int pos = WorksheetTextItem::BottomRight, qreal x = 0); void moveToNextEntry(int pos = WorksheetTextItem::TopLeft, qreal x = 0); void recalculateSize(); // similiar to recalculateSize, but the size change is animated void animateSizeChange(); // animate the size change and the opacity of item void fadeInItem(QGraphicsObject* item = 0, const char* slot = 0); void fadeOutItem(QGraphicsObject* item = 0, const char* slot = "deleteLater()"); void endAnimation(); + signals: + void aboutToBeDeleted(); + protected: Worksheet* worksheet(); void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); void keyPressEvent(QKeyEvent *event); void evaluateNext(EvaluationOption opt); void setSize(QSizeF size); bool animationActive(); void updateSizeAnimation(const QSizeF& size); void invokeSlotOnObject(const char* slot, QObject* obj); virtual bool wantToEvaluate() = 0; virtual bool wantFocus(); protected: static const qreal VerticalMargin; private: QSizeF m_size; WorksheetEntry* m_prev; WorksheetEntry* m_next; Q_PROPERTY(QSizeF m_size READ size WRITE setSize); AnimationData* m_animation; bool m_aboutToBeRemoved; }; #endif // WORKSHEETENTRY_H