diff --git a/DESIGN b/DESIGN index 5cc7ade3..2ce9d64d 100644 --- a/DESIGN +++ b/DESIGN @@ -1,153 +1,143 @@ General: Cantor is designed to be very modular. It is split into these important parts: - Main application: The Cantor application itself is stored in the src/ directory. It implements the worksheet and all the Graphical user interface. It uses the Cantor library to access the different backends. Cantor consists of two important parts, a KPart that contains one Worksheet, and a shell, that contains several parts, and sorts them into Tabs. - Cantor library: The library provides all the interfaces for using the different Backends. It should be completely independend of the final graphical representation of the results. (e.g. always keep the possibility of a Cantor KRunner or plasmoid in mind). It resides in src/lib/ - Backends The src/backends directory contains all the different Backends. They all reimplement the interfaces found in the Cantor library, to enable access to a math package. There are lots of different ways to communicate with the Math app. To check how a backend is designed, look at the DESIGN file in the backends directory. For dependencies between these modules, these rules apply: - everything can depend on lib - the main app can depend on any other module - lib doesn't depend on other modules - each backend, and the assistants only depend on lib Coding style: Generally Cantor follows the KDELibs coding style (see http://techbase.kde.org/Policies/Kdelibs_Coding_Style ). Some notes: - use 4 spaces for intendation - use m_ prefix for member variables. no other prefixes - for the library use d-pointers (see http://techbase.kde.org/Policies/Library_Code_Policy#D-Pointers) to allow binary compatibility later - if you add things to the library, remember to add apidocs The Worksheet: -Cantors worksheet is implemented as a KTextEdit, as it allows easy editing of text and images. -It is quite heavily customized, to behave like a Worksheet. The relevant code is in the -Worksheet and WorksheetEntry classes. -Each entry in the worksheet is represented by it's own WorksheetEntry. An entry contains a table -in which all the information is stored. There are several rows/cells, each with its own purpose: - -_______________________ -| prompt | command | ------------------------- -| question | answer | <---- optionally one or more lines for additional questions asked by the backend (see interactive mode) ------------------------ -| result | <---- a row dedicated to the result of the computation ( text, image etc). ------------------------ - -The WorksheetEntry holds an Expression object, and reacts on it's status changes, and arriving results. +Cantor's worksheet is implemented as a QGraphicsScene (the Worksheet class) with a corresponding QGraphicsView (the WorksheetView class). +Each entry in the worksheet is represented by its own WorksheetEntry, which is a subclass of QGraphicsObject. +The entries can have child items to display text (WorksheetTextItem) or images (WorksheetImageItem). The layout of +these child items is defined in WorksheetEntry::layOutForWidth(double width, bool force). +To display the calculation results the special items TextResultItem, ImageResultItem and AnimationResultItem are available. + +The WorksheetEntry holds an Expression object, and reacts on its status changes, and arriving results. Also it is in charge of requesting Completions or SyntaxHelp. -To allow different results than simple images or text in the Worksheet (e.g. eps graphics) there is -a Class called ResultProxy, that translates between the Result class(from the lib) to a format -QTextEdit understands. For example it renders eps files to a pixmap, and lets QTextDocument embed -that image. +The worksheet also stores one instance of the EpsRenderer, which is used whenever eps images need to be displayed, +e.g. in LaTeX results. Note: The HelpResult is not handled by the Entry, but by the Worksheet, and it forwards it's content outside of the part using a signal. So the CantorShell can show it in the side-panel. The library: The library consists of a bunch of interfaces, for the different backends, and some stuff that is useful for math applications, independend of the Design of the ui(not limited to Worksheet-interfaces). Things should not be depending on a specific backend, but it's acceptable to have interfaces only implemented by one backend. (allthough that might change once a better plugin infrastructure is in place) - Basic design of a backend: This is just a brief introduction of the concepts relevant to the Backends. For more information please see the apidocs of the relevant classes. For a simple sample of a backend you can look at the NullBackend. It only echoes the entered command, or shows an image if you type "img", so it akts as a good example on how to do things. Backends are implemented as plugins that are found on runtime, to allow easy installation of additional backends. A backend consists of the following parts: - Backend: This class is used to store some static information of the backend, like its name, some links, or the features it supports. Each backend can supply a widget, for changing the relevant settings. - Session: A session is where a connection to an instance of the math-app is created, e.g. by spawning a maxima process. It implements methods for evaluating expressions, it delivers a syntax highlighter, or CompletionObjects. The most important methods are login(), logout() and evaluateExpression(). see the apidocs for more information. - Expression: The Expression encapsulates one specific command send to the backend, and its result. It gets created by the session, and destroyed by the Worksheet when the entry is deleted or replaced by a new Expression. When the expression is run, it sends the command to the underlying backend to do the computation, and sets the state to Computing. Once the task is completed it should sets the state to Done or Error, depending on the result, to notify the worksheet that it is no longer busy. Once the result arrives, it emits the resultChanged() signal, so that the Worksheet can display it. Remember, the state and the result are independent, e.g. if you have typesetting enabled, the state will be set to done when the computation is finished, and the result will be set to a text-result. Once the typesetting is done the result changes again. To be as flexible as possible regarding different kinds of results (a computation can deliver some text, a plot, a formula, an animation etc.), Cantor uses the concept of Results. For each kind of result there is a subclass, that stores the informations relevant for the kind of result (e.g. the text, the image, an url etc.). Your backend is in charge of creating the right Result Type, and feeding it the information. The ResultProxy is then used to render the result in the Worksheet(@see The Worksheet) - Syntax Highlighting: Syntax highlighting is a very useful feature for entering commands. In Cantor it's implemented using Qt's QSyntaxHighlighter functionality. The Session is in charge for creating a SyntaxHighlighter object, so it can access all the Session relevant information (if you want to). While you can use an ordinary QSyntaxHighlighter object, you may want to use the DefaultHighlighter class. It delivers highlighting for parenthesis, and information on the different Blocks inside a Worksheet, e.g. to check if the part you're highlighting is a command or not. - Completion: To allow Completion of partially entered Commands in a Backend-agnostic way, Cantor uses so called CompletionObjects. To get a completion, Cantor calls the Sessions completionFor method which is then in charge of creating a Backend-Specific CompletionObject. A CompletionObject should fetch the information from the backend, and emit the done() signal when finished, so the process of fetching should (but doesn't have to) be async to make sure the gui doesn't get blocked. - SyntaxHelp: Syntax Help is the little tooltip, you get when pressing Tab, on an already complete command, that shows syntax information (see Maxima or KAlgebra for an implementation). The concept works similar to Completion. Cantor asks the session for a SyntaxHelpObject which then fetches the information asynchroneously, and emits done(). - Extensions: To allow backend-independend functionalities like Assistant dialogs, Cantor uses so called Extensions. These are subclasses of the Cantor::Extension class, that are created by the backend, and that translate between Cantor and the Backends specific Syntax. For example the PlotExtension translates PlotExtension::plot2d("sin(x)","x","-3.14","3.14") to maxima syntax "plot2d(sin(x),[x,-3.14,3.14])" - Assistants Assistants are Dialogs, used for common tasks like entering a matrix, solving equations etc. Assistants are implemented as plugins, found at runtime. Each assistant has a list of Extensions it needs to work (located in the .desktop file), so they only will be shown for backends, capable of all the needed things. The Assistant will be run, and once it is finished, it returns a list of commands, to achieve the task. They will then be added to the Worksheet and executed. The philosophy behind this is not to hide the syntax from the user, so he may learn how things are done. NOTE: currently assistants can only work through extensions, but in the future, also backend-specific assistants will be possible. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index acedf468..9d708c92 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,98 +1,109 @@ ######################################################################### # Subdirectories ######################################################################### add_subdirectory(lib) include_directories( lib ${CMAKE_CURRENT_BINARY_DIR}/lib) if(KDE4_BUILD_TESTS) include_directories( lib/test ) endif(KDE4_BUILD_TESTS) if(LIBSPECTRE_FOUND) include_directories(${LIBSPECTRE_INCLUDE_DIR}) endif(LIBSPECTRE_FOUND) add_subdirectory(backends) add_subdirectory(assistants) add_subdirectory(xslt) add_subdirectory(panelplugins) #build the config object in a separate library, shared between shell and part kde4_add_kcfg_files(config_SRCS settings.kcfgc) kde4_add_library( cantor_config SHARED ${config_SRCS} ) target_link_libraries( cantor_config ${KDE4_KDEUI_LIBS} ${KDE4_KPARTS_LIBS} ${KDE4_KNEWSTUFF3_LIBS} ) install( TARGETS cantor_config ${INSTALL_TARGETS_DEFAULT_ARGS} ) set(cantor_SRCS main.cpp cantor.cpp backendchoosedialog.cpp ) install(FILES cantor.kcfg DESTINATION ${KCFG_INSTALL_DIR}) kde4_add_ui_files(cantor_SRCS settings.ui) kde4_add_ui_files(cantor_SRCS backendchooser.ui) kde4_add_ui_files(cantor_SRCS imagesettings.ui) +kde4_add_ui_files(cantor_SRCS standardsearchbar.ui) +kde4_add_ui_files(cantor_SRCS extendedsearchbar.ui) kde4_add_app_icon(cantor_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/../icons/hi*-app-cantor.png") kde4_add_executable(cantor ${cantor_SRCS}) target_link_libraries(cantor ${KDE4_KDEUI_LIBS} ${KDE4_KPARTS_LIBS} ${KDE4_KNEWSTUFF3_LIBS} cantorlibs cantor_config) ########### install files ############### install(TARGETS cantor ${INSTALL_TARGETS_DEFAULT_ARGS} ) install( PROGRAMS cantor.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install( FILES cantor_shell.rc DESTINATION ${DATA_INSTALL_DIR}/cantor ) install( FILES cantor.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) install( FILES cantor_maxima.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) install( FILES cantor_sage.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) install( FILES cantor_kalgebra.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) if (R_FOUND) install( FILES cantor_r.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) endif (R_FOUND) ######################################################################### # KPART SECTION ######################################################################### #kde_module_LTLIBRARIES = libcantorpart.la set(cantor_PART_SRCS cantor_part.cpp worksheet.cpp + worksheetview.cpp worksheetentry.cpp + worksheettextitem.cpp + worksheetimageitem.cpp commandentry.cpp textentry.cpp pagebreakentry.cpp imageentry.cpp latexentry.cpp + placeholderentry.cpp + worksheetcursor.cpp + searchbar.cpp + actionbar.cpp + worksheettoolbutton.cpp imagesettingsdialog.cpp scripteditorwidget.cpp - resultproxy.cpp + resultitem.cpp + textresultitem.cpp + imageresultitem.cpp + animationresultitem.cpp loadedexpression.cpp - animationhandler.cpp animation.cpp - resultcontextmenu.cpp - formulatextobject.cpp + epsrenderer.cpp ) configure_file (config-cantor.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-cantor.h ) kde4_add_plugin(cantorpart WITH_PREFIX ${cantor_PART_SRCS}) #add_library(blahtexcore STATIC IMPORTED) #set_property(TARGET blahtexcore PROPERTY IMPORTED_LOCATION Path_To_BlahtexCore_Library/libblahtexcore.a) target_link_libraries(cantorpart ${KDE4_KDEUI_LIBS} ${KDE4_KPARTS_LIBS} ${KDE4_KNEWSTUFF3_LIBS} ${KDE4_KTEXTEDITOR_LIBS} ${QT_QTXMLPATTERNS_LIBRARY} cantorlibs cantor_config ) if(LIBSPECTRE_FOUND) target_link_libraries(cantorpart ${LIBSPECTRE_LIBRARY}) endif(LIBSPECTRE_FOUND) install( FILES cantor_part.desktop DESTINATION ${SERVICES_INSTALL_DIR}/cantor ) install( FILES cantor_part.rc DESTINATION ${DATA_INSTALL_DIR}/cantor ) install( FILES cantor_scripteditor.rc DESTINATION ${DATA_INSTALL_DIR}/cantor ) install(TARGETS cantorpart DESTINATION ${PLUGIN_INSTALL_DIR} ) diff --git a/src/actionbar.cpp b/src/actionbar.cpp new file mode 100644 index 00000000..f90c4f80 --- /dev/null +++ b/src/actionbar.cpp @@ -0,0 +1,87 @@ +/* + 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 "actionbar.h" +#include "worksheet.h" +#include "worksheetentry.h" +#include "worksheettoolbutton.h" + +#include + +ActionBar::ActionBar(WorksheetEntry* parent) + : QGraphicsObject(parent) +{ + setPos(parent->size().width(), 0); + m_pos = 0; + m_height = 0; +} + +ActionBar::~ActionBar() +{ +} + +WorksheetToolButton* ActionBar::addButton(const KIcon& icon, QString toolTip, + QObject* receiver, const char* method ) +{ + WorksheetToolButton* button = new WorksheetToolButton(this); + button->setIcon(icon); + button->setIconScale(worksheet()->epsRenderer()->scale()); + button->setToolTip(toolTip); + if (receiver && method) + connect(button, SIGNAL(clicked()), receiver, method); + m_pos -= button->width() + 2; + m_height = (m_height > button->height()) ? m_height : button->height(); + button->setPos(m_pos, 4); + m_buttons.append(button); + return button; +} + +void ActionBar::addSpace() +{ + m_pos -= 8; +} + +void ActionBar::updatePosition(const QSizeF& parentSize) +{ + setPos(parentSize.width(), 0); + const qreal scale = worksheet()->epsRenderer()->scale(); + foreach(WorksheetToolButton* button, m_buttons) { + button->setIconScale(scale); + } +} + +WorksheetEntry* ActionBar::parentEntry() +{ + return qgraphicsitem_cast(parentItem()); +} + +QRectF ActionBar::boundingRect() const +{ + return QRectF(m_pos, 0, -m_pos, m_height); +} + +void ActionBar::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) +{ +} + +Worksheet* ActionBar::worksheet() +{ + return qobject_cast(scene()); +} diff --git a/src/actionbar.h b/src/actionbar.h new file mode 100644 index 00000000..ed047c1e --- /dev/null +++ b/src/actionbar.h @@ -0,0 +1,61 @@ +/* + 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 ACTIONBAR_H +#define ACTIONBAR_H + +#include + +#include + +class Worksheet; +class WorksheetEntry; +class WorksheetToolButton; + +class ActionBar : public QGraphicsObject +{ + Q_OBJECT + public: + ActionBar(WorksheetEntry* parent); + ~ActionBar(); + + WorksheetToolButton* addButton(const KIcon& icon, QString toolTip, + QObject* receiver = 0, + const char* method = 0); + void addSpace(); + + void updatePosition(const QSizeF& parentSize); + + WorksheetEntry* parentEntry(); + + QRectF boundingRect() const; + void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); + + private: + Worksheet* worksheet(); + + private: + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity); + QList m_buttons; + qreal m_pos; + qreal m_height; +}; + +#endif // ACTIONBAR_H diff --git a/src/animationhandler.cpp b/src/animationhandler.cpp deleted file mode 100644 index 7e2e07f9..00000000 --- a/src/animationhandler.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - 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 "animationhandler.h" - -#include -#include -#include -#include "animation.h" - -AnimationHandler::AnimationHandler(QTextDocument *doc) - : QObject(doc) -{ - m_defaultAnimationHandler = doc->documentLayout()->handlerForObject(QTextFormat::ImageObject); -} - -QSizeF AnimationHandler::intrinsicSize(QTextDocument *doc, int posInDoc, const QTextFormat &format) -{ - QTextImageFormat imageFormat = format.toImageFormat(); - QString name = imageFormat.name(); - - const AnimationHelperItem& anim = format.property(AnimationHandler::MovieProperty).value(); - QMovie* movie=anim.movie(); - - if (!movie) - return m_defaultAnimationHandler->intrinsicSize(doc, posInDoc, format); - else - return movie->currentImage().size(); -} - -void AnimationHandler::drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDoc, const QTextFormat &format) -{ - QTextImageFormat imageFormat = format.toImageFormat(); - QString name = imageFormat.name(); - const AnimationHelperItem& anim = format.property(AnimationHandler::MovieProperty).value(); - QMovie* movie=anim.movie(); - - if (!movie) - m_defaultAnimationHandler->drawObject(painter, rect, doc, posInDoc, format); - else - painter->drawPixmap(rect.topLeft(), movie->currentPixmap()); -} - diff --git a/src/animationresultitem.cpp b/src/animationresultitem.cpp new file mode 100644 index 00000000..62e05101 --- /dev/null +++ b/src/animationresultitem.cpp @@ -0,0 +1,126 @@ +/* + 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 "commandentry.h" +#include "animationresultitem.h" +#include "lib/result.h" +#include "lib/animationresult.h" + +#include +#include + +AnimationResultItem::AnimationResultItem(QGraphicsObject* parent) + : WorksheetImageItem(parent), ResultItem(), m_height(0), m_movie(0) +{ + connect(this, SIGNAL(removeResult()), parentEntry(), + SLOT(removeResult())); +} + +AnimationResultItem::~AnimationResultItem() +{ +} + +double AnimationResultItem::setGeometry(double x, double y, double w) +{ + Q_UNUSED(w); + setPos(x,y); + + return m_height; +} + +void AnimationResultItem::populateMenu(KMenu* menu, const QPointF& pos) +{ + addCommonActions(this, menu); + + menu->addSeparator(); + kDebug() << "populate Menu"; + emit menuCreated(menu, mapToParent(pos)); +} + +ResultItem* AnimationResultItem::updateFromResult(Cantor::Result* result) +{ + QMovie* mov; + switch(result->type()) { + case Cantor::AnimationResult::Type: + mov = static_cast(result->data().value()); + setMovie(mov); + return this; + default: + deleteLater(); + return create(parentEntry(), result); + } +} + +QRectF AnimationResultItem::boundingRect() const +{ + return QRectF(0, 0, width(), height()); +} + +void AnimationResultItem::setMovie(QMovie* movie) +{ + if (m_movie) { + m_movie->disconnect(this, SLOT(updateFrame())); + m_movie->disconnect(this, SLOT(updateSize())); + } + m_movie = movie; + m_height = 0; + connect(m_movie, SIGNAL(frameChanged(int)), this, + SLOT(updateFrame())); + connect(m_movie, SIGNAL(resized(const QSize&)), + this, SLOT(updateSize(const QSize&))); +} + +void AnimationResultItem::updateFrame() +{ + setImage(m_movie->currentImage()); +} + +void AnimationResultItem::updateSize(const QSize& size) +{ + if (m_height != size.height()) { + m_height = size.height(); + emit sizeChanged(); + } +} + +void AnimationResultItem::saveResult() +{ + Cantor::Result* res = result(); + const QString& filename=KFileDialog::getSaveFileName(KUrl(), res->mimeType(), worksheet()->worksheetView()); + kDebug()<<"saving result to "<save(filename); +} + +void AnimationResultItem::deleteLater() +{ + WorksheetImageItem::deleteLater(); +} + +CommandEntry* AnimationResultItem::parentEntry() +{ + return qobject_cast(parentObject()); +} + +Cantor::Result* AnimationResultItem::result() +{ + return parentEntry()->expression()->result(); +} + +#include "animationresultitem.moc" diff --git a/src/resultcontextmenu.h b/src/animationresultitem.h similarity index 51% copy from src/resultcontextmenu.h copy to src/animationresultitem.h index 20fa55b1..54636d28 100644 --- a/src/resultcontextmenu.h +++ b/src/animationresultitem.h @@ -1,63 +1,68 @@ /* 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 _RESULTCONTEXTMENU_H -#define _RESULTCONTEXTMENU_H +#ifndef ANIMATIONRESULTITEM_H +#define ANIMATIONRESULTITEM_H -#include +#include "imageresultitem.h" -class CommandEntry; -namespace Cantor{ - class Result; -} +#include -/** - * this is the Menu shown when Right clicking on a Result. - * It offers different options depending on the Type of the - * result. - **/ -class ResultContextMenu : public KMenu +class WorksheetEntry; + +class AnimationResultItem : public WorksheetImageItem, public ResultItem { Q_OBJECT + public: - ResultContextMenu( CommandEntry* entry, QWidget* parent); - ~ResultContextMenu(); + AnimationResultItem(QGraphicsObject* parent); + ~AnimationResultItem(); + + double setGeometry(double x, double y, double w); + void populateMenu(KMenu* menu, const QPointF& pos); - CommandEntry* entry(); + ResultItem* updateFromResult(Cantor::Result* result); + + void deleteLater(); + + QRectF boundingRect() const; + CommandEntry* parentEntry(); Cantor::Result* result(); - public slots: - void saveResult(); + signals: void removeResult(); - void latexToggleShowCode(); - void animationPause(); - void animationRestart(); - private: - void addGeneralActions(); - void addTypeSpecificActions(); + protected slots: + void saveResult(); private: - CommandEntry* m_entry; - Cantor::Result* m_result; + void setMovie(QMovie* movie); + + private slots: + void updateFrame(); + void updateSize(const QSize& size); + private: + double m_height; + QMovie* m_movie; }; -#endif /* _RESULTCONTEXTMENU_H */ +#endif //ANIMATIONRESULTITEM_H + diff --git a/src/backends/R/rexpression.cpp b/src/backends/R/rexpression.cpp index 5f3cbf17..894e9d34 100644 --- a/src/backends/R/rexpression.cpp +++ b/src/backends/R/rexpression.cpp @@ -1,147 +1,149 @@ /* 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 "rexpression.h" #include "textresult.h" #include "imageresult.h" #include "helpresult.h" #include "epsresult.h" #include "rsession.h" #include #include #include #include #include #include RExpression::RExpression( Cantor::Session* session ) : Cantor::Expression(session) { kDebug(); } RExpression::~RExpression() { } void RExpression::evaluate() { kDebug()<<"evaluating "<(session())->queueExpression(this); } void RExpression::interrupt() { kDebug()<<"interrupting command"; if(status()==Cantor::Expression::Computing) session()->interrupt(); setStatus(Cantor::Expression::Interrupted); } void RExpression::finished(int returnCode, const QString& text) { if(returnCode==RExpression::SuccessCode) { setResult(new Cantor::TextResult(Qt::convertFromPlainText(text))); setStatus(Cantor::Expression::Done); }else if (returnCode==RExpression::ErrorCode) { setResult(new Cantor::TextResult(Qt::convertFromPlainText(text))); setStatus(Cantor::Expression::Error); setErrorMessage(Qt::convertFromPlainText(text)); } } void RExpression::evaluationStarted() { setStatus(Cantor::Expression::Computing); } void RExpression::addInformation(const QString& information) { static_cast(session())->sendInputToServer(information); } void RExpression::showFilesAsResult(const QStringList& files) { kDebug()<<"showing files: "<name(); if(type->is("application/postscript")) { kDebug()<<"its PostScript"; setResult(new Cantor::EpsResult(file)); } else if(type->is("text/plain")) { //Htmls are also plain texts, combining this in one if(type->is("text/html")) kDebug()<<"its a HTML document"; else kDebug()<<"its plain text"; QFile f(file); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { setResult(new Cantor::TextResult(i18n("Error opening file %1", file))); setErrorMessage(i18n("Error opening file %1", file)); setStatus(Cantor::Expression::Error); } QString content=QTextStream(&f).readAll(); if (!type->is("text/html")) { //replace appearing backspaces, as they mess the whole output up content.remove(QRegExp(".\b")); //Replace < and > with their html code, so they won't be confused as html tags content.replace( '<' , "<"); content.replace( '>' , ">"); } kDebug()<<"content: "<name().contains("image")) { setResult(new Cantor::ImageResult(file)); + setStatus(Cantor::Expression::Done); } else { setResult(new Cantor::TextResult(i18n("cannot open file %1: Unknown MimeType", file))); setErrorMessage(i18n("cannot open file %1: Unknown MimeType", file)); setStatus(Cantor::Expression::Error); } } } #include "rexpression.moc" diff --git a/src/backends/R/rhighlighter.cpp b/src/backends/R/rhighlighter.cpp index f08028e7..98075e5c 100644 --- a/src/backends/R/rhighlighter.cpp +++ b/src/backends/R/rhighlighter.cpp @@ -1,96 +1,96 @@ /* 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) 2010 Oleksiy Protas */ #include "rhighlighter.h" #include #include const QStringList RHighlighter::keywords_list=QStringList() << "if" << "else" << "switch" << "while" << "for" << "repeat" << "function" << "in" << "next" << "break" << "TRUE" << "FALSE" << "NULL" << "NA" << "NA_integer_" << "NA_real_" << "NA_complex_" << "NA_character_" << "Inf" << "NaN"; const QStringList RHighlighter::operators_list=QStringList() << "(\\+|\\-|\\*|/|<-|->|<=|>=|={1,2}|\\!=|\\|{1,2}|&{1,2}|:{1,3}|\\^|@|\\$|~)((?!(\\+|\\-|\\*|/|<-|->|<=|>=|=|\\!=|\\||&|:|\\^|@|\\$|~))|$)" << "%[^%]*%"; // Taken in Kate highlighter const QStringList RHighlighter::specials_list=QStringList() << "BUG" << "TODO" << "FIXME" << "NB" << "WARNING" << "ERROR"; -RHighlighter::RHighlighter(QTextEdit* edit) : Cantor::DefaultHighlighter(edit) +RHighlighter::RHighlighter(QObject* parent) : Cantor::DefaultHighlighter(parent) { foreach (const QString& s, keywords_list) keywords.append(QRegExp("\\b"+s+"\\b")); foreach (const QString& s, operators_list) operators.append(QRegExp(s)); foreach (const QString& s, specials_list) specials.append(QRegExp("\\b"+s+"\\b")); } RHighlighter::~RHighlighter() { } void RHighlighter::refreshSyntaxRegExps() { emit syntaxRegExps(variables,functions); } // FIXME: due to lack of lookbehinds in QRegExp here we use a flag showing if we need to shift the boundary of formating // to make up for the accidently matched character void RHighlighter::formatRule(const QRegExp &p, const QTextCharFormat &fmt, const QString& text,bool shift) { int index = p.indexIn(text); while (index >= 0) { int length = p.matchedLength(); setFormat(index+(shift?1:0), length-(shift?1:0), fmt); index = p.indexIn(text, index + length); } } void RHighlighter::massFormat(const QVector &p, const QTextCharFormat &fmt, const QString& text,bool shift) { foreach (const QRegExp &rule, p) formatRule(rule,fmt,text,shift); } void RHighlighter::highlightBlock(const QString& text) { if(text.isEmpty()) return; //Do some backend independent highlighting (brackets etc.) DefaultHighlighter::highlightBlock(text); //Let's mark every functionlike call as an error, then paint right ones in their respective format // TODO: find more elegant solution not involving double formatting formatRule(QRegExp("\\b[A-Za-z0-9_]+(?=\\()"),errorFormat(),text); //formatRule(QRegExp("[^A-Za-z_]-?([0-9]+)?(((e|i)?-?)|\\.)[0-9]*L?"),numberFormat(),text,true); // TODO: errorneous number formats, refine massFormat(keywords,keywordFormat(),text); massFormat(operators,operatorFormat(),text); massFormat(specials,commentFormat(),text); // FIXME must be distinc massFormat(functions,functionFormat(),text); massFormat(variables,variableFormat(),text); formatRule(QRegExp("\"[^\"]+\""),stringFormat(),text); // WARNING a bit redundant } diff --git a/src/backends/R/rhighlighter.h b/src/backends/R/rhighlighter.h index d8c2ed70..c059698f 100644 --- a/src/backends/R/rhighlighter.h +++ b/src/backends/R/rhighlighter.h @@ -1,57 +1,57 @@ /* 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) 2010 Oleksiy Protas */ #ifndef _RHIGHLIGHTER_H #define _RHIGHLIGHTER_H #include "defaulthighlighter.h" class RHighlighter : public Cantor::DefaultHighlighter { Q_OBJECT public: - RHighlighter( QTextEdit* edit); + RHighlighter( QObject* parent); ~RHighlighter(); protected: void highlightBlock(const QString &text); public slots: void refreshSyntaxRegExps(); signals: void syntaxRegExps(QVector&,QVector&); private: inline void formatRule(const QRegExp &p, const QTextCharFormat &fmt, const QString& text,bool shift=false); inline void massFormat(const QVector& rules, const QTextCharFormat &fmt, const QString& text,bool shift=false); static const QStringList keywords_list; static const QStringList operators_list; static const QStringList specials_list; QVector keywords; QVector operators; QVector specials; QVector functions; QVector variables; }; #endif /* _RHIGHLIGHTER_H */ diff --git a/src/backends/R/rsession.cpp b/src/backends/R/rsession.cpp index 185844a8..aeba004c 100644 --- a/src/backends/R/rsession.cpp +++ b/src/backends/R/rsession.cpp @@ -1,186 +1,190 @@ /* 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 "rsession.h" #include "rexpression.h" #include "rcompletionobject.h" #include "rhighlighter.h" #include #include #include #include #include RSession::RSession( Cantor::Backend* backend) : Session(backend) { kDebug(); m_rProcess=0; } RSession::~RSession() { kDebug(); m_rProcess->terminate(); } void RSession::login() { kDebug()<<"login"; if(m_rProcess) m_rProcess->deleteLater(); m_rProcess=new KProcess(this); m_rProcess->setOutputChannelMode(KProcess::ForwardedChannels); (*m_rProcess)<start(); m_rServer=new org::kde::Cantor::R(QString("org.kde.cantor_rserver-%1").arg(m_rProcess->pid()), "/R", QDBusConnection::sessionBus(), this); connect(m_rServer, SIGNAL(statusChanged(int)), this, SLOT(serverChangedStatus(int))); connect(m_rServer,SIGNAL(symbolList(const QStringList&,const QStringList&)),this,SLOT(receiveSymbols(const QStringList&,const QStringList&))); changeStatus(Cantor::Session::Done); connect(m_rServer, SIGNAL(ready()), this, SIGNAL(ready())); } void RSession::logout() { kDebug()<<"logout"; m_rProcess->terminate(); } void RSession::interrupt() { - kDebug()<<"interrupt"; - kill(m_rProcess->pid(), 2); + kDebug()<<"interrupt" << m_rProcess->pid(); + if (m_rProcess->pid()) + kill(m_rProcess->pid(), 2); + m_expressionQueue.removeFirst(); changeStatus(Cantor::Session::Done); } Cantor::Expression* RSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave) { kDebug()<<"evaluating: "<setFinishingBehavior(behave); expr->setCommand(cmd); expr->evaluate(); changeStatus(Cantor::Session::Running); return expr; } Cantor::CompletionObject* RSession::completionFor(const QString& command, int index) { RCompletionObject *cmp=new RCompletionObject(command, index, this); connect(m_rServer,SIGNAL(completionFinished(const QString&,const QStringList&)),cmp,SLOT(receiveCompletions(const QString&,const QStringList&))); connect(cmp,SIGNAL(requestCompletion(const QString&)),m_rServer,SLOT(completeCommand(const QString&))); return cmp; } -QSyntaxHighlighter* RSession::syntaxHighlighter(QTextEdit* parent) +QSyntaxHighlighter* RSession::syntaxHighlighter(QObject* parent) { RHighlighter *h=new RHighlighter(parent); connect(h,SIGNAL(syntaxRegExps(QVector&,QVector&)),this,SLOT(fillSyntaxRegExps(QVector&,QVector&))); connect(this,SIGNAL(symbolsChanged()),h,SLOT(refreshSyntaxRegExps())); return h; } void RSession::fillSyntaxRegExps(QVector& v, QVector& f) { // WARNING: current implementation as-in-maxima is a performance hit // think about grouping expressions together or only fetching needed ones v.clear(); f.clear(); foreach (const QString s, m_variables) if (!s.contains(QRegExp("[^A-Za-z0-9_.]"))) v.append(QRegExp("\\b"+s+"\\b")); foreach (const QString s, m_functions) if (!s.contains(QRegExp("[^A-Za-z0-9_.]"))) f.append(QRegExp("\\b"+s+"\\b")); } void RSession::receiveSymbols(const QStringList& v, const QStringList & f) { m_variables=v; m_functions=f; emit symbolsChanged(); } void RSession::queueExpression(RExpression* expr) { m_expressionQueue.append(expr); if(status()==Cantor::Session::Done) QTimer::singleShot(0, this, SLOT(runNextExpression())); } void RSession::serverChangedStatus(int status) { kDebug()<<"changed status to "<command(); } if(m_expressionQueue.isEmpty()) changeStatus(Cantor::Session::Done); else runNextExpression(); } else changeStatus(Cantor::Session::Running); } void RSession::runNextExpression() { + if (m_expressionQueue.isEmpty()) + return; disconnect(m_rServer, SIGNAL(expressionFinished(int, const QString&)), 0, 0); disconnect(m_rServer, SIGNAL(inputRequested(const QString&)), 0, 0); disconnect(m_rServer, SIGNAL(showFilesNeeded(const QStringList&)), 0, 0); kDebug()<<"size: "<command(); connect(m_rServer, SIGNAL(expressionFinished(int, const QString &)), expr, SLOT(finished(int, const QString&))); connect(m_rServer, SIGNAL(inputRequested(const QString&)), expr, SIGNAL(needsAdditionalInformation(const QString&))); connect(m_rServer, SIGNAL(showFilesNeeded(const QStringList&)), expr, SLOT(showFilesAsResult(const QStringList&))); m_rServer->runCommand(expr->command()); } void RSession::sendInputToServer(const QString& input) { QString s=input; if(!input.endsWith('\n')) s+='\n'; m_rServer->answerRequest(s); } #include "rsession.moc" diff --git a/src/backends/R/rsession.h b/src/backends/R/rsession.h index 48bff22e..74f0925e 100644 --- a/src/backends/R/rsession.h +++ b/src/backends/R/rsession.h @@ -1,72 +1,72 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #ifndef _RSESSION_H #define _RSESSION_H #include #include #include "session.h" #include "rserver_interface.h" class RExpression; class KProcess; class RSession : public Cantor::Session { Q_OBJECT public: RSession( Cantor::Backend* backend); ~RSession(); void login(); void logout(); void interrupt(); Cantor::Expression* evaluateExpression(const QString& command, Cantor::Expression::FinishingBehavior behave); Cantor::CompletionObject* completionFor(const QString& command, int index=-1); - QSyntaxHighlighter* syntaxHighlighter(QTextEdit* parent); + QSyntaxHighlighter* syntaxHighlighter(QObject* parent); void queueExpression(RExpression* expr); void sendInputToServer(const QString& input); protected slots: void serverChangedStatus(int status); void runNextExpression(); void receiveSymbols(const QStringList& v, const QStringList & f); void fillSyntaxRegExps(QVector& v, QVector& f); signals: void symbolsChanged(); private: KProcess* m_rProcess; org::kde::Cantor::R* m_rServer; QList m_expressionQueue; /* Available variables and functions, TODO make full classes and type infos */ QStringList m_variables; QStringList m_functions; }; #endif /* _RSESSION_H */ diff --git a/src/backends/kalgebra/kalgebrasession.cpp b/src/backends/kalgebra/kalgebrasession.cpp index a2095b64..9e601451 100644 --- a/src/backends/kalgebra/kalgebrasession.cpp +++ b/src/backends/kalgebra/kalgebrasession.cpp @@ -1,99 +1,101 @@ /************************************************************************************* * Copyright (C) 2009 by Aleix Pol * * * * 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 * *************************************************************************************/ #include "kalgebrasession.h" #include "kalgebraexpression.h" #include "kalgebracompletionobject.h" #include #include #include #include #include "kalgebrasyntaxhelpobject.h" #include #include KAlgebraSession::KAlgebraSession( Cantor::Backend* backend) : Session(backend) { m_analyzer = new Analitza::Analyzer; m_operatorsModel = new OperatorsModel; m_variablesModel = new VariablesModel(m_analyzer->variables()); m_operatorsModel->setVariables(m_analyzer->variables()); } KAlgebraSession::~KAlgebraSession() { delete m_analyzer; } void KAlgebraSession::login() { changeStatus(Cantor::Session::Done); emit ready(); } void KAlgebraSession::logout() {} void KAlgebraSession::interrupt() { changeStatus(Cantor::Session::Done); } Cantor::Expression* KAlgebraSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave) { KAlgebraExpression* expr=new KAlgebraExpression(this); expr->setFinishingBehavior(behave); changeStatus(Cantor::Session::Running); expr->setCommand(cmd); expr->evaluate(); changeStatus(Cantor::Session::Done); m_operatorsModel->setVariables(m_analyzer->variables()); m_variablesModel->updateInformation(); return expr; } Cantor::CompletionObject* KAlgebraSession::completionFor(const QString& command, int index) { return new KAlgebraCompletionObject(command, index, this); } Cantor::SyntaxHelpObject* KAlgebraSession::syntaxHelpFor(const QString& cmd) { return new KAlgebraSyntaxHelpObject(cmd, this); } OperatorsModel* KAlgebraSession::operatorsModel() { return m_operatorsModel; } -QSyntaxHighlighter* KAlgebraSession::syntaxHighlighter(QTextEdit* parent) +QSyntaxHighlighter* KAlgebraSession::syntaxHighlighter(QObject* parent) { - return new AlgebraHighlighter(parent->document()); + //return new AlgebraHighlighter(parent->document()); + // TODO: Think of something better here. + return new AlgebraHighlighter(NULL); } QAbstractItemModel* KAlgebraSession::variableModel() { return m_variablesModel; } diff --git a/src/backends/kalgebra/kalgebrasession.h b/src/backends/kalgebra/kalgebrasession.h index 02f91d87..000e0c12 100644 --- a/src/backends/kalgebra/kalgebrasession.h +++ b/src/backends/kalgebra/kalgebrasession.h @@ -1,56 +1,56 @@ /************************************************************************************* * Copyright (C) 2009 by Aleix Pol * * * * 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 * *************************************************************************************/ #ifndef KALGEBRA_SESSION_H #define KALGEBRA_SESSION_H #include "session.h" class OperatorsModel; class VariablesModel; class KAlgebraExpression; namespace Analitza { class Analyzer; } class KAlgebraSession : public Cantor::Session { Q_OBJECT public: KAlgebraSession( Cantor::Backend* backend); ~KAlgebraSession(); void login(); void logout(); void interrupt(); Cantor::Expression* evaluateExpression(const QString& command, Cantor::Expression::FinishingBehavior behave); Cantor::CompletionObject* completionFor(const QString& cmd, int index=-1); Cantor::SyntaxHelpObject* syntaxHelpFor(const QString& cmd); Analitza::Analyzer* analyzer() const { return m_analyzer; } OperatorsModel* operatorsModel(); - QSyntaxHighlighter* syntaxHighlighter(QTextEdit* parent); + QSyntaxHighlighter* syntaxHighlighter(QObject* parent); QAbstractItemModel* variableModel(); private: Analitza::Analyzer* m_analyzer; OperatorsModel* m_operatorsModel; VariablesModel* m_variablesModel; }; #endif diff --git a/src/backends/maxima/maximahighlighter.cpp b/src/backends/maxima/maximahighlighter.cpp index b5922f65..178e2e69 100644 --- a/src/backends/maxima/maximahighlighter.cpp +++ b/src/backends/maxima/maximahighlighter.cpp @@ -1,80 +1,80 @@ /* 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 "maximahighlighter.h" #include "maximakeywords.h" #include #include -MaximaHighlighter::MaximaHighlighter(QTextEdit* edit) : Cantor::DefaultHighlighter(edit) +MaximaHighlighter::MaximaHighlighter(QObject* parent) : Cantor::DefaultHighlighter(parent) { addRule(QRegExp("\\b[A-Za-z0-9_]+(?=\\()"), functionFormat()); //Code highlighting the different keywords addKeywords(MaximaKeywords::instance()->keywords()); addRule("FIXME", commentFormat()); addRule("TODO", commentFormat()); addFunctions(MaximaKeywords::instance()->functions()); addVariables(MaximaKeywords::instance()->variables()); addRule(QRegExp("\".*\""), stringFormat()); addRule(QRegExp("'.*'"), stringFormat()); commentStartExpression = QRegExp("/\\*"); commentEndExpression = QRegExp("\\*/"); } MaximaHighlighter::~MaximaHighlighter() { } void MaximaHighlighter::highlightBlock(const QString& text) { if (skipHighlighting(text)) return; //Do some backend independent highlighting (brackets etc.) DefaultHighlighter::highlightBlock(text); setCurrentBlockState(0); int startIndex = 0; if (previousBlockState() != 1) startIndex = commentStartExpression.indexIn(text); while (startIndex >= 0) { int endIndex = commentEndExpression.indexIn(text, startIndex); int commentLength; if (endIndex == -1) { setCurrentBlockState(1); commentLength = text.length() - startIndex; } else { commentLength = endIndex - startIndex + commentEndExpression.matchedLength(); } setFormat(startIndex, commentLength, commentFormat()); startIndex = commentStartExpression.indexIn(text, startIndex + commentLength); } } diff --git a/src/backends/maxima/maximahighlighter.h b/src/backends/maxima/maximahighlighter.h index ee769c21..17b4a210 100644 --- a/src/backends/maxima/maximahighlighter.h +++ b/src/backends/maxima/maximahighlighter.h @@ -1,40 +1,40 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #ifndef _MAXIMAHIGHLIGHTER_H #define _MAXIMAHIGHLIGHTER_H #include "defaulthighlighter.h" class MaximaHighlighter : public Cantor::DefaultHighlighter { public: - MaximaHighlighter( QTextEdit* edit); + MaximaHighlighter( QObject* parent); ~MaximaHighlighter(); protected: void highlightBlock(const QString &text); private: QRegExp commentStartExpression; QRegExp commentEndExpression; }; #endif /* _MAXIMAHIGHLIGHTER_H */ diff --git a/src/backends/maxima/maximasession.cpp b/src/backends/maxima/maximasession.cpp index 67a254e3..33149a95 100644 --- a/src/backends/maxima/maximasession.cpp +++ b/src/backends/maxima/maximasession.cpp @@ -1,604 +1,610 @@ /* 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 "maximasession.h" #include "maximaexpression.h" #include "maximacompletionobject.h" #include "maximasyntaxhelpobject.h" #include "maximahighlighter.h" #include #include #include #include #include #include #include #include #include "settings.h" #include "result.h" //NOTE: the \\s in the expressions is needed, because Maxima seems to sometimes insert newlines/spaces between the letters //maybe this is caused by some behaviour if the Prompt is split into multiple "readStdout" calls //the Expressions are encapsulated in () to allow capturing for the text const QRegExp MaximaSession::MaximaPrompt=QRegExp("(\\(\\s*%\\s*I\\s*[0-9\\s]*\\))"); //Text, maxima outputs, if it's taking new input const QRegExp MaximaSession::MaximaOutputPrompt=QRegExp("(\\(\\s*%\\s*O\\s*[0-9\\s]*\\))"); //Text, maxima outputs, before any output static QByteArray initCmd="display2d:false$ \n"\ "inchar:%I$ \n"\ "outchar:%O$ \n"\ "print(____END_OF_INIT____); \n"; static QByteArray helperInitCmd="simp: false$ \n"; MaximaSession::MaximaSession( Cantor::Backend* backend) : Session(backend) { kDebug(); m_isInitialized=false; m_isHelperReady=false; m_server=0; m_maxima=0; m_process=0; m_helperProcess=0; m_helperMaxima=0; m_justRestarted=false; m_useLegacy=false; } MaximaSession::~MaximaSession() { kDebug(); } void MaximaSession::login() { kDebug()<<"login"; if (m_process) m_process->deleteLater(); if(!m_server||!m_server->isListening()) startServer(); m_maxima=0; m_process=new KProcess(this); QStringList args; //TODO: these parameters may need tweaking to run on windows (see wxmaxima for hints) if(m_useLegacy) args<<"-r"<serverPort()); else args<<"-r"<serverPort()); m_process->setProgram(MaximaSettings::self()->path().toLocalFile(),args); m_process->start(); connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(restartMaxima())); connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(reportProcessError(QProcess::ProcessError))); if(!m_helperQueue.isEmpty()) runNextHelperCommand(); } void MaximaSession::startServer() { kDebug()<<"starting up maxima server"; const int defaultPort=4060; int port=defaultPort; m_server=new QTcpServer(this); connect(m_server, SIGNAL(newConnection()), this, SLOT(newConnection())); while(! m_server->listen(QHostAddress::LocalHost, port) ) { kDebug()<<"Could not listen to "<defaultPort+50) { KMessageBox::error(0, i18n("Could not start the server."), i18n("Error - Cantor")); return; } } kDebug()<<"got a server on "<write(initCmd); } void MaximaSession::newHelperClient(QTcpSocket* socket) { kDebug()<<"got new helper client"; m_helperMaxima=socket; connect(m_helperMaxima, SIGNAL(readyRead()), this, SLOT(readHelperOut())); m_helperMaxima->write(helperInitCmd); m_helperMaxima->write(initCmd); } void MaximaSession::logout() { kDebug()<<"logout"; if(!m_process||!m_maxima) return; disconnect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(restartMaxima())); if(m_expressionQueue.isEmpty()) { m_maxima->write("quit();\n"); m_maxima->flush(); //evaluateExpression("quit();", Cantor::Expression::DeleteOnFinish); } else { m_expressionQueue.clear(); } //Give maxima time to clean up kDebug()<<"waiting for maxima to finish"; if(m_process->state()!=QProcess::NotRunning) { if(!m_maxima->waitForDisconnected(3000)) { m_process->kill(); m_maxima->waitForDisconnected(3000); } } m_maxima->close(); kDebug()<<"done logging out"; delete m_process; m_process=0; delete m_helperProcess; m_helperProcess=0; delete m_helperMaxima; m_helperMaxima=0; delete m_maxima; m_maxima=0; kDebug()<<"destroyed maxima"; m_expressionQueue.clear(); } void MaximaSession::newConnection() { kDebug()<<"new connection"; QTcpSocket* const socket=m_server->nextPendingConnection(); if(m_maxima==0) { newMaximaClient(socket); }else if (m_helperMaxima==0) { newHelperClient(socket); }else { kDebug()<<"got another client, without needing one"; } } Cantor::Expression* MaximaSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave) { kDebug()<<"evaluating: "<setFinishingBehavior(behave); expr->setCommand(cmd); expr->evaluate(); return expr; } MaximaExpression* MaximaSession::evaluateHelperExpression(const QString& cmd) { if(!m_helperMaxima) startHelperProcess(); MaximaExpression* expr=new MaximaExpression(this, MaximaExpression::HelpExpression); expr->setFinishingBehavior(Cantor::Expression::DoNotDelete); expr->setCommand(cmd); expr->evaluate(); return expr; } void MaximaSession::appendExpressionToQueue(MaximaExpression* expr) { m_expressionQueue.append(expr); kDebug()<<"queue: "<readAll(); kDebug()<<"out: "<parseOutput(txt); m_cache.remove(0, max); } } void MaximaSession::killLabels() { Cantor::Expression* e=evaluateExpression("kill(labels);", Cantor::Expression::DeleteOnFinish); connect(e, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SIGNAL(ready())); } void MaximaSession::reportProcessError(QProcess::ProcessError e) { kDebug()<<"process error"; if(e==QProcess::FailedToStart) { changeStatus(Cantor::Session::Done); emit error(i18n("Failed to start Maxima")); } } void MaximaSession::readHelperOut() { kDebug()<<"reading stdOut from helper process"; QString out=m_helperMaxima->readAll(); kDebug()<<"out: "<needsLatexResult(); expr->parseOutput(out); if(expr->type()==MaximaExpression::TexExpression&&!expr->needsLatexResult()) { kDebug()<<"expression doesn't need latex anymore"; m_helperQueue.removeFirst(); runNextHelperCommand(); } } } void MaximaSession::currentExpressionChangedStatus(Cantor::Expression::Status status) { if(status!=Cantor::Expression::Computing) //The session is ready for the next command { kDebug()<<"expression finished"; MaximaExpression* expression=m_expressionQueue.first(); disconnect(expression, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentExpressionChangedStatus(Cantor::Expression::Status))); if(expression->needsLatexResult()) { kDebug()<<"asking for tex version"; expression->setType(MaximaExpression::TexExpression); m_helperQueue<internalCommand(); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentExpressionChangedStatus(Cantor::Expression::Status))); if(command.isEmpty()) { kDebug()<<"empty command"; expr->forceDone(); }else { kDebug()<<"writing "<write((command+'\n').toLatin1()); } } } void MaximaSession::runNextHelperCommand() { kDebug()<<"helperQueue: "<type()==MaximaExpression::TexExpression) { QStringList out=expr->output(); if(!out.isEmpty()) { QString texCmd; foreach(const QString& part, out) { if(part.isEmpty()) continue; kDebug()<<"running "<write(texCmd.toUtf8()); }else { kDebug()<<"current tex request is empty, so drop it"; m_helperQueue.removeFirst(); } }else { QString command=expr->internalCommand(); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentHelperExpressionChangedStatus(Cantor::Expression::Status))); if(command.isEmpty()) { kDebug()<<"empty command"; expr->forceDone(); }else { kDebug()<<"writing "<write((command+'\n').toLatin1()); } } } } void MaximaSession::interrupt() { if(!m_expressionQueue.isEmpty()) m_expressionQueue.first()->interrupt(); m_expressionQueue.clear(); changeStatus(Cantor::Session::Done); } void MaximaSession::interrupt(MaximaExpression* expr) { Q_ASSERT(!m_expressionQueue.isEmpty()); if(expr==m_expressionQueue.first()) { disconnect(m_maxima, 0); disconnect(expr, 0, this, 0); restartMaxima(); kDebug()<<"done interrupting"; }else { m_expressionQueue.removeAll(expr); } } void MaximaSession::sendInputToProcess(const QString& input) { kDebug()<<"WARNING: use this method only if you know what you're doing. Use evaluateExpression to run commands"; kDebug()<<"running "<write(input.toLatin1()); } void MaximaSession::restartMaxima() { kDebug()<<"restarting maxima cooldown: "<serverPort()); else args<<"-r"<serverPort()); m_helperProcess->setProgram(MaximaSettings::self()->path().toLocalFile(),args); connect(m_helperProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(startHelperProcess())); m_helperProcess->start(); } void MaximaSession::setTypesettingEnabled(bool enable) { if(enable) { if(!m_isHelperReady) startHelperProcess(); //LaTeX and Display2d don't go together and even deliver wrong results evaluateExpression("display2d:false", Cantor::Expression::DeleteOnFinish); } else if(m_helperProcess) { disconnect(m_helperProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(startHelperProcess())); m_helperProcess->deleteLater(); m_helperProcess=0; m_helperMaxima=0; m_isHelperReady=false; } Cantor::Session::setTypesettingEnabled(enable); } Cantor::CompletionObject* MaximaSession::completionFor(const QString& command, int index) { return new MaximaCompletionObject(command, index, this); } Cantor::SyntaxHelpObject* MaximaSession::syntaxHelpFor(const QString& command) { return new MaximaSyntaxHelpObject(command, this); } -QSyntaxHighlighter* MaximaSession::syntaxHighlighter(QTextEdit* parent) +QSyntaxHighlighter* MaximaSession::syntaxHighlighter(QObject* parent) { return new MaximaHighlighter(parent); } #include "maximasession.moc" diff --git a/src/backends/maxima/maximasession.h b/src/backends/maxima/maximasession.h index 9795d224..58761ee0 100644 --- a/src/backends/maxima/maximasession.h +++ b/src/backends/maxima/maximasession.h @@ -1,108 +1,108 @@ /* Tims program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #ifndef _MAXIMASESSION_H #define _MAXIMASESSION_H #include "session.h" #include "expression.h" #include #include #include class MaximaExpression; class KProcess; class QTcpServer; class QTcpSocket; class QTimer; class MaximaSession : public Cantor::Session { Q_OBJECT public: static const QRegExp MaximaPrompt; static const QRegExp MaximaOutputPrompt; MaximaSession( Cantor::Backend* backend); ~MaximaSession(); void login(); void logout(); void startServer(); void newMaximaClient(QTcpSocket* socket); void newHelperClient(QTcpSocket* socket); Cantor::Expression* evaluateExpression(const QString& command, Cantor::Expression::FinishingBehavior behave); MaximaExpression* evaluateHelperExpression(const QString& command); void appendExpressionToQueue(MaximaExpression* expr); void appendExpressionToHelperQueue(MaximaExpression* expr); void interrupt(); void interrupt(MaximaExpression* expr); void sendInputToProcess(const QString& input); void setTypesettingEnabled(bool enable); Cantor::CompletionObject* completionFor(const QString& command, int index=-1); Cantor::SyntaxHelpObject* syntaxHelpFor(const QString& command); - QSyntaxHighlighter* syntaxHighlighter(QTextEdit* parent); + QSyntaxHighlighter* syntaxHighlighter(QObject* parent); public slots: void readStdOut(); void readHelperOut(); private slots: void newConnection(); void letExpressionParseOutput(); void currentExpressionChangedStatus(Cantor::Expression::Status status); void currentHelperExpressionChangedStatus(Cantor::Expression::Status status); void restartMaxima(); void restartsCooledDown(); void runFirstExpression(); void runNextHelperCommand(); void startHelperProcess(); void killLabels(); void reportProcessError(QProcess::ProcessError error); private: QTcpServer* m_server; QTcpSocket* m_maxima; KProcess* m_process; QTcpSocket* m_helperMaxima; KProcess* m_helperProcess; //only used to convert from expression to TeX/get syntax information QList m_expressionQueue; QList m_helperQueue; //Queue used for Expressions that need to be converted to LaTeX QString m_cache; bool m_isInitialized; bool m_isHelperReady; QString m_tmpPath; QTimer* m_restartCooldown; bool m_justRestarted; bool m_useLegacy; }; #endif /* _MAXIMASESSION_H */ diff --git a/src/backends/octave/octavehighlighter.cpp b/src/backends/octave/octavehighlighter.cpp index 35634dd7..3dd4b1f2 100644 --- a/src/backends/octave/octavehighlighter.cpp +++ b/src/backends/octave/octavehighlighter.cpp @@ -1,126 +1,126 @@ /* Copyright (C) 2010 Miha Čančula 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. */ #include "octavehighlighter.h" #include "result.h" #include "session.h" #include -OctaveHighlighter::OctaveHighlighter(QTextEdit* parent, Cantor::Session* session): DefaultHighlighter(parent), m_session(session) +OctaveHighlighter::OctaveHighlighter(QObject* parent, Cantor::Session* session): DefaultHighlighter(parent), m_session(session) { updateFunctions(); updateVariables(); m_operators << "+" << "-" << "*" << "/" << ".+" << ".-" << ".*" << "./" << "="; m_operators << "or" << "and" << "xor" << "not"; m_operators << "||" << "&&" << "=="; addRules(m_operators, operatorFormat()); m_keywords << "function" << "endfunction"; m_keywords << "for" << "endfor"; m_keywords << "while" << "endwhile"; m_keywords << "if" << "endif" << "else" << "elseif"; m_keywords << "switch" << "case" << "otherwise" << "endswitch"; m_keywords << "end"; addKeywords(m_keywords); addRule(QRegExp("\".*\""), stringFormat()); addRule(QRegExp("'.*'"), stringFormat()); rehighlight(); } OctaveHighlighter::~OctaveHighlighter() { } void OctaveHighlighter::updateFunctions() { m_functionsExpr = m_session->evaluateExpression("completion_matches('')"); connect ( m_functionsExpr, SIGNAL(statusChanged(Cantor::Expression::Status)), SLOT(receiveFunctions()) ); } void OctaveHighlighter::updateVariables() { m_varsExpr = m_session->evaluateExpression("who"); connect ( m_varsExpr, SIGNAL(statusChanged(Cantor::Expression::Status)), SLOT(receiveVariables()) ); } void OctaveHighlighter::receiveFunctions() { kDebug(); if (m_functionsExpr->status() != Cantor::Expression::Done || !m_functionsExpr->result()) { return; } QStringList names = m_functionsExpr->result()->toHtml().split("
\n"); QLatin1String under("__"); while (!names.first().contains(under)) { names.removeFirst(); } while (names.first().contains(under)) { names.removeFirst(); } int i = names.indexOf("zlim"); // Currently the last function alphabetically while (i > 0 && i < names.size() && names.at(i).startsWith('z')) { // Check if there are more functions after zlim i++; } names.erase(names.begin() + i, names.end()); kDebug() << "Received" << names.size() << "functions"; addFunctions(names); // The list of functions from completion_matches('') includes keywords and variables too, so we have to re-add them addVariables(m_variables); addKeywords(m_keywords); rehighlight(); } void OctaveHighlighter::receiveVariables() { if (m_varsExpr->status() != Cantor::Expression::Done || !m_varsExpr->result()) { return; } QString res = m_varsExpr->result()->toHtml(); res.replace("
"," "); res.remove(0, res.indexOf('\n')); res.remove('\n'); res = res.trimmed(); m_variables.clear(); foreach ( const QString& var, res.split(' ', QString::SkipEmptyParts)) { m_variables << var.trimmed(); } kDebug() << "Received" << m_variables.size() << "variables"; addVariables(m_variables); rehighlight(); } diff --git a/src/backends/octave/octavehighlighter.h b/src/backends/octave/octavehighlighter.h index 458cfeb2..f752319e 100644 --- a/src/backends/octave/octavehighlighter.h +++ b/src/backends/octave/octavehighlighter.h @@ -1,58 +1,58 @@ /* Copyright (C) 2010 Miha Čančula 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. */ #ifndef OCTAVEHIGHLIGHTER_H #define OCTAVEHIGHLIGHTER_H #include "defaulthighlighter.h" namespace Cantor { class Session; class Expression; } class OctaveHighlighter : public Cantor::DefaultHighlighter { Q_OBJECT public: - OctaveHighlighter(QTextEdit* parent, Cantor::Session* session); + OctaveHighlighter(QObject* parent, Cantor::Session* session); virtual ~OctaveHighlighter(); public Q_SLOTS: void receiveFunctions(); void receiveVariables(); void updateFunctions(); void updateVariables(); private: Cantor::Session* m_session; Cantor::Expression* m_functionsExpr; Cantor::Expression* m_varsExpr; bool m_functionsReceived; bool m_variablesReceived; QStringList m_operators; QStringList m_keywords; QStringList m_variables; }; #endif // OCTAVEHIGHLIGHTER_H diff --git a/src/backends/octave/octavesession.cpp b/src/backends/octave/octavesession.cpp index 273624ed..ebb156a2 100644 --- a/src/backends/octave/octavesession.cpp +++ b/src/backends/octave/octavesession.cpp @@ -1,302 +1,302 @@ /* Copyright (C) 2010 Miha Čančula 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. */ #include "octavesession.h" #include "octaveexpression.h" #include "octavecompletionobject.h" #include "octavesyntaxhelpobject.h" #include "result.h" #include "textresult.h" #include "settings.h" #include "octave-backend-config.h" #include #include #include #include #include "octavehighlighter.h" #include #include #include OctaveSession::OctaveSession ( Cantor::Backend* backend ) : Session ( backend ), m_process(0), m_currentExpression(0), m_watch(0), m_variableModel(new Cantor::DefaultVariableModel(this)) { kDebug() << octaveScriptInstallDir; } OctaveSession::~OctaveSession() { } void OctaveSession::login() { kDebug() << "login"; m_process = new KProcess ( this ); QStringList args; args << "--silent"; args << "--interactive"; args << "--persist"; // Add the cantor script directory to search path args << "--eval"; args << QString("addpath %1;").arg(octaveScriptInstallDir); if (OctaveSettings::integratePlots()) { // Do not show the popup when plotting, rather only print to a file args << "--eval"; args << "set (0, \"defaultfigurevisible\",\"off\");"; } else { args << "--eval"; args << "set (0, \"defaultfigurevisible\",\"on\");"; } // Do not show extra text in help commands args << "--eval"; args << "suppress_verbose_help_message(1);"; // Print the temp dir, used for plot files args << "--eval"; args << "____TMP_DIR____ = tempdir"; m_process->setProgram ( OctaveSettings::path().toLocalFile(), args ); kDebug() << m_process->program(); m_process->setOutputChannelMode ( KProcess::SeparateChannels ); connect ( m_process, SIGNAL ( readyReadStandardOutput() ), SLOT ( readOutput() ) ); connect ( m_process, SIGNAL ( readyReadStandardError() ), SLOT ( readError() ) ); connect ( m_process, SIGNAL ( error ( QProcess::ProcessError ) ), SLOT ( processError() ) ); m_process->start(); if (OctaveSettings::integratePlots()) { m_watch = new KDirWatch(this); m_watch->setObjectName("OctaveDirWatch"); connect (m_watch, SIGNAL(dirty(QString)), SLOT(plotFileChanged(QString)) ); } } void OctaveSession::logout() { kDebug() << "logout"; m_process->write("exit\n"); if (!m_process->waitForFinished(1000)) { m_process->kill(); } } void OctaveSession::interrupt() { kDebug() << "interrupt"; if (m_currentExpression) { m_currentExpression->interrupt(); } m_expressionQueue.clear(); kDebug() << "Sending SIGINT to Octave"; kill(m_process->pid(), SIGINT); changeStatus(Done); } void OctaveSession::processError() { kDebug() << "processError"; emit error(m_process->errorString()); } Cantor::Expression* OctaveSession::evaluateExpression ( const QString& command, Cantor::Expression::FinishingBehavior finishingBehavior ) { kDebug() << "evaluateExpression: " << command; OctaveExpression* expression = new OctaveExpression ( this ); expression->setCommand ( command ); expression->setFinishingBehavior ( finishingBehavior ); expression->evaluate(); return expression; } void OctaveSession::runExpression ( OctaveExpression* expression ) { kDebug() << "runExpression"; if ( status() != Done ) { m_expressionQueue.enqueue ( expression ); kDebug() << m_expressionQueue.size(); } else { m_currentExpression = expression; changeStatus(Running); connect(m_currentExpression, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentExpressionStatusChanged(Cantor::Expression::Status))); QString command = expression->command(); command.replace('\n', ','); command += '\n'; m_process->write ( command.toLocal8Bit() ); } } void OctaveSession::readError() { kDebug() << "readError"; QString error = QString::fromLocal8Bit(m_process->readAllStandardError()); if (!m_currentExpression || error.isEmpty()) { return; } m_currentExpression->parseError(error); } void OctaveSession::readOutput() { kDebug() << "readOutput"; while (m_process->bytesAvailable() > 0) { if (m_tempDir.isEmpty() && !m_process->canReadLine()) { kDebug() << "Waiting"; // Wait for the full line containing octave's tempDir return; } QString line = QString::fromLocal8Bit(m_process->readLine()); if (!m_currentExpression) { if (m_prompt.isEmpty() && line.contains(":1>")) { kDebug() << "Found Octave prompt:" << line; line.replace(":1", ":[0-9]+"); m_prompt.setPattern(line); changeStatus(Done); if (!m_expressionQueue.isEmpty()) { runExpression(m_expressionQueue.dequeue()); } emit ready(); } else if (line.contains("____TMP_DIR____")) { m_tempDir = line; m_tempDir.remove(0,18); m_tempDir.chop(1); // isolate the tempDir's location kDebug() << "Got temporary file dir:" << m_tempDir; if (m_watch) { m_watch->addDir(m_tempDir, KDirWatch::WatchFiles); } } } else if (line.contains(m_prompt)) { // Check for errors before finalizing the expression // this makes sure that all errors are caught readError(); m_currentExpression->finalize(); if (m_currentExpression->command().contains(" = ")) { emit variablesChanged(); } if (m_currentExpression->command().contains("function ")) { emit functionsChanged(); } } else { // Avoid many calls to setResult if a lot of output came at the same time while (m_process->canReadLine()) { line += QString::fromLocal8Bit(m_process->readLine()); } m_currentExpression->parseOutput(line); } } } void OctaveSession::currentExpressionStatusChanged(Cantor::Expression::Status status) { kDebug() << "currentExpressionStatusChanged"; if (!m_currentExpression) { return; } switch (status) { case Cantor::Expression::Computing: break; case Cantor::Expression::Interrupted: break; case Cantor::Expression::Done: case Cantor::Expression::Error: changeStatus(Done); if (!m_expressionQueue.isEmpty()) { runExpression(m_expressionQueue.dequeue()); } break; } } void OctaveSession::plotFileChanged(const QString& filename) { if (!QFile::exists(filename) || !filename.split('/').last().contains("c-ob-")) { return; } if (m_currentExpression) { m_currentExpression->parsePlotFile(filename); } } Cantor::CompletionObject* OctaveSession::completionFor ( const QString& cmd, int index ) { return new OctaveCompletionObject ( cmd, index, this ); } Cantor::SyntaxHelpObject* OctaveSession::syntaxHelpFor ( const QString& cmd ) { return new OctaveSyntaxHelpObject ( cmd, this ); } -QSyntaxHighlighter* OctaveSession::syntaxHighlighter ( QTextEdit* parent ) +QSyntaxHighlighter* OctaveSession::syntaxHighlighter ( QObject* parent ) { OctaveHighlighter* highlighter = new OctaveHighlighter ( parent, this ); connect ( this, SIGNAL(functionsChanged()), highlighter, SLOT(updateFunctions()) ); connect ( this, SIGNAL(variablesChanged()), highlighter, SLOT(updateVariables()) ); return highlighter; } QAbstractItemModel* OctaveSession::variableModel() { return m_variableModel; } void OctaveSession::runSpecificCommands() { m_process->write("figure(1,'visible','off')"); } diff --git a/src/backends/octave/octavesession.h b/src/backends/octave/octavesession.h index 2e9e5d3d..53058863 100644 --- a/src/backends/octave/octavesession.h +++ b/src/backends/octave/octavesession.h @@ -1,83 +1,83 @@ /* Copyright (C) 2010 Miha Čančula 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. */ #ifndef OCTAVESESSION_H #define OCTAVESESSION_H #include #include #include #include #include namespace Cantor { class DefaultVariableModel; } class KTemporaryFile; class KDirWatch; class OctaveExpression; class KProcess; class OctaveSession : public Cantor::Session { Q_OBJECT public: OctaveSession(Cantor::Backend* backend); ~OctaveSession(); virtual void interrupt(); virtual Cantor::Expression* evaluateExpression(const QString& command, Cantor::Expression::FinishingBehavior finishingBehavior); virtual void logout(); virtual void login(); virtual Cantor::CompletionObject* completionFor(const QString& cmd, int index=-1); virtual Cantor::SyntaxHelpObject* syntaxHelpFor(const QString& cmd); - virtual QSyntaxHighlighter* syntaxHighlighter(QTextEdit* parent); + virtual QSyntaxHighlighter* syntaxHighlighter(QObject* parent); virtual QAbstractItemModel* variableModel(); void runExpression(OctaveExpression* expression); private: KProcess* m_process; QTextStream m_stream; QQueue m_expressionQueue; QPointer m_currentExpression; QRegExp m_prompt; KDirWatch* m_watch; QString m_tempDir; Cantor::DefaultVariableModel* m_variableModel; void readFromOctave(QByteArray data); private Q_SLOTS: void readOutput(); void readError(); void currentExpressionStatusChanged(Cantor::Expression::Status status); void processError(); void plotFileChanged(const QString& filename); void runSpecificCommands(); Q_SIGNALS: void functionsChanged(); void variablesChanged(); }; #endif // OCTAVESESSION_H diff --git a/src/backends/qalculate/qalculatehighlighter.cpp b/src/backends/qalculate/qalculatehighlighter.cpp index c9a3ad42..dd74a784 100644 --- a/src/backends/qalculate/qalculatehighlighter.cpp +++ b/src/backends/qalculate/qalculatehighlighter.cpp @@ -1,135 +1,135 @@ /************************************************************************************ * Copyright (C) 2009 by Milian Wolff * * * * 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 * *************************************************************************************/ #include "qalculatehighlighter.h" #include #include #include #include #include #include #include #include -QalculateHighlighter::QalculateHighlighter(QTextEdit* parent) +QalculateHighlighter::QalculateHighlighter(QObject* parent) : Cantor::DefaultHighlighter(parent) { } QalculateHighlighter::~QalculateHighlighter() { } void QalculateHighlighter::highlightBlock(const QString& text) { if ( text.isEmpty() || text.trimmed().isEmpty() || text.startsWith(QLatin1String(">>> ")) // filter error messages, they get highlighted via html || text.startsWith(i18n("ERROR")+':') || text.startsWith(i18n("WARNING")+':') ) { return; } int pos = 0; int count; ///TODO: Can't we use CALCULATOR->parse() or similar? /// Question is how to get the connection between /// MathStructur and position+length in @p text const QStringList& words = text.split(QRegExp("\\b"), QString::SkipEmptyParts); kDebug() << "highlight block:" << text; CALCULATOR->beginTemporaryStopMessages(); const QString decimalSymbol = KGlobal::locale()->decimalSymbol(); for ( int i = 0; i < words.size(); ++i, pos += count ) { count = words[i].size(); if ( words[i].trimmed().isEmpty() ) { continue; } kDebug() << "highlight word:" << words[i]; QTextCharFormat format = errorFormat(); if ( i < words.size() - 1 && words[i+1].trimmed() == "(" && CALCULATOR->getFunction(words[i].toUtf8().constData()) ) { // should be a function kDebug() << "function"; format = functionFormat(); } else if ( isOperatorAndWhitespace(words[i]) ) { // stuff like ") * (" is an invalid expression, but acutally OK // check if last number is actually a float bool isFloat = false; if ( words[i].trimmed() == decimalSymbol ) { if ( i > 0 ) { // lookbehind QString lastWord = words[i-1].trimmed(); if ( !lastWord.isEmpty() && lastWord.at(lastWord.size()-1).isNumber() ) { kDebug() << "actually float"; isFloat = true; } } if ( !isFloat && i < words.size() - 1 ) { // lookahead QString nextWord = words[i+1].trimmed(); if ( !nextWord.isEmpty() && nextWord.at(0).isNumber() ) { kDebug() << "float coming"; isFloat = true; } } } if ( !isFloat ) { kDebug() << "operator / whitespace"; format = operatorFormat(); } else { format = numberFormat(); } } else { MathStructure expr = CALCULATOR->parse(words[i].toAscii().constData()); if ( expr.isNumber() || expr.isNumber_exp() ) { kDebug() << "number"; format = numberFormat(); } else if ( expr.isVariable() ) { kDebug() << "variable"; format = variableFormat(); } else if ( expr.isUndefined() ) { kDebug() << "undefined"; } else if ( expr.isUnit() || expr.isUnit_exp() ) { kDebug() << "unit"; format = keywordFormat(); } } setFormat(pos, count, format); } CALCULATOR->endTemporaryStopMessages(); } bool QalculateHighlighter::isOperatorAndWhitespace(const QString& word) const { foreach ( const QChar& c, word ) { if ( c.isLetterOrNumber() ) { return false; } } return true; } diff --git a/src/backends/qalculate/qalculatehighlighter.h b/src/backends/qalculate/qalculatehighlighter.h index 921586de..ede58d83 100644 --- a/src/backends/qalculate/qalculatehighlighter.h +++ b/src/backends/qalculate/qalculatehighlighter.h @@ -1,37 +1,37 @@ /************************************************************************************ * Copyright (C) 2009 by Milian Wolff * * * * 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 * *************************************************************************************/ #ifndef QALCULATEHIGHLIGHTER_H #define QALCULATEHIGHLIGHTER_H #include "defaulthighlighter.h" class QalculateHighlighter : public Cantor::DefaultHighlighter { public: - QalculateHighlighter(QTextEdit* parent); + QalculateHighlighter(QObject* parent); ~QalculateHighlighter(); protected: virtual void highlightBlock(const QString& text); private: bool isOperatorAndWhitespace(const QString &word) const; }; #endif // QALCULATEHIGHLIGHTER_H diff --git a/src/backends/qalculate/qalculatesession.cpp b/src/backends/qalculate/qalculatesession.cpp index 75acbac8..f1400550 100644 --- a/src/backends/qalculate/qalculatesession.cpp +++ b/src/backends/qalculate/qalculatesession.cpp @@ -1,121 +1,121 @@ /************************************************************************************ * Copyright (C) 2009 by Milian Wolff * * Copyright (C) 2011 by Matteo Agostinelli * * * * 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 * *************************************************************************************/ #include "qalculatesession.h" #include "qalculateexpression.h" #include "qalculatecompletionobject.h" #include "qalculatehighlighter.h" #include "defaultvariablemodel.h" #include #include #include #include #include #include #include #include #include "qalculatesyntaxhelpobject.h" QalculateSession::QalculateSession( Cantor::Backend* backend) : Session(backend), m_variableModel(new Cantor::DefaultVariableModel(this)) { if ( !CALCULATOR ) { new Calculator(); CALCULATOR->loadGlobalDefinitions(); CALCULATOR->loadLocalDefinitions(); CALCULATOR->loadExchangeRates(); } // from qalc.cc in libqalculate std::string ansName = "ans"; // m_undefined is not a variable in this class, but is defined in // libqalculate/includes.h m_ansVariables.append(static_cast(CALCULATOR->addVariable(new KnownVariable("Temporary", ansName, m_undefined, "Last Answer", false)))); m_ansVariables[0]->addName("answer"); m_ansVariables[0]->addName(ansName + "1"); m_ansVariables.append(static_cast(CALCULATOR->addVariable(new KnownVariable("Temporary", ansName+"2", m_undefined, "Answer 2", false)))); m_ansVariables.append(static_cast(CALCULATOR->addVariable(new KnownVariable("Temporary", ansName+"3", m_undefined, "Answer 3", false)))); m_ansVariables.append(static_cast(CALCULATOR->addVariable(new KnownVariable("Temporary", ansName+"4", m_undefined, "Answer 4", false)))); m_ansVariables.append(static_cast(CALCULATOR->addVariable(new KnownVariable("Temporary", ansName+"5", m_undefined, "Answer 5", false)))); } QalculateSession::~QalculateSession() { CALCULATOR->abort(); } void QalculateSession::login() { changeStatus(Cantor::Session::Done); emit ready(); } void QalculateSession::logout() { } void QalculateSession::interrupt() { changeStatus(Cantor::Session::Done); } Cantor::Expression* QalculateSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave) { QalculateExpression* expr = new QalculateExpression(this); expr->setFinishingBehavior(behave); changeStatus(Cantor::Session::Running); expr->setCommand(cmd); expr->evaluate(); changeStatus(Cantor::Session::Done); return expr; } Cantor::CompletionObject* QalculateSession::completionFor(const QString& command, int index) { return new QalculateCompletionObject(command, index, this); } Cantor::SyntaxHelpObject* QalculateSession::syntaxHelpFor(const QString& cmd) { return new QalculateSyntaxHelpObject(cmd, this); } -QSyntaxHighlighter* QalculateSession::syntaxHighlighter(QTextEdit* parent) +QSyntaxHighlighter* QalculateSession::syntaxHighlighter(QObject* parent) { return new QalculateHighlighter(parent); } void QalculateSession::setLastResult(MathStructure result) { for (int i = m_ansVariables.size()-1; i >0 ; --i) { m_ansVariables[i]->set(m_ansVariables[i-1]->get()); } m_ansVariables[0]->set(result); } QAbstractItemModel* QalculateSession::variableModel() { return m_variableModel; } diff --git a/src/backends/qalculate/qalculatesession.h b/src/backends/qalculate/qalculatesession.h index 3a1eeecb..b911a0f2 100644 --- a/src/backends/qalculate/qalculatesession.h +++ b/src/backends/qalculate/qalculatesession.h @@ -1,61 +1,61 @@ /************************************************************************************* * Copyright (C) 2009 by Milian Wolff * * * * 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 * *************************************************************************************/ #ifndef QALCULATE_SESSION_H #define QALCULATE_SESSION_H #include "session.h" #include #include #include namespace Cantor { class DefaultVariableModel; } class QalculateEngine; class QalculateSession : public Cantor::Session { Q_OBJECT private: QList m_ansVariables; Cantor::DefaultVariableModel* m_variableModel; public: QalculateSession( Cantor::Backend* backend); ~QalculateSession(); virtual void login(); virtual void logout(); virtual void interrupt(); virtual Cantor::Expression* evaluateExpression(const QString& command, Cantor::Expression::FinishingBehavior behave); virtual Cantor::CompletionObject* completionFor(const QString& cmd, int index=-1); virtual Cantor::SyntaxHelpObject* syntaxHelpFor(const QString& cmd); - virtual QSyntaxHighlighter* syntaxHighlighter(QTextEdit* parent); + virtual QSyntaxHighlighter* syntaxHighlighter(QObject* parent); void setLastResult(MathStructure); QAbstractItemModel* variableModel(); }; #endif diff --git a/src/backends/qalculate/qalculatesyntaxhelpobject.cpp b/src/backends/qalculate/qalculatesyntaxhelpobject.cpp index dccd008c..725e5290 100644 --- a/src/backends/qalculate/qalculatesyntaxhelpobject.cpp +++ b/src/backends/qalculate/qalculatesyntaxhelpobject.cpp @@ -1,277 +1,279 @@ /************************************************************************************* * Copyright (C) 2009 by Milian Wolff * * * * 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 * *************************************************************************************/ #include "settings.h" #include "qalculatesyntaxhelpobject.h" #include "qalculatesession.h" #include "settings.h" #include #include #include #include #include #include #include #include QalculateSyntaxHelpObject::QalculateSyntaxHelpObject(const QString& command, QalculateSession* session) : SyntaxHelpObject(command, session), m_answer(QString()) { } void QalculateSyntaxHelpObject::fetchInformation() { std::string cmd = command().remove("help").simplified().toLatin1().data(); kDebug() << "HELP CALLED FOR:" << QString(cmd.c_str()); if (cmd == "plot") { setPlotInformation(); return; } if (cmd == "saveVariables") { setSaveVariablesInformation(); return; } if (cmd == "loadVariables") { setLoadVariablesInformation(); return; } ExpressionItem *item = CALCULATOR->getActiveExpressionItem(cmd); if (!item) { m_answer = i18n("No function, variable or unit with specified name exist."); return; } switch(item->type()) { case TYPE_FUNCTION: MathFunction *f = (MathFunction*) item; QString title = i18n("Function: %1", item->title().c_str()); const ExpressionName *ename = &f->preferredName(false); int iargs = f->maxargs(); if(iargs < 0) { iargs = f->minargs() + 1; } QString str,str2,syntax; str += ename->name.c_str(); str += "("; if(iargs != 0) { Argument *arg; Argument default_arg; for(int i2 = 1; i2 <= iargs; i2++) { if(i2 > f->minargs()) { str += "["; } if(i2 > 1) { str += QString(CALCULATOR->getComma().c_str()); str += " "; } arg = f->getArgumentDefinition(i2); if(arg && !arg->name().empty()) { str2 = arg->name().c_str(); } else { str2 = "argument"; str2 += " "; str2 += QString::number(i2); } str += str2; if(i2 > f->minargs()) { str += "]"; } } if(f->maxargs() < 0) { str += CALCULATOR->getComma().c_str(); str += " ..."; } } str += ")"; syntax = QString("

%1

").arg(str); QString arguments = ""; if(iargs != 0) { Argument *arg; Argument default_arg; for(int i2 = 1; i2 <= iargs; i2++) { arg = f->getArgumentDefinition(i2); if(arg && !arg->name().empty()) { str = arg->name().c_str(); } else { str = QString::number(i2); } str += ": "; if(arg) { str2 = arg->printlong().c_str(); } else { str2 = default_arg.printlong().c_str(); } if(i2 > f->minargs()) { str2 += " ("; //optional argument, in description str2 += "optional"; if(!f->getDefaultValue(i2).empty()) { str2 += ", "; //argument default, in description str2 += "default: "; str2 += f->getDefaultValue(i2).c_str(); } str2 += ")"; } str += str2; arguments += QString("

%1

").arg(str); } } QString desc = QString("

%1

").arg(item->description().c_str()); m_answer = title + desc + syntax + arguments; + setHtml("

"+syntax+"

"); + emit done(); } } void QalculateSyntaxHelpObject::setPlotInformation() { QString title = "

" + i18n("Plotting interface") + "

"; QString desc = "

" + i18n("Plots one or more functions either inline or in a separate window.") + "

"; QString expression = i18n("expression"); QString option = i18n("option"); QString value = i18n("value"); QString syntax = QString("

plot %1 [%2=%3 ...] [, %4 [%5=%6 ...]] ...

"); syntax = syntax.arg(expression, option, value, expression, option, value); QString integer = i18n("integer"); QString boolean = i18n("boolean"); QString number = i18n("number"); QString defaultValue = i18n("default: %1"); QString noDefault = ""; QString optionFormat2 = "

%1: %2

"; QString optionFormat3 = "

%1: %2 (%3)

"; QString optionFormat4 = "

%1: %2 (%3, %4)

"; QStringList boolList; boolList << "false" << "true"; QString legendDefault; QString styleDefault; QString smoothingDefault; switch (QalculateSettings::plotLegend()) { case QalculateSettings::LEGEND_NONE: legendDefault = "none"; break; case QalculateSettings::LEGEND_TOP_LEFT: legendDefault = "top_left"; break; case QalculateSettings::LEGEND_TOP_RIGHT: legendDefault = "top_right"; break; case QalculateSettings::LEGEND_BOTTOM_LEFT: legendDefault = "bottom_left"; break; case QalculateSettings::LEGEND_BOTTOM_RIGHT: legendDefault = "bottom_right"; break; case QalculateSettings::LEGEND_BELOW: legendDefault = "below"; break; case QalculateSettings::LEGEND_OUTSIDE: legendDefault = "outside"; break; } switch(QalculateSettings::plotSmoothing()) { case QalculateSettings::SMOOTHING_NONE: smoothingDefault = "none"; break; case QalculateSettings::SMOOTHING_UNIQUE: smoothingDefault = "monotonic"; break; case QalculateSettings::SMOOTHING_CSPLINES: smoothingDefault = "csplines"; break; case QalculateSettings::SMOOTHING_BEZIER: smoothingDefault = "bezier"; break; case QalculateSettings::SMOOTHING_SBEZIER: smoothingDefault = "sbezier"; break; } switch(QalculateSettings::plotStyle()) { case QalculateSettings::STYLE_LINES: styleDefault = "lines"; break; case QalculateSettings::STYLE_POINTS: styleDefault = "points"; break; case QalculateSettings::STYLE_LINES_POINTS: styleDefault = "points_lines"; break; case QalculateSettings::STYLE_BOXES: styleDefault = "boxes"; break; case QalculateSettings::STYLE_HISTOGRAM: styleDefault = "histogram"; break; case QalculateSettings::STYLE_STEPS: styleDefault = "steps"; break; case QalculateSettings::STYLE_CANDLESTICKS: styleDefault = "candlesticks"; break; case QalculateSettings::STYLE_DOTS: styleDefault = "dots"; break; } QString arguments = ""; arguments += optionFormat3.arg("title", i18n("The function's name"), defaultValue.arg("expression")); arguments += optionFormat2.arg("plottitle", i18n("Title label")); arguments += optionFormat2.arg("xlabel", i18n("x-axis label")); arguments += optionFormat2.arg("ylabel", i18n("y-axis label")); arguments += optionFormat2.arg("filename", i18n("Image to save plot to. If empty shows plot in a window on the screen. If inline=true the image is shown regardless of this option.")); arguments += optionFormat3.arg("filetype", i18n("The image type to save as. One of auto, png, ps, eps, latex, svg, fig."), defaultValue.arg("auto")); arguments += optionFormat4.arg("color", i18n("Set to true for colored plot, false for monochrome."), boolean, defaultValue.arg(boolList[QalculateSettings::coloredPlot()])); arguments += optionFormat3.arg("xmin", i18n("Minimum x-axis value."), number); arguments += optionFormat3.arg("xmax", i18n("Maximum x-axis value."), number); arguments += optionFormat4.arg("xlog", i18n("If a logarithmic scale shall be used for the x-axis."), boolean, defaultValue.arg("false")); arguments += optionFormat4.arg("ylog", i18n("If a logarithmic scale shall be used for the y-axis."), boolean, defaultValue.arg("false")); arguments += optionFormat4.arg("xlogbase", i18n("Logarithmic base for the x-axis."), number, defaultValue.arg("10")); arguments += optionFormat4.arg("ylogbase", i18n("Logarithmic base for the y-axis."), boolean, defaultValue.arg("10")); arguments += optionFormat4.arg("grid", i18n("If a grid shall be shown in the plot."), boolean, defaultValue.arg(boolList[QalculateSettings::plotGrid()])); arguments += optionFormat4.arg("border", i18n("If the plot shall be surrounded by borders on all sides (not just axis)."), boolean, defaultValue.arg(boolList[QalculateSettings::plotBorder()])); arguments += optionFormat4.arg("linewidth", i18n("Width of lines."), integer, defaultValue.arg(QString::number(QalculateSettings::plotLineWidth()))); arguments += optionFormat3.arg("legend", i18n("Where the plot legend shall be placed. One of none, top_left, top_right, bottom_left, bottom_right, below, outside"), defaultValue.arg(legendDefault)); arguments += optionFormat3.arg("smoothing", i18n("Plot smoothing. One of none, unique, csplines, bezier, sbezier"), defaultValue.arg(smoothingDefault)); arguments += optionFormat3.arg("style", i18n("Plot style. One of lines, points, points_lines, boxes, histogram, steps, candlesticks, dots"), defaultValue.arg(styleDefault)); arguments += optionFormat4.arg("xaxis2", i18n("Use scale on second x-axis."), boolean, defaultValue.arg("false")); arguments += optionFormat4.arg("yaxis2", i18n("Use scale on second y-axis."), boolean, defaultValue.arg("false")); arguments += optionFormat4.arg("inline", i18n("If the plot is to be drawn inline, instead of in a new window."), boolean, defaultValue.arg(boolList[QalculateSettings::inlinePlot()])); arguments += optionFormat3.arg("step", i18n("Distance between two interpolation points. See also steps."), number); arguments += optionFormat4.arg("steps", i18n("Number of interpolation points. See also step."), integer, defaultValue.arg(QString::number(QalculateSettings::plotSteps()))); arguments += optionFormat3.arg("xvar", i18n("The name of the x variable. This must be an unknown variable"), defaultValue.arg("x")); m_answer = title + desc + syntax + arguments; } void QalculateSyntaxHelpObject::setSaveVariablesInformation() { QString title = "

" + i18n("Save variables to a file") + "

"; QString desc = "

" + i18n("Save all currently defined variables to a file. They can be reloaded with %1.", QLatin1String("loadVariables")) + "

"; QString syntax = "

saveVariables " + i18n("file") + "

"; QString arguments = "

" + i18n("file: the file to save to") + "

"; m_answer = title + desc + syntax + arguments; } void QalculateSyntaxHelpObject::setLoadVariablesInformation() { QString title = "

" + i18n("Load variables from a file") + "

"; QString desc = "

" + i18n("Load variables from a file that has previously been created by %1.", QLatin1String("saveVariables")) + "

"; QString syntax = "

loadVariables " + i18n("file") + "

"; QString arguments = "

" + i18n("file: the file to load") + "

"; m_answer = title + desc + syntax + arguments; } QString QalculateSyntaxHelpObject::answer() { fetchInformation(); return m_answer; } diff --git a/src/backends/sage/sagehighlighter.cpp b/src/backends/sage/sagehighlighter.cpp index 1c484530..86de136a 100644 --- a/src/backends/sage/sagehighlighter.cpp +++ b/src/backends/sage/sagehighlighter.cpp @@ -1,83 +1,83 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #include "sagehighlighter.h" #include "sagekeywords.h" #include -SageHighlighter::SageHighlighter(QTextEdit* edit) : Cantor::DefaultHighlighter(edit) +SageHighlighter::SageHighlighter(QObject* parent) : Cantor::DefaultHighlighter(parent) { addRule(QRegExp("[A-Za-z0-9_]+(?=\\()"), functionFormat()); QStringList keywords; //Preprocessor keywords = SageKeywords::instance()->keywords(); //specialvars keywords << "None" << "self" << "True" << "true" << "False" << "false" << "NotImplemented" << "Ellipsis"; addKeywords(keywords); QStringList builtinFunctions; builtinFunctions << "__future__" << "__import__" << "__name__" << "abs" << "all" << "any" << "apply" << "basestring" << "bool" << "buffer" << "callable" << "chr" << "classmethod" << "cmp" << "coerce" << "compile" << "complex" << "delattr" << "dict" << "dir" << "divmod" << "enumerate" << "eval" << "execfile" << "file" << "filter" << "float" << "frozenset" << "getattr" << "globals" << "hasattr" << "hash" << "hex" << "id" << "input" << "int" << "intern" << "isinstance" << "issubclass" << "iter" << "len" << "list" << "locals" << "long" << "map" << "max" << "min" << "object" << "oct" << "open" << "ord" << "pow" << "property" << "range" << "raw_input" << "reduce" << "reload" << "repr" << "reversed" << "round" << "set" << "setattr" << "slice" << "sorted" << "staticmethod" << "str" << "sum" << "super" << "tuple" << "type" << "unichr" << "unicode" << "vars" << "xrange" << "zip"; addRules(builtinFunctions, functionFormat()); addRule(QRegExp("\\S*[a-zA-Z\\-\\_]+\\S*\\.(?!\\d)"), objectFormat()); QStringList exceptionPatterns; exceptionPatterns<< "ArithmeticError" << "AssertionError" << "AttributeError" << "BaseException" << "DeprecationWarning" << "EnvironmentError" << "EOFError" << "Exception" << "FloatingPointError" << "FutureWarning" << "GeneratorExit" << "IOError" << "ImportError" << "ImportWarning" << "IndexError" << "KeyError" << "KeyboardInterrupt" << "LookupError" << "MemoryError" << "NameError" << "NotImplementedError" << "OSError" << "OverflowError" << "PendingDeprecationWarning" << "ReferenceError" << "RuntimeError" << "RuntimeWarning" << "StandardError" << "StopIteration" << "SyntaxError" << "SyntaxWarning" << "SystemError" << "SystemExit" << "TypeError" << "UnboundLocalError" << "UserWarning" << "UnicodeError" << "UnicodeWarning" << "UnicodeEncodeError" << "UnicodeDecodeError" << "UnicodeTranslateError" << "ValueError" << "Warning" << "WindowsError" << "ZeroDivisionError"; addRules(exceptionPatterns, objectFormat()); addRule(QRegExp("\".*\""), stringFormat()); addRule(QRegExp("'.*'"), stringFormat()); addRule(QRegExp("#[^\n]*"), commentFormat()); } SageHighlighter::~SageHighlighter() { } diff --git a/src/backends/sage/sagehighlighter.h b/src/backends/sage/sagehighlighter.h index 3178770b..dbbbe161 100644 --- a/src/backends/sage/sagehighlighter.h +++ b/src/backends/sage/sagehighlighter.h @@ -1,40 +1,40 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #ifndef _SAGEHIGHLIGHTER_H #define _SAGEHIGHLIGHTER_H #include "defaulthighlighter.h" /* this is basically a syntax highlighter for the Python programming Language, as Sage is based on it */ class SageHighlighter : public Cantor::DefaultHighlighter { public: - SageHighlighter( QTextEdit* edit); + SageHighlighter( QObject* parent); ~SageHighlighter(); }; #endif /* _SAGEHIGHLIGHTER_H */ diff --git a/src/backends/sage/sagesession.cpp b/src/backends/sage/sagesession.cpp index 33b90520..0c1281bf 100644 --- a/src/backends/sage/sagesession.cpp +++ b/src/backends/sage/sagesession.cpp @@ -1,302 +1,302 @@ /* 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 "sagesession.h" #include "sageexpression.h" #include "sagecompletionobject.h" #include "sagehighlighter.h" #include #include #include #include #include #include "settings.h" #include "sagehighlighter.h" const QByteArray SageSession::SagePrompt="sage: "; //Text, sage outputs after each command const QByteArray SageSession::SageAlternativePrompt="....: "; //Text, sage outputs when it expects further input //some commands that are run after login static QByteArray initCmd="os.environ['PAGER'] = 'cat' \n "\ "sage.misc.pager.EMBEDDED_MODE = True \n "\ "sage.misc.viewer.BROWSER='' \n "\ "sage.plot.plot3d.base.SHOW_DEFAULTS['viewer'] = 'tachyon' \n"\ "sage.misc.latex.EMBEDDED_MODE = True \n "\ "os.environ['PAGER'] = 'cat' \n "\ " __IPYTHON__.shell.autoindent=False \n "\ "print '____TMP_DIR____', sage.misc.misc.SAGE_TMP\n"\ "print '____END_OF_INIT____' \n "; SageSession::SageSession( Cantor::Backend* backend) : Session(backend) { kDebug(); m_isInitialized=false; connect( &m_dirWatch, SIGNAL( created( const QString& ) ), this, SLOT( fileCreated( const QString& ) ) ); } SageSession::~SageSession() { kDebug(); } void SageSession::login() { kDebug()<<"login"; m_process=new KPtyProcess(this); m_process->setProgram(SageSettings::self()->path().toLocalFile()); m_process->setOutputChannelMode(KProcess::SeparateChannels); m_process->setPtyChannels(KPtyProcess::AllChannels); m_process->pty()->setEcho(false); connect(m_process->pty(), SIGNAL(readyRead()), this, SLOT(readStdOut())); connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(readStdErr())); connect(m_process, SIGNAL(finished ( int, QProcess::ExitStatus )), this, SLOT(processFinished(int, QProcess::ExitStatus))); connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(reportProcessError(QProcess::ProcessError))); m_process->start(); m_process->pty()->write(initCmd); } void SageSession::logout() { kDebug()<<"logout"; interrupt(); disconnect(m_process, SIGNAL(finished ( int, QProcess::ExitStatus )), this, SLOT(processFinished(int, QProcess::ExitStatus))); m_process->pty()->write("exit\n"); m_process->deleteLater(); //Run sage-cleaner to kill all the orphans KProcess::startDetached(SageSettings::self()->path().toLocalFile(),QStringList()<<"-cleaner"); m_expressionQueue.clear(); } Cantor::Expression* SageSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave) { kDebug()<<"evaluating: "<setFinishingBehavior(behave); expr->setCommand(cmd); expr->evaluate(); return expr; } void SageSession::appendExpressionToQueue(SageExpression* expr) { m_expressionQueue.append(expr); if(m_expressionQueue.size()==1) { changeStatus(Cantor::Session::Running); runFirstExpression(); } } void SageSession::readStdOut() { QString out=m_process->pty()->readAll(); kDebug()<<"out: "<parseOutput(out); } } void SageSession::readStdErr() { kDebug()<<"reading stdErr"; QString out=m_process->readAllStandardError(); kDebug()<<"err: "<parseError(out); } } void SageSession::currentExpressionChangedStatus(Cantor::Expression::Status status) { if(status!=Cantor::Expression::Computing) //The session is ready for the next command { SageExpression* expr=m_expressionQueue.takeFirst(); disconnect(expr, 0, this, 0); if(m_expressionQueue.isEmpty()) changeStatus(Cantor::Session::Done); runFirstExpression(); } } void SageSession::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitCode); if(exitStatus==QProcess::CrashExit) { if(!m_expressionQueue.isEmpty()) { m_expressionQueue.last()->onProcessError(i18n("The Sage process crashed while evaluating this expression")); }else { //We don't have an actual command. it crashed for some other reason, just show a plain error message box KMessageBox::error(0, i18n("The Sage process crashed"), i18n("Cantor")); } }else { if(!m_expressionQueue.isEmpty()) { m_expressionQueue.last()->onProcessError(i18n("The Sage process exited while evaluating this expression")); }else { //We don't have an actual command. it crashed for some other reason, just show a plain error message box KMessageBox::error(0, i18n("The Sage process exited"), i18n("Cantor")); } } } void SageSession::reportProcessError(QProcess::ProcessError e) { if(e==QProcess::FailedToStart) { changeStatus(Cantor::Session::Done); emit error(i18n("Failed to start Sage")); } } void SageSession::runFirstExpression() { if(!m_expressionQueue.isEmpty()&&m_isInitialized) { SageExpression* expr=m_expressionQueue.first(); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentExpressionChangedStatus(Cantor::Expression::Status))); QString command=expr->command(); if(command.endsWith('?')) command=("help("+command.left(command.size()-1)+')'); if(command.startsWith('?')) command=("help("+command.mid(1)+')'); kDebug()<<"writing "<pty()->write(QString(command+'\n').toUtf8()); } } void SageSession::interrupt() { if(!m_expressionQueue.isEmpty()) m_expressionQueue.first()->interrupt(); m_expressionQueue.clear(); changeStatus(Cantor::Session::Done); } void SageSession::sendSignalToProcess(int signal) { kDebug()<<"sending signal....."<bash->sage-ipython QString cmd=QString("pkill -%1 -f -P `pgrep -P %2 bash` .*sage-ipython.*").arg(signal).arg(m_process->pid()); KProcess proc(this); proc.setShellCommand(cmd); proc.execute(); } void SageSession::sendInputToProcess(const QString& input) { m_process->pty()->write(input.toUtf8()); } void SageSession::waitForNextPrompt() { m_waitingForPrompt=true; } void SageSession::fileCreated( const QString& path ) { SageExpression* expr=m_expressionQueue.first(); if ( expr ) expr->addFileResult( path ); } void SageSession::setTypesettingEnabled(bool enable) { Cantor::Session::setTypesettingEnabled(enable); //tell the sage server to enable/disable pretty_print //the _ and __IP.outputcache() are needed to keep the // _ operator working if (enable) evaluateExpression("sage.misc.latex.pretty_print_default(true);_;__IP.outputcache()", Cantor::Expression::DeleteOnFinish); else evaluateExpression("sage.misc.latex.pretty_print_default(false);_;__IP.outputcache()", Cantor::Expression::DeleteOnFinish); } Cantor::CompletionObject* SageSession::completionFor(const QString& command, int index) { return new SageCompletionObject(command, index, this); } -QSyntaxHighlighter* SageSession::syntaxHighlighter(QTextEdit* parent) +QSyntaxHighlighter* SageSession::syntaxHighlighter(QObject* parent) { return new SageHighlighter(parent); } #include "sagesession.moc" diff --git a/src/backends/sage/sagesession.h b/src/backends/sage/sagesession.h index 5a70d21f..006d92ba 100644 --- a/src/backends/sage/sagesession.h +++ b/src/backends/sage/sagesession.h @@ -1,84 +1,84 @@ /* Tims program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #ifndef _SAGESESSION_H #define _SAGESESSION_H #include "session.h" #include "expression.h" #include #include class SageExpression; class KPtyProcess; class SageSession : public Cantor::Session { Q_OBJECT public: static const QByteArray SagePrompt; static const QByteArray SageAlternativePrompt; SageSession( Cantor::Backend* backend); ~SageSession(); void login(); void logout(); Cantor::Expression* evaluateExpression(const QString& command,Cantor::Expression::FinishingBehavior behave); void appendExpressionToQueue(SageExpression* expr); void interrupt(); void sendSignalToProcess(int signal); void sendInputToProcess(const QString& input); void waitForNextPrompt(); void setTypesettingEnabled(bool enable); Cantor::CompletionObject* completionFor(const QString& command, int index=-1); - QSyntaxHighlighter* syntaxHighlighter(QTextEdit* parent); + QSyntaxHighlighter* syntaxHighlighter(QObject* parent); public slots: void readStdOut(); void readStdErr(); private slots: void currentExpressionChangedStatus(Cantor::Expression::Status status); void processFinished(int exitCode, QProcess::ExitStatus exitStatus); void reportProcessError(QProcess::ProcessError error); void fileCreated(const QString& path); private: void runFirstExpression(); private: KPtyProcess* m_process; QList m_expressionQueue; bool m_isInitialized; QString m_tmpPath; KDirWatch m_dirWatch; bool m_waitingForPrompt; }; #endif /* _SAGESESSION_H */ diff --git a/src/backends/scilab/scilabhighlighter.cpp b/src/backends/scilab/scilabhighlighter.cpp index b19f8858..3ba8c0af 100644 --- a/src/backends/scilab/scilabhighlighter.cpp +++ b/src/backends/scilab/scilabhighlighter.cpp @@ -1,93 +1,93 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2011 Filipe Saraiva */ #include "scilabhighlighter.h" #include "scilabkeywords.h" #include #include -ScilabHighlighter::ScilabHighlighter(QTextEdit* edit) : Cantor::DefaultHighlighter(edit) +ScilabHighlighter::ScilabHighlighter(QObject* parent) : Cantor::DefaultHighlighter(parent) { kDebug() << "ScilabHighlighter construtor"; addRule(QRegExp("\\b[A-Za-z0-9_]+(?=\\()"), functionFormat()); //Code highlighting the different keywords addKeywords(ScilabKeywords::instance()->keywords()); addRule("FIXME", commentFormat()); addRule("TODO", commentFormat()); addFunctions(ScilabKeywords::instance()->functions()); addVariables(ScilabKeywords::instance()->variables()); addRule(QRegExp("\".*\""), stringFormat()); addRule(QRegExp("'.*'"), stringFormat()); addRule(QRegExp("//[^\n]*"), commentFormat()); commentStartExpression = QRegExp("/\\*"); commentEndExpression = QRegExp("\\*/"); } ScilabHighlighter::~ScilabHighlighter() { } void ScilabHighlighter::highlightBlock(const QString& text) { kDebug() << "ScilabHighlighter::highlightBlock"; kDebug() << "text: " << text; if (skipHighlighting(text)){ kDebug() << "skipHighlighting(" << text << " ) " << "== true"; return; } //Do some backend independent highlighting (brackets etc.) DefaultHighlighter::highlightBlock(text); setCurrentBlockState(0); int startIndex = 0; if (previousBlockState() != 1) startIndex = commentStartExpression.indexIn(text); while (startIndex >= 0) { int endIndex = commentEndExpression.indexIn(text, startIndex); int commentLength; if (endIndex == -1) { setCurrentBlockState(1); commentLength = text.length() - startIndex; } else { commentLength = endIndex - startIndex + commentEndExpression.matchedLength(); } setFormat(startIndex, commentLength, commentFormat()); startIndex = commentStartExpression.indexIn(text, startIndex + commentLength); } } QString ScilabHighlighter::nonSeparatingCharacters() const { kDebug() << "ScilabHighlighter::nonSeparatingCharacters() function"; return "[%]"; } diff --git a/src/backends/scilab/scilabhighlighter.h b/src/backends/scilab/scilabhighlighter.h index 9ada7b22..502e714f 100644 --- a/src/backends/scilab/scilabhighlighter.h +++ b/src/backends/scilab/scilabhighlighter.h @@ -1,41 +1,41 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2011 Filipe Saraiva */ #ifndef _SCILABHIGHLIGHTER_H #define _SCILABHIGHLIGHTER_H #include "defaulthighlighter.h" class ScilabHighlighter : public Cantor::DefaultHighlighter { public: - ScilabHighlighter(QTextEdit* edit); + ScilabHighlighter(QObject* parent); ~ScilabHighlighter(); protected: void highlightBlock(const QString &text); QString nonSeparatingCharacters() const; private: QRegExp commentStartExpression; QRegExp commentEndExpression; }; #endif /* _SCILABHIGHLIGHTER_H */ diff --git a/src/backends/scilab/scilabsession.cpp b/src/backends/scilab/scilabsession.cpp index 05f903ee..d406edc0 100644 --- a/src/backends/scilab/scilabsession.cpp +++ b/src/backends/scilab/scilabsession.cpp @@ -1,266 +1,266 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2011 Filipe Saraiva */ #include "scilabsession.h" #include "scilabexpression.h" #include "scilabhighlighter.h" #include "scilabcompletionobject.h" #include #include #include #include #include #include #include #include #include #include #include ScilabSession::ScilabSession( Cantor::Backend* backend) : Session(backend) { m_process = 0; kDebug(); } ScilabSession::~ScilabSession() { m_process->terminate(); kDebug(); } void ScilabSession::login() { kDebug()<<"login"; QStringList args; args << "-nb"; m_process = new KProcess(this); m_process->setProgram(ScilabSettings::self()->path().toLocalFile(), args); kDebug() << m_process->program(); m_process->setOutputChannelMode(KProcess::SeparateChannels); QObject::connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT (readOutput())); /* * Apparently, Scilab receive error messages by output standard stream. * So, standard error will be commented to verified it and fix a bug. * * See bug 292611 * -> https://bugs.kde.org/show_bug.cgi?id=292611 * * Filipe Saraiva - filipe@kde.org * * QObject::connect(m_process, SIGNAL(readyReadStandardError()), SLOT (readError())); */ m_process->start(); if(ScilabSettings::integratePlots()) { kDebug() << "integratePlots"; QString tempPath = QDir::tempPath(); QString pathScilabOperations = tempPath; pathScilabOperations.prepend("chdir('"); pathScilabOperations.append("');\n"); kDebug() << "Processing command to change chdir in Scilab. Command " << pathScilabOperations.toLocal8Bit(); m_process->write(pathScilabOperations.toLocal8Bit()); m_watch = new KDirWatch(this); m_watch->setObjectName("ScilabDirWatch"); m_watch->addDir(tempPath, KDirWatch::WatchFiles); kDebug() << "addDir " << tempPath << "? " << m_watch->contains(tempPath.toLocal8Bit()); QObject::connect(m_watch, SIGNAL(created(QString)), SLOT(plotFileChanged(QString))); } emit ready(); } void ScilabSession::logout() { kDebug()<<"logout"; m_process->write("exit\n"); if (!m_process->waitForFinished(1000)) { m_process->kill(); } m_runningExpressions.clear(); kDebug() << "m_runningExpressions: " << m_runningExpressions.isEmpty(); QDir removePlotFigures; QListIterator i(m_listPlotName); while(i.hasNext()){ removePlotFigures.remove(i.next().toLocal8Bit().constData()); } changeStatus(Cantor::Session::Done); } void ScilabSession::interrupt() { kDebug()<<"interrupt"; foreach(Cantor::Expression* e, m_runningExpressions) e->interrupt(); m_runningExpressions.clear(); changeStatus(Cantor::Session::Done); } Cantor::Expression* ScilabSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave) { kDebug() << "evaluating: " << cmd; ScilabExpression* expr = new ScilabExpression(this); changeStatus(Cantor::Session::Running); expr->setFinishingBehavior(behave); expr->setCommand(cmd); expr->evaluate(); return expr; } void ScilabSession::runExpression(ScilabExpression* expr) { QString command; command.prepend("\nprintf('begin-cantor-scilab-command-processing')\n"); command += expr->command(); m_currentExpression = expr; connect(m_currentExpression, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentExpressionStatusChanged(Cantor::Expression::Status))); command += "\nprintf('terminated-cantor-scilab-command-processing')\n"; kDebug() << "Writing command to process" << command; m_process->write(command.toLocal8Bit()); } void ScilabSession::expressionFinished() { kDebug()<<"finished"; ScilabExpression* expression = qobject_cast(sender()); m_runningExpressions.removeAll(expression); kDebug() << "size: " << m_runningExpressions.size(); } void ScilabSession::readError() { kDebug() << "readError"; QString error = m_process->readAllStandardError(); kDebug() << "error: " << error; m_currentExpression->parseError(error); } void ScilabSession::readOutput() { kDebug() << "readOutput"; while(m_process->bytesAvailable() > 0){ m_output.append(QString::fromLocal8Bit(m_process->readLine())); } kDebug() << "output.isNull? " << m_output.isNull(); kDebug() << "output: " << m_output; if(status() != Running || m_output.isNull()){ return; } if(m_output.contains("begin-cantor-scilab-command-processing") && m_output.contains("terminated-cantor-scilab-command-processing")){ m_output.remove("begin-cantor-scilab-command-processing"); m_output.remove("terminated-cantor-scilab-command-processing"); m_currentExpression->parseOutput(m_output); m_output.clear(); } } void ScilabSession::plotFileChanged(QString filename) { kDebug() << "plotFileChanged filename:" << filename; if ((m_currentExpression) && (filename.contains("cantor-export-scilab-figure"))) { kDebug() << "Calling parsePlotFile"; m_currentExpression->parsePlotFile(filename); m_listPlotName.append(filename); } } void ScilabSession::currentExpressionStatusChanged(Cantor::Expression::Status status) { kDebug() << "currentExpressionStatusChanged: " << status; switch (status) { case Cantor::Expression::Computing: break; case Cantor::Expression::Interrupted: break; case Cantor::Expression::Done: case Cantor::Expression::Error: changeStatus(Done); break; } } -QSyntaxHighlighter* ScilabSession::syntaxHighlighter(QTextEdit* parent) +QSyntaxHighlighter* ScilabSession::syntaxHighlighter(QObject* parent) { return new ScilabHighlighter(parent); } Cantor::CompletionObject* ScilabSession::completionFor(const QString& command, int index) { return new ScilabCompletionObject(command, index, this); } #include "scilabsession.moc" diff --git a/src/backends/scilab/scilabsession.h b/src/backends/scilab/scilabsession.h index b82942b7..93a5cf1f 100644 --- a/src/backends/scilab/scilabsession.h +++ b/src/backends/scilab/scilabsession.h @@ -1,69 +1,69 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2011 Filipe Saraiva */ #ifndef _SCILABSESSION_H #define _SCILABSESSION_H #include "session.h" #include class ScilabExpression; class KTemporaryFile; class KDirWatch; class KProcess; class ScilabSession : public Cantor::Session { Q_OBJECT public: ScilabSession( Cantor::Backend* backend); ~ScilabSession(); void login(); void logout(); void interrupt(); void runExpression(ScilabExpression* expr); - QSyntaxHighlighter* syntaxHighlighter(QTextEdit* parent); + QSyntaxHighlighter* syntaxHighlighter(QObject* parent); Cantor::Expression* evaluateExpression(const QString& command, Cantor::Expression::FinishingBehavior behave); Cantor::CompletionObject* completionFor(const QString& command, int index=-1); public slots: void readOutput(); void readError(); void plotFileChanged(QString filename); private: KProcess* m_process; KDirWatch* m_watch; QStringList m_listPlotName; QString m_output; QList m_runningExpressions; ScilabExpression* m_currentExpression; private slots: void expressionFinished(); void currentExpressionStatusChanged(Cantor::Expression::Status status); }; #endif /* _SCILABSESSION_H */ diff --git a/src/cantor.cpp b/src/cantor.cpp index 63f87500..62321b55 100644 --- a/src/cantor.cpp +++ b/src/cantor.cpp @@ -1,508 +1,510 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #include "cantor.h" #include "cantor.moc" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/backend.h" #include "lib/panelpluginhandler.h" #include "lib/panelplugin.h" #include "settings.h" #include "ui_settings.h" #include "backendchoosedialog.h" CantorShell::CantorShell() : KParts::MainWindow( ) { m_part=0; // set the shell's ui resource file setXMLFile("cantor_shell.rc"); // then, setup our actions setupActions(); createGUI(0); m_tabWidget=new KTabWidget(this); m_tabWidget->setTabsClosable(true); m_tabWidget->setMovable(true); m_tabWidget->setDocumentMode(true); setCentralWidget(m_tabWidget); connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(activateWorksheet(int))); connect(m_tabWidget, SIGNAL(closeRequest (QWidget *)), this, SLOT(closeTab(QWidget*))); // apply the saved mainwindow settings, if any, and ask the mainwindow // to automatically save settings if changed: window size, toolbar // position, icon size, etc. setAutoSaveSettings(); setDockOptions(QMainWindow::AnimatedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs); } CantorShell::~CantorShell() { } void CantorShell::load(const KUrl& url) { if (!m_part||!m_part->url().isEmpty() || m_part->isModified() ) { addWorksheet("null"); m_tabWidget->setCurrentIndex(m_parts.size()-1); } m_part->openUrl( url ); } bool CantorShell::hasAvailableBackend() { bool hasBackend=false; foreach(Cantor::Backend* b, Cantor::Backend::availableBackends()) { if(b->isEnabled()) hasBackend=true; } return hasBackend; } void CantorShell::setupActions() { - KStandardAction::openNew(this, SLOT(fileNew()), actionCollection()); - KStandardAction::open(this, SLOT(fileOpen()), actionCollection()); + KAction* openNew = KStandardAction::openNew(this, SLOT(fileNew()), actionCollection()); + openNew->setPriority(QAction::LowPriority); + KAction* open = KStandardAction::open(this, SLOT(fileOpen()), actionCollection()); + open->setPriority(QAction::LowPriority); KStandardAction::close (this, SLOT(closeTab()), actionCollection()); KStandardAction::quit(qApp, SLOT(closeAllWindows()), actionCollection()); createStandardStatusBarAction(); //setStandardToolBarMenuEnabled(true); KStandardAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection()); KStandardAction::configureToolbars(this, SLOT(configureToolbars()), actionCollection()); KStandardAction::preferences(this, SLOT(showSettings()), actionCollection()); KAction* downloadExamples = new KAction(i18n("Download Example Worksheets"), actionCollection()); downloadExamples->setIcon(KIcon("get-hot-new-stuff")); actionCollection()->addAction("file_example_download", downloadExamples); connect(downloadExamples, SIGNAL(triggered()), this, SLOT(downloadExamples())); KAction* openExample =new KAction(i18n("&Open Example"), actionCollection()); openExample->setIcon(KIcon("document-open")); actionCollection()->addAction("file_example_open", openExample); connect(openExample, SIGNAL(triggered()), this, SLOT(openExample())); } void CantorShell::saveProperties(KConfigGroup & /*config*/) { // the 'config' object points to the session managed // config file. anything you write here will be available // later when this app is restored } void CantorShell::readProperties(const KConfigGroup & /*config*/) { // the 'config' object points to the session managed // config file. this function is automatically called whenever // the app is being restored. read in here whatever you wrote // in 'saveProperties' } void CantorShell::fileNew() { if (sender()->inherits("KAction")) { KAction* a = qobject_cast(sender()); QString backendName = a->data().toString(); if (!backendName.isEmpty()) { addWorksheet(backendName); return; } } addWorksheet(); } void CantorShell::optionsConfigureKeys() { KShortcutsDialog dlg( KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed, this ); dlg.addCollection( actionCollection(), i18n("Cantor") ); dlg.addCollection( m_part->actionCollection(), i18n("Cantor") ); dlg.configure( true ); } void CantorShell::fileOpen() { // this slot is called whenever the File->Open menu is selected, // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar // button is clicked KUrl url = KFileDialog::getOpenUrl( KUrl(), i18n("*.cws|Cantor Worksheet"), this ); if (url.isEmpty() == false) { // About this function, the style guide ( // http://developer.kde.org/documentation/standards/kde/style/basics/index.html ) // says that it should open a new window if the document is _not_ // in its initial state. This is what we do here.. /*if ( m_part->url().isEmpty() && ! m_part->isModified() ) { // we open the file in this window... load( url ); } else { // we open the file in a new window... CantorShell* newWin = new CantorShell; newWin->load( url ); newWin->show(); }*/ load( url ); } } void CantorShell::addWorksheet() { if(hasAvailableBackend()) //There is no point in asking for the backend, if no one is available { QString backend = Settings::self()->defaultBackend(); if (backend.isEmpty()) { QPointer dlg=new BackendChooseDialog(this); if(dlg->exec()) { backend = dlg->backendName(); addWorksheet(backend); } delete dlg; } else { addWorksheet(backend); } }else { KTextBrowser *browser=new KTextBrowser(this); QString backendList="
    "; int backendListSize = 0; foreach(Cantor::Backend* b, Cantor::Backend::availableBackends()) { if(!b->requirementsFullfilled()) //It's disabled because of misssing dependencies, not because of some other reason(like eg. nullbackend) { backendList+=QString("
  • %1: %2
  • ").arg(b->name(), b->url()); ++backendListSize; } } browser->setHtml(i18np("

    No Backend Found

    \n" \ "
    You could try:\n" \ "
      " \ "
    • Changing the settings in the config dialog;
    • " \ "
    • Installing packages for the following program:
    • " \ " %2 " \ "
    " \ "
    " , "

    No Backend Found

    \n" \ "
    You could try:\n" \ "
      " \ "
    • Changing the settings in the config dialog;
    • " \ "
    • Installing packages for one of the following programs:
    • " \ " %2 " \ "
    " \ "
    " , backendListSize, backendList )); browser->setObjectName("ErrorMessage"); m_tabWidget->addTab(browser, i18n("Error")); } } void CantorShell::addWorksheet(const QString& backendName) { static int sessionCount=1; // this routine will find and load our Part. it finds the Part by // name which is a bad idea usually.. but it's alright in this // case since our Part is made for this Shell KPluginFactory* factory = KPluginLoader("libcantorpart").factory(); if (factory) { // now that the Part is loaded, we cast it to a Part to get // our hands on it KParts::ReadWritePart* part = factory->create(m_tabWidget, QVariantList()<addTab(part->widget(), i18n("Session %1", sessionCount++)); m_tabWidget->setCurrentIndex(tab); } else { kDebug()<<"error creating part "; } } else { // if we couldn't find our Part, we exit since the Shell by // itself can't do anything useful KMessageBox::error(this, i18n("Could not find the Cantor Part.")); qApp->quit(); // we return here, cause qApp->quit() only means "exit the // next time we enter the event loop... return; } } void CantorShell::activateWorksheet(int index) { QObject* pluginHandler=m_part->findChild("PanelPluginHandler"); disconnect(pluginHandler,SIGNAL(pluginsChanged()), this, SLOT(updatePanel())); m_part=findPart(m_tabWidget->widget(index)); if(m_part) { createGUI(m_part); QObject* pluginHandler=m_part->findChild("PanelPluginHandler"); connect(pluginHandler, SIGNAL(pluginsChanged()), this, SLOT(updatePanel())); updatePanel(); } else kDebug()<<"selected part doesn't exist"; m_tabWidget->setCurrentIndex(index); } void CantorShell::setTabCaption(const QString& caption) { if (caption.isEmpty()) return; KParts::ReadWritePart* part=dynamic_cast(sender()); m_tabWidget->setTabText(m_tabWidget->indexOf(part->widget()), caption); } void CantorShell::closeTab(QWidget* widget) { if(widget==0) { if(m_part!=0) widget=m_part->widget(); else return; } int index=m_tabWidget->indexOf(widget); m_tabWidget->removeTab(index); if(widget->objectName()=="ErrorMessage") { widget->deleteLater(); }else { KParts::ReadWritePart* part= findPart(widget); if(part) { m_parts.removeAll(part); delete part; } } } void CantorShell::showSettings() { KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self()); QWidget *generalSettings = new QWidget; Ui::SettingsBase base; base.setupUi(generalSettings); base.kcfg_DefaultBackend->addItems(Cantor::Backend::listAvailableBackends()); dialog->addPage(generalSettings, i18n("General"), "preferences-other"); foreach(Cantor::Backend* backend, Cantor::Backend::availableBackends()) { if (backend->config()) //It has something to configure, so add it to the dialog dialog->addPage(backend->settingsWidget(dialog), backend->config(), backend->name(), backend->icon()); } dialog->show(); } void CantorShell::downloadExamples() { KNS3::DownloadDialog dialog; dialog.exec(); foreach (const KNS3::Entry& e, dialog.changedEntries()) { kDebug() << "Changed Entry: " << e.name(); } } void CantorShell::openExample() { QString dir = KStandardDirs::locateLocal("appdata", "examples"); if (dir.isEmpty()) return; KStandardDirs::makeDir(dir); QStringList files=QDir(dir).entryList(QDir::Files); QPointer dlg=new KDialog(this); QListWidget* list=new QListWidget(dlg); foreach(const QString& file, files) { QString name=file; name.remove(QRegExp("-.*\\.hotstuff-access$")); list->addItem(name); } dlg->setMainWidget(list); if (dlg->exec()==QDialog::Accepted&&list->currentRow()>=0) { const QString& selectedFile=files[list->currentRow()]; KUrl url; url.setDirectory(dir); url.setFileName(selectedFile); kDebug()<<"loading file "<widget()==widget) return part; } return 0; } void CantorShell::updatePanel() { kDebug()<<"updating panels"; unplugActionList("view_show_panels"); //remove all of the previous panels (but do not delete the widgets) foreach(QDockWidget* dock, m_panels) { QWidget* widget=dock->widget(); if(widget!=0) { widget->setParent(this); widget->hide(); } dock->deleteLater(); } m_panels.clear(); QList panelActions; Cantor::PanelPluginHandler* handler=m_part->findChild("PanelPluginHandler"); if(!handler) { kDebug()<<"no PanelPluginHandle found for this part"; return; } QDockWidget* last=0; QList plugins=handler->plugins(); foreach(Cantor::PanelPlugin* plugin, plugins) { if(plugin==0) { kDebug()<<"somethings wrong"; continue; } kDebug()<<"adding panel for "<name(); plugin->setParentWidget(this); QDockWidget* docker=new QDockWidget(plugin->name(), this); docker->setObjectName(plugin->name()); docker->setWidget(plugin->widget()); addDockWidget ( Qt::RightDockWidgetArea, docker ); if(last!=0) tabifyDockWidget(last, docker); last=docker; m_panels.append(docker); //Create the action to show/hide this panel panelActions<toggleViewAction(); } plugActionList("view_show_panel_list", panelActions); unplugActionList("new_worksheet_with_backend_list"); QList newBackendActions; foreach (Cantor::Backend* backend, Cantor::Backend::availableBackends()) { if (!backend->isEnabled()) continue; KAction* action = new KAction(KIcon(backend->icon()), backend->name(), 0); action->setData(backend->name()); connect(action, SIGNAL(triggered()), this, SLOT(fileNew())); newBackendActions << action; } plugActionList("new_worksheet_with_backend_list", newBackendActions); } diff --git a/src/cantor.kcfg b/src/cantor.kcfg index 1dca74a6..9698efb8 100644 --- a/src/cantor.kcfg +++ b/src/cantor.kcfg @@ -1,42 +1,38 @@ - - - - - - - PopupCompletion - true true true false + + + true + false diff --git a/src/cantor_part.cpp b/src/cantor_part.cpp index e46143ac..98e4e4d8 100644 --- a/src/cantor_part.cpp +++ b/src/cantor_part.cpp @@ -1,661 +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(); - m_worksheet=new Worksheet(b, parentWidget); - m_worksheet->setEnabled(false); //disable input until the session has successfully logged in and emits the ready signal + 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(m_worksheet); + setWidget(widget); // create our actions - m_worksheet->createActions( actionCollection() ); + 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())); - KStandardAction::print(this, SLOT(print()), actionCollection()); + KAction* print = KStandardAction::print(this, SLOT(print()), actionCollection()); + print->setPriority(QAction::LowPriority); - KStandardAction::zoomIn(m_worksheet, SLOT(zoomIn()), actionCollection()); - KStandardAction::zoomOut(m_worksheet, SLOT(zoomOut()), actionCollection()); + KStandardAction::zoomIn(m_worksheetview, SLOT(zoomIn()), actionCollection()); + KStandardAction::zoomOut(m_worksheetview, SLOT(zoomOut()), actionCollection()); m_evaluate=new KAction(i18n("Evaluate Worksheet"), actionCollection()); actionCollection()->addAction("evaluate_worksheet", m_evaluate); m_evaluate->setIcon(KIcon("system-run")); connect(m_evaluate, SIGNAL(triggered()), this, SLOT(evaluateOrInterrupt())); m_typeset=new KToggleAction(i18n("Typeset using LaTeX"), actionCollection()); m_typeset->setChecked(Settings::self()->typesetDefault()); actionCollection()->addAction("enable_typesetting", m_typeset); connect(m_typeset, SIGNAL(toggled(bool)), this, SLOT(enableTypesetting(bool))); m_highlight=new KToggleAction(i18n("Syntax Highlighting"), actionCollection()); m_highlight->setChecked(Settings::self()->highlightDefault()); actionCollection()->addAction("enable_highlighting", m_highlight); connect(m_highlight, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableHighlighting(bool))); m_completion=new KToggleAction(i18n("Completion"), actionCollection()); m_completion->setChecked(Settings::self()->completionDefault()); actionCollection()->addAction("enable_completion", m_completion); connect(m_completion, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableCompletion(bool))); m_exprNumbering=new KToggleAction(i18n("Line Numbers"), actionCollection()); m_exprNumbering->setChecked(Settings::self()->expressionNumberingDefault()); actionCollection()->addAction("enable_expression_numbers", m_exprNumbering); connect(m_exprNumbering, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableExpressionNumbering(bool))); + m_animateWorksheet=new KToggleAction(i18n("Animate Worksheet"), actionCollection()); + m_animateWorksheet->setChecked(Settings::self()->animationDefault()); + actionCollection()->addAction("enable_animations", m_animateWorksheet); + connect(m_animateWorksheet, SIGNAL(toggled(bool)), m_worksheet, SLOT(enableAnimations(bool))); + KAction* restart=new KAction(i18n("Restart Backend"), actionCollection()); actionCollection()->addAction("restart_backend", restart); restart->setIcon(KIcon("system-reboot")); connect(restart, SIGNAL(triggered()), this, SLOT(restartBackend())); KAction* evaluateCurrent=new KAction(i18n("Evaluate Entry"), actionCollection()); evaluateCurrent->setShortcut(Qt::SHIFT + Qt::Key_Return); actionCollection()->addAction("evaluate_current", evaluateCurrent); connect(evaluateCurrent, SIGNAL(triggered()), m_worksheet, SLOT(evaluateCurrentEntry())); KAction* insertCommandEntry=new KAction(i18n("Insert Command Entry"), actionCollection()); insertCommandEntry->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_command_entry", insertCommandEntry); connect(insertCommandEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertCommandEntry())); KAction* insertTextEntry=new KAction(i18n("Insert Text Entry"), actionCollection()); //insertEntry->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_text_entry", insertTextEntry); connect(insertTextEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertTextEntry())); + KAction* insertLatexEntry=new KAction(i18n("Insert Latex Entry"), actionCollection()); + //insertEntry->setShortcut(Qt::CTRL + Qt::Key_Return); + actionCollection()->addAction("insert_latex_entry", insertLatexEntry); + connect(insertLatexEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertLatexEntry())); + KAction* insertPageBreakEntry=new KAction(i18n("Insert Page Break"), actionCollection()); actionCollection()->addAction("insert_page_break_entry", insertPageBreakEntry); connect(insertPageBreakEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertPageBreakEntry())); KAction* insertImageEntry=new KAction(i18n("Insert Image"), actionCollection()); actionCollection()->addAction("insert_image_entry", insertImageEntry); connect(insertImageEntry, SIGNAL(triggered()), m_worksheet, SLOT(insertImageEntry())); + /* KAction* insertCommandEntryBefore=new KAction(i18n("Insert Command Entry Before"), actionCollection()); //insertCommandEntryBefore->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_command_entry_before", insertCommandEntryBefore); connect(insertCommandEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertCommandEntryBefore())); KAction* insertTextEntryBefore=new KAction(i18n("Insert Text Entry Before"), actionCollection()); //insertTextEntryBefore->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_text_entry_before", insertTextEntryBefore); connect(insertTextEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertTextEntryBefore())); KAction* insertPageBreakEntryBefore=new KAction(i18n("Insert Page Break Before"), actionCollection()); actionCollection()->addAction("insert_page_break_entry_before", insertPageBreakEntryBefore); connect(insertPageBreakEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertPageBreakEntryBefore())); KAction* insertImageEntryBefore=new KAction(i18n("Insert Image Entry Before"), actionCollection()); //insertTextEntryBefore->setShortcut(Qt::CTRL + Qt::Key_Return); actionCollection()->addAction("insert_image_entry_before", insertImageEntryBefore); connect(insertImageEntryBefore, SIGNAL(triggered()), m_worksheet, SLOT(insertImageEntryBefore())); */ KAction* removeCurrent=new KAction(i18n("Remove current Entry"), actionCollection()); removeCurrent->setShortcut(Qt::ShiftModifier + Qt::Key_Delete); actionCollection()->addAction("remove_current", removeCurrent); connect(removeCurrent, SIGNAL(triggered()), m_worksheet, SLOT(removeCurrentEntry())); m_showBackendHelp=new KAction(i18n("Show %1 Help", b->name()) , actionCollection()); m_showBackendHelp->setIcon(KIcon("help-contents")); actionCollection()->addAction("backend_help", m_showBackendHelp); connect(m_showBackendHelp, SIGNAL(triggered()), this, SLOT(showBackendHelp())); KAction* publishWorksheet=new KAction(i18n("Publish Worksheet"), actionCollection()); publishWorksheet->setIcon(KIcon("get-hot-new-stuff")); actionCollection()->addAction("file_publish_worksheet", publishWorksheet); connect(publishWorksheet, SIGNAL(triggered()), this, SLOT(publishWorksheet())); KToggleAction* showEditor=new KToggleAction(i18n("Show Script Editor"), actionCollection()); showEditor->setChecked(false); actionCollection()->addAction("show_editor", showEditor); connect(showEditor, SIGNAL(toggled(bool)), this, SLOT(showScriptEditor(bool))); showEditor->setEnabled(b->extensions().contains("ScriptExtension")); KAction* showCompletion=new KAction(i18n("Show Completion"), actionCollection()); actionCollection()->addAction("show_completion", showCompletion); showCompletion->setShortcut(Qt::Key_Tab); //Qt::CTRL + Qt::Key_Space); connect(showCompletion, SIGNAL(triggered()), m_worksheet, SLOT(showCompletion())); // set our XML-UI resource file setXMLFile("cantor_part.rc"); // we are read-write by default setReadWrite(true); // we are not modified since we haven't done anything yet setModified(false); worksheetSessionChanged(); } CantorPart::~CantorPart() { if (m_scriptEditor) { disconnect(m_scriptEditor, SIGNAL(destroyed()), this, SLOT(scriptEditorClosed())); delete m_scriptEditor; } + if (m_searchBar) + delete m_searchBar; } void CantorPart::setReadWrite(bool rw) { // notify your internal widget of the read-write state - m_worksheet->setReadOnly(!rw); + m_worksheetview->setInteractive(rw); ReadWritePart::setReadWrite(rw); } void CantorPart::setModified(bool modified) { // get a handle on our Save action and make sure it is valid if (!m_save) return; // if so, we either enable or disable it based on the current // state if (modified) m_save->setEnabled(true); else m_save->setEnabled(false); // in any event, we want our parent to do it's thing ReadWritePart::setModified(modified); } KAboutData *CantorPart::createAboutData() { // the non-i18n name here must be the same as the directory in // which the part's rc file is installed ('partrcdir' in the // Makefile) KAboutData *aboutData = new KAboutData("cantorpart", "cantor", ki18n("CantorPart"), "0.3"); aboutData->addAuthor(ki18n("Alexander Rieder"), KLocalizedString(), "alexanderrieder@gmail.com"); return aboutData; } bool CantorPart::openFile() { //don't crash if for some reason the worksheet is invalid if(m_worksheet==0) { kWarning()<<"trying to open in an invalid cantor part"; return false; } m_worksheet->load(localFilePath()); // just for fun, set the status bar //setStatusMessage( m_url.prettyUrl() ); updateCaption(); return true; } bool CantorPart::saveFile() { // if we aren't read-write, return immediately if (isReadWrite() == false) return false; kDebug()<<"saving to: "<save( localFilePath() ); - else - m_worksheet->savePlain( localFilePath()); - - } + m_worksheet->save( localFilePath() ); setModified(false); return true; } void CantorPart::fileSaveAs() { // this slot is called whenever the File->Save As menu is selected, QString filter=i18n("*.cws|Cantor Worksheet"); //if the backend supports scripts, also append their scriptFile endings to the filter Cantor::Backend * const backend=m_worksheet->session()->backend(); if (backend->extensions().contains("ScriptExtension")) { Cantor::ScriptExtension* e=dynamic_cast(backend->extension("ScriptExtension")); filter+='\n'+e->scriptFileFilter(); } QString file_name = KFileDialog::getSaveFileName(KUrl(), filter, widget()); - if (file_name.isEmpty() == false) + if (!file_name.isEmpty()) { + if (!file_name.endsWith(QLatin1String(".cws")) && + !file_name.endsWith(QLatin1String(".mws"))) + file_name += ".cws"; saveAs(file_name); + } updateCaption(); } +void CantorPart::fileSavePlain() +{ + QString file_name = KFileDialog::getSaveFileName(KUrl(), "", widget()); + if (!file_name.isEmpty()) + m_worksheet->savePlain(file_name); +} + void CantorPart::exportToLatex() { // this slot is called whenever the File->Save As menu is selected, QString filter=i18n("*.tex|LaTeX Document"); QString file_name = KFileDialog::getSaveFileName(KUrl(), filter, widget()); if (file_name.isEmpty() == false) { int exportImages=KMessageBox::questionYesNo(widget(), i18n("Do you also want to export the images?"), i18n("Question - Cantor")); m_worksheet->saveLatex( file_name , exportImages==KMessageBox::Yes); } } void CantorPart::guiActivateEvent( KParts::GUIActivateEvent * event ) { KParts::ReadWritePart::guiActivateEvent(event); if(event->activated()) { if(m_scriptEditor) m_scriptEditor->show(); }else { if(m_scriptEditor) m_scriptEditor->hide(); } } void CantorPart::evaluateOrInterrupt() { kDebug()<<"evalorinterrupt"; if(m_worksheet->isRunning()) m_worksheet->interrupt(); else m_worksheet->evaluate(); } void CantorPart::restartBackend() { m_worksheet->session()->logout(); m_worksheet->session()->login(); } void CantorPart::worksheetStatusChanged(Cantor::Session::Status status) { kDebug()<<"wsStatusChange"<setText(i18n("Interrupt")); m_evaluate->setIcon(KIcon("dialog-close")); setStatusMessage(i18n("Calculating...")); }else { m_evaluate->setText(i18n("Evaluate Worksheet")); m_evaluate->setIcon(KIcon("system-run")); setStatusMessage(i18n("Ready")); } } void CantorPart::showSessionError(const QString& message) { kDebug()<<"Error: "<session(), SIGNAL(statusChanged(Cantor::Session::Status)), this, SLOT(worksheetStatusChanged(Cantor::Session::Status))); connect(m_worksheet->session(), SIGNAL(ready()),this, SLOT(initialized())); connect(m_worksheet->session(), SIGNAL(error(const QString&)), this, SLOT(showSessionError(const QString&))); loadAssistants(); m_panelHandler->setSession(m_worksheet->session()); adjustGuiToSession(); if(!m_initProgressDlg) { m_initProgressDlg=new KProgressDialog(widget(), i18n("Cantor"), i18n("Initializing Session")); m_initProgressDlg->setMinimumDuration(500); m_initProgressDlg->progressBar()->setRange(0, 0); } } void CantorPart::initialized() { - m_worksheet->appendCommandEntry(); - m_worksheet->setEnabled(true); - m_worksheet->setFocus(); + 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()); - if (m_worksheet->textCursor().hasSelection()) - dialog->addEnabledOption(QAbstractPrintDialog::PrintSelection); + // TODO: Re-enable print selection + //if (m_worksheet->textCursor().hasSelection()) + // dialog->addEnabledOption(QAbstractPrintDialog::PrintSelection); if (dialog->exec() == QDialog::Accepted) m_worksheet->print(&printer); delete dialog; } void CantorPart::showScriptEditor(bool show) { if(show) { if (m_scriptEditor) { return; } Cantor::ScriptExtension* scriptE=dynamic_cast(m_worksheet->session()->backend()->extension("ScriptExtension")); if (!scriptE) { return; } m_scriptEditor=new ScriptEditorWidget(scriptE->scriptFileFilter(), widget()->window()); connect(m_scriptEditor, SIGNAL(runScript(const QString&)), this, SLOT(runScript(const QString&))); connect(m_scriptEditor, SIGNAL(destroyed()), this, SLOT(scriptEditorClosed())); m_scriptEditor->show(); }else { delete m_scriptEditor; } } void CantorPart::scriptEditorClosed() { QAction* showEditor = actionCollection()->action("show_editor"); if (showEditor) { showEditor->setChecked(false); } } void CantorPart::runScript(const QString& file) { Cantor::Backend* backend=m_worksheet->session()->backend(); if(!backend->extensions().contains("ScriptExtension")) { KMessageBox::error(widget(), i18n("This backend does not support scripts."), i18n("Error - Cantor")); return; } Cantor::ScriptExtension* scriptE=dynamic_cast(backend->extension("ScriptExtension")); m_worksheet->appendCommandEntry(scriptE->runExternalScript(file)); } void CantorPart::blockStatusBar() { m_statusBarBlocked=true; } void CantorPart::unblockStatusBar() { m_statusBarBlocked=false; if(!m_cachedStatusMessage.isNull()) setStatusMessage(m_cachedStatusMessage); m_cachedStatusMessage.clear(); } void CantorPart::setStatusMessage(const QString& message) { if(!m_statusBarBlocked) emit setStatusBarText(message); else m_cachedStatusMessage=message; } void CantorPart::showImportantStatusMessage(const QString& message) { setStatusMessage(message); blockStatusBar(); QTimer::singleShot(3000, this, SLOT(unblockStatusBar())); } diff --git a/src/cantor_part.h b/src/cantor_part.h index 3786d31c..228b49dc 100644 --- a/src/cantor_part.h +++ b/src/cantor_part.h @@ -1,159 +1,174 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #ifndef CANTORPART_H #define CANTORPART_H #include #include class QWidget; class Worksheet; +class WorksheetView; +class SarchBar; +class SearchBar; class ScriptEditorWidget; class KAboutData; class KAction; class KToggleAction; class KProgressDialog; namespace Cantor{ class PanelPluginHandler; } /** * This is a "Part". It that does all the real work in a KPart * application. * * @short Main Part * @author Alexander Rieder */ class CantorPart : public KParts::ReadWritePart { Q_OBJECT public: /** * Default constructor */ CantorPart(QWidget *parentWidget,QObject *parent, const QVariantList &args); /** * Destructor */ virtual ~CantorPart(); /** * This is a virtual function inherited from KParts::ReadWritePart. * A shell will use this to inform this Part if it should act * read-only */ virtual void setReadWrite(bool rw); /** * Reimplemented to disable and enable Save action */ virtual void setModified(bool modified); static KAboutData *createAboutData(); Worksheet* worksheet(); signals: void setCaption(const QString& caption); void showHelp(const QString& help); protected: /** * This must be implemented by each part */ virtual bool openFile(); /** * This must be implemented by each read-write part */ virtual bool saveFile(); /** * Called when this part becomes the active one, * or if it looses activity **/ void guiActivateEvent( KParts::GUIActivateEvent * event ); void loadAssistants(); void adjustGuiToSession(); protected slots: void fileSaveAs(); + void fileSavePlain(); void exportToLatex(); void evaluateOrInterrupt(); void restartBackend(); void enableTypesetting(bool enable); void showBackendHelp(); void print(); void worksheetStatusChanged(Cantor::Session::Status stauts); void showSessionError(const QString& error); void worksheetSessionChanged(); void initialized(); void updateCaption(); void pluginsChanged(); void runCommand(const QString& value); void runAssistant(); void publishWorksheet(); void showScriptEditor(bool show); void scriptEditorClosed(); void runScript(const QString& file); + void showSearchBar(); + void showExtendedSearchBar(); + void findNext(); + void findPrev(); + void searchBarDeleted(); + /** sets the status message, or cached it, if the StatusBar is blocked. * Use this method instead of "emit setStatusBarText" */ void setStatusMessage(const QString& message); /** Shows an important status message. It makes sure the message is displayed, * by blocking the statusbarText for 3 seconds */ void showImportantStatusMessage(const QString& message); /** Blocks the StatusBar for new messages, so the currently shown one won't be overridden */ void blockStatusBar(); /** Removes the block from the StatusBar, and shows the last one of the StatusMessages that where set during the block **/ void unblockStatusBar(); private: Worksheet *m_worksheet; + WorksheetView *m_worksheetview; + SearchBar *m_searchBar; QPointer m_scriptEditor; Cantor::PanelPluginHandler* m_panelHandler; KProgressDialog* m_initProgressDlg; KAction* m_evaluate; KAction* m_save; + KAction* m_findNext; + KAction* m_findPrev; KToggleAction* m_typeset; KToggleAction* m_highlight; KToggleAction* m_completion; KToggleAction* m_exprNumbering; + KToggleAction* m_animateWorksheet; KAction* m_showBackendHelp; QString m_cachedStatusMessage; bool m_statusBarBlocked; }; #endif // CANTORPART_H diff --git a/src/cantor_part.rc b/src/cantor_part.rc index 0152c85a..3fc126a6 100644 --- a/src/cantor_part.rc +++ b/src/cantor_part.rc @@ -1,71 +1,78 @@ + + &Edit + + + + + &View - - - - - - &Worksheet - + + - - - - + + + + + + + + + diff --git a/src/cantor_shell.rc b/src/cantor_shell.rc index 2b54b994..6ed96ce3 100644 --- a/src/cantor_shell.rc +++ b/src/cantor_shell.rc @@ -1,45 +1,44 @@ &File &New + &Edit + + &View Panels + &Settings - -Main Toolbar - - - diff --git a/src/commandentry.cpp b/src/commandentry.cpp index 402fb10b..f0d57534 100644 --- a/src/commandentry.cpp +++ b/src/commandentry.cpp @@ -1,999 +1,788 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder + Copyright (C) 2012 Martin Kuettler */ #include "commandentry.h" -#include "worksheetentry.h" - +#include "worksheet.h" +#include "worksheettextitem.h" +#include "resultitem.h" +#include "loadedexpression.h" +#include "settings.h" #include "lib/expression.h" #include "lib/result.h" #include "lib/helpresult.h" #include "lib/completionobject.h" #include "lib/syntaxhelpobject.h" #include "lib/defaulthighlighter.h" #include "lib/session.h" -#include "worksheet.h" -#include "resultproxy.h" -#include "resultcontextmenu.h" -#include "settings.h" -#include "loadedexpression.h" -#include -#include -#include #include -#include +#include +#include #include +#include + #include -#include -#include -#include #include -#include -#include +#include const QString CommandEntry::Prompt=">>> "; +const double CommandEntry::HorizontalSpacing = 4; +const double CommandEntry::VerticalSpacing = 4; -CommandEntry::CommandEntry( QTextCursor position, Worksheet* parent ) : WorksheetEntry( position, parent ) +CommandEntry::CommandEntry(Worksheet* worksheet) : WorksheetEntry(worksheet) { - m_expression=0; - m_completionObject=0; - m_syntaxHelpObject=0; - - connect(m_worksheet, SIGNAL(updatePrompt()), this, SLOT(updatePrompt())); - - QTextTableFormat tableFormat; - QVector constraints; - QFontMetrics metrics(parent->document()->defaultFont()); - constraints<< QTextLength(QTextLength::FixedLength, metrics.width(CommandEntry::Prompt)) - <setPlainText(Prompt); + m_promptItem->setItemDragable(true); + m_commandItem = new WorksheetTextItem(this, Qt::TextEditorInteraction); + m_commandItem->enableCompletion(true); + m_errorItem = 0; + m_resultItem = 0; + + connect(m_commandItem, SIGNAL(tabPressed()), this, SLOT(showCompletion())); + connect(m_commandItem, SIGNAL(backtabPressed()), + this, SLOT(selectPreviousCompletion())); + connect(m_commandItem, SIGNAL(applyCompletion()), + this, SLOT(applySelectedCompletion())); + connect(m_commandItem, SIGNAL(execute()), this, SLOT(evaluate())); + connect(m_commandItem, SIGNAL(moveToPrevious(int, qreal)), + this, SLOT(moveToPreviousItem(int, qreal))); + connect(m_commandItem, SIGNAL(moveToNext(int, qreal)), + this, SLOT(moveToNextItem(int, qreal))); + connect(m_commandItem, SIGNAL(receivedFocus(WorksheetTextItem*)), + worksheet, SLOT(highlightItem(WorksheetTextItem*))); + connect(m_promptItem, SIGNAL(drag(const QPointF&, const QPointF&)), + this, SLOT(startDrag(const QPointF&))); + connect(worksheet, SIGNAL(updatePrompt()), + this, SLOT(updatePrompt())); +} - tableFormat.setColumnWidthConstraints(constraints); - tableFormat.setBorderStyle(QTextFrameFormat::BorderStyle_None); - tableFormat.setCellSpacing(10); - tableFormat.setTopMargin(5); +CommandEntry::~CommandEntry() +{ + if (m_completionBox) + m_completionBox->deleteLater(); +} - position=m_frame->firstCursorPosition(); +int CommandEntry::type() const +{ + return Type; +} - m_table=position.insertTable(1, 2, tableFormat); - //make sure, everything is invalid, when the table gets removed - connect(m_table, SIGNAL(destroyed(QObject*)), this, SLOT(invalidate())); - //delete the worksheet entry, when the table gets removed from the worksheet - connect(m_table, SIGNAL(destroyed(QObject*)), this, SLOT(deleteLater())); +void CommandEntry::populateMenu(KMenu *menu, const QPointF& pos) +{ + kDebug() << "populate Menu"; + WorksheetEntry::populateMenu(menu, pos); +} - m_table->cellAt(0, 0).firstCursorPosition().insertText(Prompt); +void CommandEntry::moveToNextItem(int pos, qreal x) +{ + WorksheetTextItem* item = qobject_cast(sender()); - QTextCharFormat cmdF=m_table->cellAt(0, 1).format(); - cmdF.setProperty(Cantor::DefaultHighlighter::BlockTypeProperty, Cantor::DefaultHighlighter::CommandBlock); - m_table->cellAt(0, 1).setFormat(cmdF); + if (!item) + return; - //m_table->mergeCells(0, 1, 1, 2); - m_commandCell=m_table->cellAt(0, 1); + if (item == m_commandItem) { + if (m_informationItems.isEmpty() || + !currentInformationItem()->isEditable()) + moveToNextEntry(pos, x); + else + currentInformationItem()->setFocusAt(pos, x); + } else if (item == currentInformationItem()) { + moveToNextEntry(pos, x); + } } -CommandEntry::~CommandEntry() +void CommandEntry::moveToPreviousItem(int pos, qreal x) { - if(m_completionBox) - m_completionBox->deleteLater(); -} + WorksheetTextItem* item = qobject_cast(sender()); -int CommandEntry::type() -{ - return Type; + if (!item) + return; + + if (item == m_commandItem || item == 0) { + moveToPreviousEntry(pos, x); + } else if (item == currentInformationItem()) { + m_commandItem->setFocusAt(pos, x); + } } QString CommandEntry::command() { - QTextCursor c=m_commandCell.firstCursorPosition(); - c.setPosition(m_commandCell.lastCursorPosition().position(), QTextCursor::KeepAnchor); - QString cmd=c.selectedText(); + QString cmd = m_commandItem->toPlainText(); cmd.replace(QChar::ParagraphSeparator, '\n'); //Replace the U+2029 paragraph break by a Normal Newline cmd.replace(QChar::LineSeparator, '\n'); //Replace the line break by a Normal Newline - return cmd; } void CommandEntry::setExpression(Cantor::Expression* expr) { - if ( m_expression ) + /* + if ( m_expression ) { + if (m_expression->status() == Cantor::Expression::Computing) { + kDebug() << "OLD EXPRESSION STILL ACTIVE"; + m_expression->interrupt(); + } m_expression->deleteLater(); + }*/ - // Delete any previus error and/or result - if(m_errorCell.isValid()) + // Delete any previus error + if(m_errorItem) { - m_table->removeRows(m_errorCell.row(), 1); - m_errorCell=QTextTableCell(); + m_errorItem->deleteLater(); + m_errorItem = 0; } - removeResult(); - - foreach(const QTextTableCell& cell, m_informationCells) + foreach(WorksheetTextItem* item, m_informationItems) { - m_table->removeRows(cell.row()-1, 2); + item->deleteLater(); } - m_informationCells.clear(); + m_informationItems.clear(); + m_expression = 0; // Delete any previous result - if (m_table && m_resultCell.isValid()) - { - m_table->removeRows(m_resultCell.row(), m_resultCell.rowSpan()); - m_resultCell=QTextTableCell(); - } + removeResult(); m_expression=expr; - connect(expr, SIGNAL(gotResult()), this, SLOT(update())); + connect(expr, SIGNAL(gotResult()), this, SLOT(updateEntry())); connect(expr, SIGNAL(idChanged()), this, SLOT(updatePrompt())); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(expressionChangedStatus(Cantor::Expression::Status))); connect(expr, SIGNAL(needsAdditionalInformation(const QString&)), this, SLOT(showAdditionalInformationPrompt(const QString&))); connect(expr, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(updatePrompt())); updatePrompt(); if(expr->result()) { - m_worksheet->gotResult(expr); - update(); + worksheet()->gotResult(expr); + updateEntry(); } if(expr->status()!=Cantor::Expression::Computing) { expressionChangedStatus(expr->status()); } } Cantor::Expression* CommandEntry::expression() { return m_expression; } -QTextCursor CommandEntry::firstValidCursorPosition() -{ - return m_commandCell.firstCursorPosition(); -} - -QTextCursor CommandEntry::lastValidCursorPosition() -{ - return m_commandCell.lastCursorPosition(); -} - -QTextCursor CommandEntry::closestValidCursor(const QTextCursor& cursor) -{ - if (firstValidCursorPosition().position() <= cursor.position() && - cursor.position() <= lastValidCursorPosition().position()) - { - kDebug()<<"In CommandCell"; - return cursor; - } - else - return firstValidCursorPosition(); -} - -bool CommandEntry::isValidCursor(const QTextCursor& cursor) -{ - return isInCommandCell(cursor) - || isInCurrentInformationCell(cursor) - || isInResultCell(cursor) - || isInErrorCell(cursor); -} - -bool CommandEntry::worksheetShortcutOverrideEvent(QKeyEvent* event, const QTextCursor& cursor) -{ - if (WorksheetEntry::worksheetShortcutOverrideEvent(event, cursor)) - return true; - - if (event->key() == Qt::Key_Tab && m_worksheet->completionEnabled()) - { - // special tab handling here - // get the current line of the entry. If it's empty, do a regular tab(indent), - // otherwise check for tab completion (if supported by the backend) - const QString line=currentLine(cursor).trimmed(); - if(line.isEmpty()) - return false; - return true; - } - - return false; -} - -bool CommandEntry::worksheetKeyPressEvent(QKeyEvent* event, const QTextCursor& cursor) -{ - if (WorksheetEntry::worksheetKeyPressEvent(event, cursor)) - { - return true; - } - else if (event->modifiers() == Qt::NoModifier - && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) - && isShowingCompletionPopup()) - { - applySelectedCompletion(); - return true; - } - else if ((event->key() == Qt::Key_Tab) && m_worksheet->completionEnabled()) - { - if (event->modifiers() == Qt::NoModifier) { - showCompletion(); - return true; - } else if (event->modifiers() == Qt::ShiftModifier && isShowingCompletionPopup()) { - m_completionBox->up(); - return true; - } - } - else if (!(isInCommandCell(cursor) || isInCurrentInformationCell(cursor))) - { - return true; - } - - return false; -} - -bool CommandEntry::worksheetMousePressEvent(QMouseEvent* event, const QTextCursor& cursor) -{ - Q_UNUSED(event); - - if (!isValidCursor(cursor)) - return true; - - return false; -} - -bool CommandEntry::worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor) -{ - if(isInResultCell(cursor) && expression() && expression()->result()) - { - kDebug()<<"context menu in result..."; - KMenu* popup=new ResultContextMenu(this, m_worksheet); - - QMenu* defaultMenu=m_worksheet->mousePopupMenu(); - defaultMenu->setTitle(i18n("Other")); - popup->addMenu(defaultMenu); - - popup->popup(event->globalPos()); - - return true; - }else if(isInCommandCell(cursor)) - { - KMenu* defaultMenu = new KMenu(m_worksheet); - - defaultMenu->addAction(KStandardAction::cut(m_worksheet)); - defaultMenu->addAction(KStandardAction::copy(m_worksheet)); - defaultMenu->addAction(KStandardAction::paste(m_worksheet)); - defaultMenu->addSeparator(); - - if(!m_worksheet->isRunning()) - { - defaultMenu->addAction(KIcon("system-run"),i18n("Evaluate Worksheet"),m_worksheet,SLOT(evaluate()),0); - if (!isEmpty()) - defaultMenu->addAction(i18n("Evaluate Entry"),m_worksheet,SLOT(evaluateCurrentEntry()),0); - } - else - defaultMenu->addAction(KIcon("process-stop"),i18n("Interrupt"),m_worksheet,SLOT(interrupt()),0); - defaultMenu->addSeparator(); - - defaultMenu->addAction(KIcon("edit-delete"),i18n("Remove Entry"), m_worksheet, SLOT(removeCurrentEntry())); - - createSubMenuInsert(defaultMenu); - - defaultMenu->popup(event->globalPos()); - - return true; - } - return false; -} bool CommandEntry::acceptRichText() { return false; } -bool CommandEntry::acceptsDrop(const QTextCursor& cursor) -{ - return isInCommandCell(cursor); -} - void CommandEntry::setContent(const QString& content) { - firstValidCursorPosition().insertText(content); + m_commandItem->setPlainText(content); } void CommandEntry::setContent(const QDomElement& content, const KZip& file) { - firstValidCursorPosition().insertText(content.firstChildElement("Command").text()); + m_commandItem->setPlainText(content.firstChildElement("Command").text()); - LoadedExpression* expr=new LoadedExpression( m_worksheet->session() ); + LoadedExpression* expr=new LoadedExpression( worksheet()->session() ); expr->loadFromXml(content, file); setExpression(expr); } void CommandEntry::showCompletion() { + if (!worksheet()->completionEnabled()) + return; + //get the current line of the entry. If it's empty, ignore the call, //otherwise check for tab completion (if supported by the backend) - const QString line=currentLine(m_worksheet->textCursor()); //.trimmed(); + const QString line = currentLine(); if(line.trimmed().isEmpty()) { return; } else if (isShowingCompletionPopup()) { QString comp = m_completionObject->completion(); kDebug() << "command" << m_completionObject->command(); kDebug() << "completion" << comp; - if (comp != m_completionObject->command() + if (comp != m_completionObject->command() || !m_completionObject->hasMultipleMatches()) { if (m_completionObject->hasMultipleMatches()) { completeCommandTo(comp, PreliminaryCompletion); } else { completeCommandTo(comp, FinalCompletion); m_completionBox->hide(); } } else { m_completionBox->down(); } - } else - { - Cantor::CompletionObject* tco=m_worksheet->session()->completionFor(line, m_worksheet->textCursor().positionInBlock()); + } else { + int p = m_commandItem->textCursor().positionInBlock(); + Cantor::CompletionObject* tco=worksheet()->session()->completionFor(line, p); if(tco) setCompletion(tco); } } -QString CommandEntry::toPlain(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq) +void CommandEntry::selectPreviousCompletion() +{ + if (isShowingCompletionPopup()) + m_completionBox->up(); +} + +QString CommandEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) { Q_UNUSED(commentStartingSeq); Q_UNUSED(commentEndingSeq); if (command().isEmpty()) return QString(); return command() + commandSep; } QDomElement CommandEntry::toXml(QDomDocument& doc, KZip* archive) { if (expression()) { if ( archive ) expression()->saveAdditionalData( archive ); return expression()->toXml(doc); } QDomElement expr=doc.createElement( "Expression" ); QDomElement cmd=doc.createElement( "Command" ); QDomText cmdText=doc.createTextNode( command() ); cmd.appendChild( cmdText ); expr.appendChild( cmd ); return expr; } -QString CommandEntry::currentLine(const QTextCursor& cursor) +QString CommandEntry::currentLine() { - if(!isInCommandCell(cursor)) - return QString(); - - QTextBlock block=m_worksheet->document()->findBlock(cursor.position()); + if (!m_commandItem->hasFocus()) + return QString(); + QTextBlock block = m_commandItem->textCursor().block(); return block.text(); } -bool CommandEntry::evaluate(bool current) +bool CommandEntry::evaluateCurrentItem() { - if (!current) - return evaluateCommand(); - - const QTextCursor c=m_worksheet->textCursor(); - if (isInCommandCell(c)) - { - return evaluateCommand(); - }else if (isInCurrentInformationCell(c)) - { - addInformation(); - return true; + if (m_commandItem->hasFocus()) { + return evaluate(); + } else if (informationItemHasFocus()) { + addInformation(); + return true; } + return false; } -bool CommandEntry::evaluateCommand() +bool CommandEntry::evaluate(EvaluationOption evalOp) { removeContextHelp(); QToolTip::hideText(); QString cmd = command(); kDebug()<<"evaluating: "<deleteLater(); + } + m_informationItems.clear(); + recalculateSize(); + + evaluateNext(m_evaluationOption); return false; + } - expr=m_worksheet->session()->evaluateExpression(cmd); - connect(expr, SIGNAL(gotResult()), m_worksheet, SLOT(gotResult())); + Cantor::Expression* expr; + expr = worksheet()->session()->evaluateExpression(cmd); + connect(expr, SIGNAL(gotResult()), worksheet(), SLOT(gotResult())); setExpression(expr); return true; } void CommandEntry::interruptEvaluation() { - Cantor::Expression* expr=expression(); + Cantor::Expression *expr = expression(); if(expr) expr->interrupt(); } -void CommandEntry::update() +void CommandEntry::updateEntry() { - if (m_expression==0||m_expression->result()==0) //Don't crash if we don't have a result - return; + kDebug() << "update Entry"; + Cantor::Expression *expr = expression(); + if (expr == 0 || expr->result() == 0) + return; - if (m_expression->result()->type()==Cantor::HelpResult::Type) return; //Help is handled elsewhere + if (expr->result()->type() == Cantor::HelpResult::Type) + return; // Help is handled elsewhere - if(!m_resultCell.isValid()) - { - int row=0; - if(actualInformationCell().isValid()) - row=actualInformationCell().row()+1; - else - row=m_commandCell.row()+1; - m_table->insertRows(row, 1); - //m_table->mergeCells(row, 1, 1, 2); - m_resultCell=m_table->cellAt(row, 1); - QTextCharFormat resF=m_table->cellAt(0, 1).format(); - resF.setProperty(Cantor::DefaultHighlighter::BlockTypeProperty, Cantor::DefaultHighlighter::ResultBlock); - m_resultCell.setFormat(resF); + if (!m_resultItem) { + m_resultItem = ResultItem::create(this, expr->result()); + kDebug() << "new result"; + animateSizeChange(); + } else { + m_resultItem = m_resultItem->updateFromResult(expr->result()); + kDebug() << "update result"; + animateSizeChange(); } - - QTextBlockFormat block; - block.setAlignment(Qt::AlignJustify); - block.setProperty(Cantor::DefaultHighlighter::BlockTypeProperty, Cantor::DefaultHighlighter::ResultBlock); - QTextCursor cursor(m_resultCell.firstCursorPosition()); - cursor.setBlockFormat(block); - cursor.setPosition(m_resultCell.lastCursorPosition().position(), QTextCursor::KeepAnchor); - - kDebug()<<"setting cell to "<result()->toHtml(); - - m_worksheet->resultProxy()->insertResult(cursor, m_expression->result()); - - m_worksheet->ensureCursorVisible(); } void CommandEntry::expressionChangedStatus(Cantor::Expression::Status status) { QString text; switch (status) { - case Cantor::Expression::Error: - text=m_expression->errorMessage(); - break; - case Cantor::Expression::Interrupted: - text=i18n("Interrupted"); - break; + case Cantor::Expression::Error: + text = m_expression->errorMessage(); + break; + case Cantor::Expression::Interrupted: + text = i18n("Interrupted"); + break; + case Cantor::Expression::Done: + evaluateNext(m_evaluationOption); + m_evaluationOption = DoNothing; + return; + default: + return; } - if(text.isEmpty()) - return; + m_commandItem->setFocusAt(WorksheetTextItem::BottomRight, 0); - QTextCursor c; - if(!m_errorCell.isValid()) - { - int row; - if(actualInformationCell().isValid()) - row=actualInformationCell().row()+1; - else - row=commandCell().row()+1; - m_table->insertRows(row, 1); - m_errorCell=m_table->cellAt(row, 1); - QTextCharFormat errF=m_table->cellAt(0, 1).format(); - errF.setProperty(Cantor::DefaultHighlighter::BlockTypeProperty, Cantor::DefaultHighlighter::ErrorBlock); - m_errorCell.setFormat(errF); - - c=m_errorCell.firstCursorPosition(); - }else + if(!m_errorItem) { - c=m_errorCell.firstCursorPosition(); - c.setPosition(m_errorCell.lastCursorPosition().position(), QTextCursor::KeepAnchor); + m_errorItem = new WorksheetTextItem(this, Qt::TextSelectableByMouse); } - c.insertHtml(text); + m_errorItem->setHtml(text); + recalculateSize(); } bool CommandEntry::isEmpty() { - QTextCursor c=m_commandCell.firstCursorPosition(); - c.setPosition(m_commandCell.lastCursorPosition().position(), QTextCursor::KeepAnchor); - QString text=c.selectedText(); - if(m_resultCell.isValid()) - { - c=m_resultCell.firstCursorPosition(); - c.setPosition( m_resultCell.lastCursorPosition().position(), QTextCursor::KeepAnchor); - text+=c.selectedText(); + if (m_commandItem->toPlainText().trimmed().isEmpty()) { + if (m_resultItem) + return false; + return true; } - text.remove(QRegExp("[\n\t\r]")); - kDebug()<<"text: "<isEditable()) + item = currentInformationItem(); + else + item = m_commandItem; + + item->setFocusAt(pos, xCoord); + return true; } void CommandEntry::setCompletion(Cantor::CompletionObject* tc) { - if(m_completionObject) - removeContextHelp(); + if (m_completionObject) + removeContextHelp(); - m_completionObject=tc; + m_completionObject = tc; connect(tc, SIGNAL(done()), this, SLOT(showCompletions())); connect(tc, SIGNAL(lineDone(QString, int)), this, SLOT(completeLineTo(QString, int))); } void CommandEntry::showCompletions() { disconnect(m_completionObject, SIGNAL(done()), this, SLOT(showCompletions())); - QString completion=m_completionObject->completion(); + QString completion = m_completionObject->completion(); kDebug()<<"completion: "<allMatches(); if(m_completionObject->hasMultipleMatches()) { completeCommandTo(completion); - QToolTip::showText(QPoint(), QString(), m_worksheet); - switch(Settings::self()->completionStyle()) - { - case Settings::PopupCompletion: - { - if (m_completionBox) - m_completionBox->deleteLater(); - m_completionBox=new KCompletionBox(m_worksheet); - m_completionBox->setItems(m_completionObject->allMatches()); - QList items = m_completionBox->findItems(m_completionObject->command(), Qt::MatchFixedString|Qt::MatchCaseSensitive); - if (!items.empty()) - m_completionBox->setCurrentItem(items.first()); - m_completionBox->setTabHandling(false); - m_completionBox->setActivateOnSelect(true); - connect(m_completionBox, SIGNAL(activated(const QString&)), this, SLOT(applySelectedCompletion())); - connect(m_worksheet, SIGNAL(textChanged()), this, SLOT(completedLineChanged())); - connect(m_completionObject, SIGNAL(done()), this, SLOT(updateCompletions())); - - QRect rect=m_worksheet->cursorRect(); - kDebug()<<"cursor is within: "<popup(); - m_completionBox->move(m_worksheet->mapToGlobal(popupPoint)); - break; - } - case Settings::InlineCompletion: - { - int oldCursorPos=m_worksheet->textCursor().position(); - - //Show a list of possible completions - if(!m_contextHelpCell.isValid()) - { - //remember the actual cursor position, and reset the cursor later - int row=m_commandCell.row()+1; - - m_table->insertRows(row, 1); - m_contextHelpCell=m_table->cellAt(row, 1); - - QTextCursor c=m_worksheet->textCursor(); - c.setPosition(oldCursorPos); - m_worksheet->setTextCursor(c); - } - - QTextCursor cursor=m_contextHelpCell.firstCursorPosition(); - cursor.setPosition(m_contextHelpCell.lastCursorPosition().position(), QTextCursor::KeepAnchor); - - int count=0; - QString html=""; - const QStringList& matches=m_completionObject->allMatches(); - foreach(const QString& item, matches) - { - html+=""; - count++; - if(count>10) - break; - } - - const int itemsLeft=matches.size()-count; - if(itemsLeft>0) - html+=""; - - html+="
    "+item+"
    "+i18n("And %1 more...", itemsLeft)+"
    "; - - cursor.insertHtml(html); - - m_worksheet->setTextCursor(cursor); - m_worksheet->ensureCursorVisible(); - QTextCursor oldC=m_worksheet->textCursor(); - oldC.setPosition(oldCursorPos); - m_worksheet->setTextCursor(oldC); - m_worksheet->ensureCursorVisible(); - break; - } - } - + QToolTip::showText(QPoint(), QString(), worksheetView()); + if (m_completionBox) + m_completionBox->deleteLater(); + m_completionBox = new KCompletionBox(worksheetView()); + m_completionBox->setItems(m_completionObject->allMatches()); + QList items = m_completionBox->findItems(m_completionObject->command(), Qt::MatchFixedString|Qt::MatchCaseSensitive); + if (!items.empty()) + m_completionBox->setCurrentItem(items.first()); + m_completionBox->setTabHandling(false); + m_completionBox->setActivateOnSelect(true); + connect(m_completionBox, SIGNAL(activated(const QString&)), this, + SLOT(applySelectedCompletion())); + connect(m_commandItem->document(), SIGNAL(contentsChanged()), this, + SLOT(completedLineChanged())); + connect(m_completionObject, SIGNAL(done()), this, SLOT(updateCompletions())); + + m_commandItem->activateCompletion(true); + QPointF cursorPos = m_commandItem->cursorPosition(); + m_completionBox->popup(); + m_completionBox->move(toGlobalPosition(cursorPos)); } else { completeCommandTo(completion, FinalCompletion); } - - } bool CommandEntry::isShowingCompletionPopup() { - return m_completionBox&&m_completionBox->isVisible(); + return m_completionBox && m_completionBox->isVisible(); } void CommandEntry::applySelectedCompletion() { - QListWidgetItem* item=m_completionBox->currentItem(); + QListWidgetItem* item = m_completionBox->currentItem(); if(item) completeCommandTo(item->text(), FinalCompletion); m_completionBox->hide(); } void CommandEntry::completedLineChanged() { if (!isShowingCompletionPopup()) { // the completion popup is not visible anymore, so let's clean up removeContextHelp(); return; } - const QString line=currentLine(m_worksheet->textCursor()); - m_completionObject->updateLine(line, m_worksheet->textCursor().positionInBlock()); - -} + const QString line = currentLine(); + m_completionObject->updateLine(line, m_commandItem->textCursor().positionInBlock()); +} void CommandEntry::updateCompletions() { if (!m_completionObject) return; - QString completion=m_completionObject->completion(); + QString completion = m_completionObject->completion(); kDebug()<<"completion: "<allMatches(); if(m_completionObject->hasMultipleMatches() || !completion.isEmpty()) { - QToolTip::showText(QPoint(), QString(), m_worksheet); - switch(Settings::self()->completionStyle()) - { - case Settings::PopupCompletion: - { - m_completionBox->setItems(m_completionObject->allMatches()); - QList items = m_completionBox->findItems(m_completionObject->command(), Qt::MatchFixedString|Qt::MatchCaseSensitive); - if (!items.empty()) - m_completionBox->setCurrentItem(items.first()); - - QRect rect=m_worksheet->cursorRect(); - kDebug()<<"cursor is within: "<popup(); - m_completionBox->move(m_worksheet->mapToGlobal(popupPoint)); - break; - } - case Settings::InlineCompletion: - { - int oldCursorPos=m_worksheet->textCursor().position(); - - //Show a list of possible completions - if(!m_contextHelpCell.isValid()) - { - //remember the actual cursor position, and reset the cursor later - int row=m_commandCell.row()+1; - - m_table->insertRows(row, 1); - m_contextHelpCell=m_table->cellAt(row, 1); - - QTextCursor c=m_worksheet->textCursor(); - c.setPosition(oldCursorPos); - m_worksheet->setTextCursor(c); - } - - QTextCursor cursor=m_contextHelpCell.firstCursorPosition(); - cursor.setPosition(m_contextHelpCell.lastCursorPosition().position(), QTextCursor::KeepAnchor); - - int count=0; - QString html=""; - const QStringList& matches=m_completionObject->allMatches(); - foreach(const QString& item, matches) - { - html+=""; - count++; - if(count>10) - break; - } - - const int itemsLeft=matches.size()-count; - if(itemsLeft>0) - html+=""; - - html+="
    "+item+"
    "+i18n("And %1 more...", itemsLeft)+"
    "; - - cursor.insertHtml(html); - - m_worksheet->setTextCursor(cursor); - m_worksheet->ensureCursorVisible(); - QTextCursor oldC=m_worksheet->textCursor(); - oldC.setPosition(oldCursorPos); - m_worksheet->setTextCursor(oldC); - m_worksheet->ensureCursorVisible(); - break; - } - } - + QToolTip::showText(QPoint(), QString(), worksheetView()); + m_completionBox->setItems(m_completionObject->allMatches()); + QList items = m_completionBox->findItems(m_completionObject->command(), Qt::MatchFixedString|Qt::MatchCaseSensitive); + if (!items.empty()) + m_completionBox->setCurrentItem(items.first()); + + QPointF cursorPos = m_commandItem->cursorPosition(); + m_completionBox->move(toGlobalPosition(cursorPos)); } else { removeContextHelp(); } - } void CommandEntry::completeCommandTo(const QString& completion, CompletionMode mode) { kDebug() << "completion: " << completion; if (mode == FinalCompletion) { - Cantor::SyntaxHelpObject* obj=m_worksheet->session()->syntaxHelpFor(completion); + Cantor::SyntaxHelpObject* obj = worksheet()->session()->syntaxHelpFor(completion); if(obj) setSyntaxHelp(obj); } else { if(m_syntaxHelpObject) m_syntaxHelpObject->deleteLater(); m_syntaxHelpObject=0; } Cantor::CompletionObject::LineCompletionMode cmode; if (mode == PreliminaryCompletion) cmode = Cantor::CompletionObject::PreliminaryCompletion; else cmode = Cantor::CompletionObject::FinalCompletion; m_completionObject->completeLine(completion, cmode); } void CommandEntry::completeLineTo(const QString& line, int index) { kDebug() << "line completion: " << line; - QTextCursor cursor = m_worksheet->textCursor(); - if(!isInCommandCell(cursor)) return; + QTextCursor cursor = m_commandItem->textCursor(); cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); int startPosition = cursor.position(); cursor.insertText(line); cursor.setPosition(startPosition + index); - m_worksheet->setTextCursor(cursor); + m_commandItem->setTextCursor(cursor); if (m_syntaxHelpObject) { m_syntaxHelpObject->fetchSyntaxHelp(); // If we are about to show syntax help, then this was the final - // completion, and we should clean up. + // completion, and we should clean up. removeContextHelp(); } } void CommandEntry::setSyntaxHelp(Cantor::SyntaxHelpObject* sh) { if(m_syntaxHelpObject) m_syntaxHelpObject->deleteLater(); m_syntaxHelpObject=sh; connect(sh, SIGNAL(done()), this, SLOT(showSyntaxHelp())); } void CommandEntry::showSyntaxHelp() { - const QString& msg=m_syntaxHelpObject->toHtml(); - const QRect r=m_worksheet->cursorRect(); - const QPoint pos=m_worksheet->mapToGlobal(r.topLeft()); + const QString& msg = m_syntaxHelpObject->toHtml(); + const QPointF cursorPos = m_commandItem->cursorPosition(); - QToolTip::showText(pos, msg, m_worksheet); + QToolTip::showText(toGlobalPosition(cursorPos), msg, worksheetView()); } void CommandEntry::resultDeleted() { kDebug()<<"result got removed..."; } -QTextTable* CommandEntry::table() -{ - return m_table; -} - -QTextTableCell CommandEntry::commandCell() -{ - return m_commandCell; -} - -QTextTableCell CommandEntry::actualInformationCell() -{ - if(m_informationCells.isEmpty()) - return QTextTableCell(); - else - return m_informationCells.last(); -} - -QTextTableCell CommandEntry::resultCell() -{ - return m_resultCell; -} - void CommandEntry::addInformation() { - QTextCursor c=actualInformationCell().firstCursorPosition(); - c.setPosition(actualInformationCell().lastCursorPosition().position(), QTextCursor::KeepAnchor); - QString inf=c.selectedText(); + WorksheetTextItem *answerItem = currentInformationItem(); + answerItem->setTextInteractionFlags(Qt::TextSelectableByMouse); - inf.replace(QChar::ParagraphSeparator, '\n'); //Replace the U+2029 paragraph break by a Normal Newline - inf.replace(QChar::LineSeparator, '\n'); //Replace the line break by a Normal Newline + QString inf = answerItem->toPlainText(); + inf.replace(QChar::ParagraphSeparator, '\n'); + inf.replace(QChar::LineSeparator, '\n'); kDebug()<<"adding information: "<addInformation(inf); } void CommandEntry::showAdditionalInformationPrompt(const QString& question) { - int row; - if (actualInformationCell().isValid()) - row=actualInformationCell().row()+1; - else - row=commandCell().row()+1; - - //insert two rows, one for the question, one for the answer - m_table->insertRows(row, 2); - - QTextTableCell cell=m_table->cellAt(row, 1); - cell.firstCursorPosition().insertText(question); - cell=m_table->cellAt(row+1, 1); - m_informationCells.append(cell); - - m_worksheet->setTextCursor(cell.firstCursorPosition()); - m_worksheet->ensureCursorVisible(); - m_worksheet->setCurrentEntry(this, false); -} - -bool CommandEntry::isInCurrentInformationCell(const QTextCursor& cursor) -{ - if(m_informationCells.isEmpty()) - return false; - - QTextTableCell cell=m_informationCells.last(); - if(cursor.position()>=cell.firstCursorPosition().position()&&cursor.position()<=cell.lastCursorPosition().position()) - return true; - else - return false; -} - -bool CommandEntry::isInCommandCell(const QTextCursor& cursor) -{ - if(cursor.position()>=m_commandCell.firstCursorPosition().position()&&cursor.position()<=m_commandCell.lastCursorPosition().position()) - return true; - else - return false; -} - -bool CommandEntry::isInPromptCell(const QTextCursor& cursor) -{ - const QTextTableCell cell=m_table->cellAt(0, 0); - if(cursor.position()>=cell.firstCursorPosition().position()&&cursor.position()<=cell.lastCursorPosition().position()) - return true; - else - return false; -} - -bool CommandEntry::isInResultCell(const QTextCursor& cursor) -{ - if(!m_resultCell.isValid()) - return false; + WorksheetTextItem* questionItem = new WorksheetTextItem(this, Qt::TextSelectableByMouse); + WorksheetTextItem* answerItem = new WorksheetTextItem(this, Qt::TextEditorInteraction); + questionItem->setPlainText(question); + m_informationItems.append(questionItem); + m_informationItems.append(answerItem); + connect(answerItem, SIGNAL(moveToPrevious(int, qreal)), + this, SLOT(moveToPreviousItem(int, qreal))); + connect(answerItem, SIGNAL(moveToNext(int, qreal)), + this, SLOT(moveToNextItem(int, qreal))); - if(cursor.position()>=m_resultCell.firstCursorPosition().position()&&cursor.position()<=m_resultCell.lastCursorPosition().position()) - return true; - else - return false; -} - -bool CommandEntry::isInErrorCell(const QTextCursor& cursor) -{ - if(!m_errorCell.isValid()) - return false; - - if(cursor.position()>=m_errorCell.firstCursorPosition().position()&&cursor.position()<=m_errorCell.lastCursorPosition().position()) - return true; - else - return false; -} - -void CommandEntry::checkForSanity() -{ - QTextTableCell cell=m_table->cellAt(0, 0); - QTextCursor c=cell.firstCursorPosition(); - c.setPosition(cell.lastCursorPosition().position(), QTextCursor::KeepAnchor); - if(c.selectedText()!=CommandEntry::Prompt) - updatePrompt(); + connect(answerItem, SIGNAL(execute()), this, SLOT(addInformation())); + answerItem->setFocus(); + recalculateSize(); } void CommandEntry::removeResult() { - if(m_resultCell.isValid()) - { - m_table->removeRows(m_resultCell.row(), 1); - m_resultCell=QTextTableCell(); - } - if(m_expression) { m_expression->clearResult(); } + if (m_resultItem) { + QGraphicsObject* obj = m_resultItem->graphicsObject(); + m_resultItem = 0; + fadeOutItem(obj); + } } - void CommandEntry::removeContextHelp() { - disconnect(m_worksheet, SIGNAL(textChanged()), this, SLOT(completedLineChanged())); + disconnect(m_commandItem->document(), SIGNAL(contentsChanged()), this, + SLOT(completedLineChanged())); if(m_completionObject) m_completionObject->deleteLater(); + m_commandItem->activateCompletion(false); m_completionObject = 0; if (m_completionBox) m_completionBox->hide(); - if(m_contextHelpCell.isValid()) - { - m_table->removeRows(m_contextHelpCell.row(), 1); - m_contextHelpCell=QTextTableCell(); - } } + void CommandEntry::updatePrompt() { KColorScheme color = KColorScheme( QPalette::Normal, KColorScheme::View); - QTextTableCell cell=m_table->cellAt(0, 0); - QTextCursor c=cell.firstCursorPosition(); + + m_promptItem->setPlainText(""); + QTextCursor c = m_promptItem->textCursor(); QTextCharFormat cformat = c.charFormat(); cformat.clearForeground(); - c.setPosition(cell.lastCursorPosition().position(), QTextCursor::KeepAnchor); c.setCharFormat(cformat); cformat.setFontWeight(QFont::Bold); //insert the session id if available - if(m_expression&&m_worksheet->showExpressionIds()) + if(m_expression && worksheet()->showExpressionIds()) c.insertText(QString::number(m_expression->id()),cformat); //detect the correct color for the prompt, depending on the //Expression state if(m_expression) { - if(m_expression ->status() == Cantor::Expression::Computing&& m_worksheet->isRunning()) + if(m_expression ->status() == Cantor::Expression::Computing&&worksheet()->isRunning()) cformat.setForeground(color.foreground(KColorScheme::PositiveText)); else if(m_expression ->status() == Cantor::Expression::Error) cformat.setForeground(color.foreground(KColorScheme::NegativeText)); else if(m_expression ->status() == Cantor::Expression::Interrupted) cformat.setForeground(color.foreground(KColorScheme::NeutralText)); else cformat.setFontWeight(QFont::Normal); } c.insertText(CommandEntry::Prompt,cformat); + recalculateSize(); +} + +WorksheetTextItem* CommandEntry::currentInformationItem() +{ + if (m_informationItems.isEmpty()) + return 0; + return m_informationItems.last(); +} + +bool CommandEntry::informationItemHasFocus() +{ + if (m_informationItems.isEmpty()) + return false; + return m_informationItems.last()->hasFocus(); +} + +bool CommandEntry::focusWithinThisItem() +{ + return focusItem() != 0; } void CommandEntry::invalidate() { - m_table=0; - m_commandCell=QTextTableCell(); - m_contextHelpCell=QTextTableCell(); - m_informationCells.clear(); - m_errorCell=QTextTableCell(); - m_resultCell=QTextTableCell(); + kDebug() << "ToDo: Invalidate here"; +} + +bool CommandEntry::wantToEvaluate() +{ + return !isEmpty(); +} + +QPoint CommandEntry::toGlobalPosition(const QPointF& localPos) +{ + const QPointF scenePos = mapToScene(localPos); + const QPoint viewportPos = worksheetView()->mapFromScene(scenePos); + return worksheetView()->viewport()->mapToGlobal(viewportPos); +} + +WorksheetCursor CommandEntry::search(QString pattern, unsigned flags, + QTextDocument::FindFlags qt_flags, + const WorksheetCursor& pos) +{ + if (pos.isValid() && pos.entry() != this) + return WorksheetCursor(); + + WorksheetCursor p = pos; + QTextCursor cursor; + if (flags & WorksheetEntry::SearchCommand) { + cursor = m_commandItem->search(pattern, qt_flags, p); + if (!cursor.isNull()) + return WorksheetCursor(this, m_commandItem, cursor); + } + + if (p.textItem() == m_commandItem) + p = WorksheetCursor(); + + if (m_errorItem && flags & WorksheetEntry::SearchError) { + cursor = m_errorItem->search(pattern, qt_flags, p); + if (!cursor.isNull()) + return WorksheetCursor(this, m_errorItem, cursor); + } + + if (p.textItem() == m_errorItem) + p = WorksheetCursor(); + + WorksheetTextItem* textResult = dynamic_cast + (m_resultItem); + if (textResult && flags & WorksheetEntry::SearchResult) { + cursor = textResult->search(pattern, qt_flags, p); + if (!cursor.isNull()) + return WorksheetCursor(this, textResult, cursor); + } + + return WorksheetCursor(); +} + +void CommandEntry::layOutForWidth(double w, bool force) +{ + if (w == size().width() && !force) + return; + + m_promptItem->setPos(0,0); + double x = 0 + m_promptItem->width() + HorizontalSpacing; + double y = 0; + + m_commandItem->setGeometry(x,y, w-x); + + y += qMax(m_commandItem->height(), m_promptItem->height()); + foreach(WorksheetTextItem* information, m_informationItems) { + y += VerticalSpacing; + y += information->setGeometry(x,y,w-x); + } + + if (m_errorItem) { + y += VerticalSpacing; + y += m_errorItem->setGeometry(x,y,w-x); + } + + if (m_resultItem) { + y += VerticalSpacing; + y += m_resultItem->setGeometry(x, y, w-x); + } + y += VerticalMargin; + + QSizeF s(w, y); + if (animationActive()) { + updateSizeAnimation(s); + } else { + setSize(s); + } +} + +void CommandEntry::startRemoving() +{ + m_promptItem->setItemDragable(false); + WorksheetEntry::startRemoving(); +} + +WorksheetTextItem* CommandEntry::highlightItem() +{ + return m_commandItem; } diff --git a/src/commandentry.h b/src/commandentry.h index 4e57ff97..627e9129 100644 --- a/src/commandentry.h +++ b/src/commandentry.h @@ -1,143 +1,152 @@ /* 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 _COMMANDENTRY_H -#define _COMMANDENTRY_H +#ifndef COMMANDENTRY_H +#define COMMANDENTRY_H + +#include +#include +#include #include "worksheetentry.h" -#include +#include "worksheettextitem.h" +#include "lib/expression.h" class Worksheet; -class WorksheetEntry; +class ResultItem; -/** - An entry in the Worksheet. it contains: - 1 Row to take command from the user - 0+ Rows for addition questions/answers from the backend - 0/1 Row for contextual help like Tab Completion offers - 1 Row for the Result - **/ +namespace Cantor{ + class Result; + class CompletionObject; + class SyntaxHelpObject; +} class CommandEntry : public WorksheetEntry { Q_OBJECT public: static const QString Prompt; - CommandEntry(QTextCursor position, Worksheet* parent); + CommandEntry(Worksheet* worksheet); ~CommandEntry(); - enum {Type = 2}; - int type(); + enum {Type = UserType + 2}; + int type() const; QString command(); void setExpression(Cantor::Expression* expr); Cantor::Expression* expression(); - //returns the line of the command cell, the textCursor is currently in - QString currentLine(const QTextCursor& cursor); + QString currentLine(); bool isEmpty(); void setContent(const QString& content); void setContent(const QDomElement& content, const KZip& file); QDomElement toXml(QDomDocument& doc, KZip* archive); - QString toPlain(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq); + QString toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq); - void showCompletion(); void setCompletion(Cantor::CompletionObject* tc); void setSyntaxHelp(Cantor::SyntaxHelpObject* sh); - QTextTable* table(); - QTextTableCell commandCell(); - QTextTableCell actualInformationCell(); - QTextTableCell resultCell(); + bool acceptRichText(); - void addInformation(); + void removeContextHelp(); - QTextCursor closestValidCursor(const QTextCursor& cursor); - QTextCursor firstValidCursorPosition(); - QTextCursor lastValidCursorPosition(); - bool isValidCursor(const QTextCursor& cursor); + void interruptEvaluation(); + bool isShowingCompletionPopup(); - bool worksheetShortcutOverrideEvent(QKeyEvent* event, const QTextCursor& cursor); - bool worksheetKeyPressEvent(QKeyEvent* event, const QTextCursor& cursor); - bool worksheetMousePressEvent(QMouseEvent* event, const QTextCursor& cursor); - bool worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor); + bool focusEntry(int pos = WorksheetTextItem::TopLeft, qreal xCoord = 0); - bool acceptRichText(); - bool acceptsDrop(const QTextCursor& cursor); + void layOutForWidth(double w, bool force = false); - bool isInCurrentInformationCell(const QTextCursor& cursor); - bool isInCommandCell(const QTextCursor& cursor); - bool isInPromptCell(const QTextCursor& cursor); - bool isInResultCell(const QTextCursor& cursor); - bool isInErrorCell(const QTextCursor& cursor); + WorksheetTextItem* highlightItem(); - //checks if this entry has still anything needed (aka the user didn't delete anything - //like the prompt. Readd missing things - void checkForSanity(); + WorksheetCursor search(QString pattern, unsigned flags, + QTextDocument::FindFlags qt_flags, + const WorksheetCursor& pos = WorksheetCursor()); - void removeContextHelp(); + public slots: + bool evaluateCurrentItem(); + bool evaluate(EvaluationOption evalOp = FocusNext); + void addInformation(); void removeResult(); - bool evaluate(bool current); - bool evaluateCommand(); - void interruptEvaluation(); - - bool isShowingCompletionPopup(); - - public slots: - void update(); + void showCompletion(); + void selectPreviousCompletion(); + void updateEntry(); void updatePrompt(); void expressionChangedStatus(Cantor::Expression::Status status); void showAdditionalInformationPrompt(const QString& question); void showCompletions(); void applySelectedCompletion(); void completedLineChanged(); void showSyntaxHelp(); void completeLineTo(const QString& line, int index); + + void startRemoving(); + + void moveToNextItem(int pos, qreal x); + void moveToPreviousItem(int pos, qreal x); + + void populateMenu(KMenu *menu, const QPointF& pos); + + protected: + bool wantToEvaluate(); + + private: + WorksheetTextItem* currentInformationItem(); + bool informationItemHasFocus(); + bool focusWithinThisItem(); + + QPoint toGlobalPosition(const QPointF& localPos); + private: enum CompletionMode { PreliminaryCompletion, FinalCompletion }; private slots: void invalidate(); void resultDeleted(); void updateCompletions(); void completeCommandTo(const QString& completion, CompletionMode mode = PreliminaryCompletion); private: - QTextTable* m_table; - QTextTableCell m_commandCell; - QTextTableCell m_contextHelpCell; - QList m_informationCells; - QTextTableCell m_errorCell; - QTextTableCell m_resultCell; + static const double HorizontalSpacing; + static const double VerticalSpacing; + + WorksheetTextItem* m_promptItem; + WorksheetTextItem* m_commandItem; + ResultItem* m_resultItem; + WorksheetTextItem* m_errorItem; + QList m_informationItems; Cantor::Expression* m_expression; Cantor::CompletionObject* m_completionObject; QPointer m_completionBox; Cantor::SyntaxHelpObject* m_syntaxHelpObject; + + EvaluationOption m_evaluationOption; }; -#endif /* _COMMANDENTRY_H */ +#endif // COMMANDENTRY_H diff --git a/src/epsrenderer.cpp b/src/epsrenderer.cpp new file mode 100644 index 00000000..b1efb40f --- /dev/null +++ b/src/epsrenderer.cpp @@ -0,0 +1,140 @@ +/* + 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 "epsrenderer.h" + +#include "config-cantor.h" + +#ifdef LIBSPECTRE_FOUND + #include "libspectre/spectre.h" +#endif + +#include + +EpsRenderer::EpsRenderer() : m_scale(1), m_useHighRes(false) +{ +} + +EpsRenderer::~EpsRenderer() +{} + +void EpsRenderer::setScale(double scale) +{ + m_scale = scale; +} + +double EpsRenderer::scale() +{ + return m_scale; +} + +void EpsRenderer::useHighResolution(bool b) +{ + m_useHighRes = b; +} + +QTextImageFormat EpsRenderer::render(QTextDocument *document, const KUrl& url) +{ + QTextImageFormat epsCharFormat; + + QSizeF s = renderToResource(document, url); + + KUrl internal = url; + internal.setProtocol("internal"); + if(s.isValid()) + { + epsCharFormat.setName(internal.url()); + epsCharFormat.setWidth(s.width()); + epsCharFormat.setHeight(s.height()); + } + + return epsCharFormat; +} + +QTextImageFormat EpsRenderer::render(QTextDocument *document, + const Cantor::LatexRenderer* latex) +{ + QTextImageFormat format = render(document, latex->imagePath()); + + if (!format.name().isEmpty()) { + format.setProperty(CantorFormula, latex->method()); + format.setProperty(ImagePath, latex->imagePath()); + format.setProperty(Code, latex->latexCode()); + } + + return format; +} + +QSizeF EpsRenderer::renderToResource(QTextDocument *document, const KUrl& url) +{ + QSizeF size; + QImage img = renderToImage(url, &size); + + KUrl internal = url; + internal.setProtocol("internal"); + kDebug() << internal; + document->addResource(QTextDocument::ImageResource, internal, QVariant(img) ); + return size; +} + +QImage EpsRenderer::renderToImage(const KUrl& url, QSizeF* size) +{ +#ifdef LIBSPECTRE_FOUND + SpectreDocument* doc = spectre_document_new(); + SpectreRenderContext* rc = spectre_render_context_new(); + + kDebug() << "rendering eps file: " << url; + + spectre_document_load(doc, url.toLocalFile().toUtf8()); + + int wdoc, hdoc; + qreal w, h; + double scale; + spectre_document_get_page_size(doc, &wdoc, &hdoc); + if(m_useHighRes) { + scale=1.2*4.0; //1.2 scaling factor, to make it look nice, 4x for high resolution + w = 1.2 * wdoc; + h = 1.2 * hdoc; + } else { + scale=1.8*m_scale; + w = 1.8 * wdoc; + h = 1.8 * hdoc; + } + + kDebug()<<"scale: "< + */ + +#ifndef EPSRENDERER_H +#define EPSRENDERER_H + +#include +#include +#include +#include + +#include + +#include "lib/latexrenderer.h" + +class EpsRenderer +{ + public: + EpsRenderer(); + ~EpsRenderer(); + + enum FormulaProperties {CantorFormula = 1, ImagePath = 2, Code = 3, + Delimiter = 4}; + enum FormulaType {LatexFormula = Cantor::LatexRenderer::LatexMethod, + MmlFormula = Cantor::LatexRenderer::MmlMethod}; + + QTextImageFormat render(QTextDocument *document, const KUrl& url); + QTextImageFormat render(QTextDocument *document, + const Cantor::LatexRenderer* latex); + + void setScale(qreal scale); + qreal scale(); + + void useHighResolution(bool b); + + QSizeF renderToResource(QTextDocument *document, const KUrl& url); + QImage renderToImage(const KUrl& url, QSizeF* size = 0); + + private: + double m_scale; + bool m_useHighRes; +}; + +#endif //EPSRENDERER_H diff --git a/src/extendedsearchbar.ui b/src/extendedsearchbar.ui new file mode 100644 index 00000000..a8c80a3b --- /dev/null +++ b/src/extendedsearchbar.ui @@ -0,0 +1,214 @@ + + + ExtendedSearchBar + + + + 0 + 0 + 995 + 97 + + + + SearchBar + + + + + + + + ... + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Find: + + + + + + + true + + + + + + + &Next + + + + + + + &Previous + + + + + + + + + ... + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Replace: + + + + + + + true + + + + + + + &Replace + + + + + + + Replace &All + + + + + + + Qt::Horizontal + + + + + + + + + Search in: + + + + + + + + 4 + 0 + + + + true + + + false + + + false + + + + + + + ... + + + + + + + ... + + + + + + + &Match case + + + + + + + + 1 + 0 + + + + Qt::AlignCenter + + + + + + + + + + KLineEdit + QLineEdit +
    klineedit.h
    +
    + + KSqueezedTextLabel + QLabel +
    ksqueezedtextlabel.h
    +
    +
    + + +
    diff --git a/src/formulatextobject.cpp b/src/formulatextobject.cpp deleted file mode 100644 index f5abc643..00000000 --- a/src/formulatextobject.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - - --- - Copyright (C) 2011 Alexander Rieder - */ - -#include - -#include "formulatextobject.h" -#include -#include - -QSizeF FormulaTextObject::intrinsicSize(QTextDocument * doc, int /*posInDocument*/, - const QTextFormat &format) -{ - KUrl url=qVariantValue(format.property(ResourceUrl)); - //kDebug()<<"looking for: "<(doc->resource(QTextDocument::ImageResource, url)); - QSize size = bufferedImage.size(); - - //kDebug()<<"size: "<(format.property(ResourceUrl)); - QImage bufferedImage = qVariantValue(doc->resource(QTextDocument::ImageResource, url)); - - painter->drawImage(rect, bufferedImage); -} - diff --git a/src/formulatextobject.h b/src/formulatextobject.h deleted file mode 100644 index 3111cd9f..00000000 --- a/src/formulatextobject.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - - --- - Copyright (C) 2011 Alexander Rieder - */ - -#ifndef _FORMULATEXTOBJECT_H -#define _FORMULATEXTOBJECT_H - - -#include - -#include "cantor_export.h" - -class QTextDocument; -class QTextFormat; -class QPainter; -class QRectF; -class QSizeF; - -class FormulaTextObject : public QObject, public QTextObjectInterface -{ - Q_OBJECT - Q_INTERFACES(QTextObjectInterface) - - public: - enum { FormulaTextFormat = QTextFormat::UserObject + 1 }; - enum FormulaProperties { LatexCode = 1, FormulaType = 2, Data = 3, ResourceUrl = 4 }; - enum FormulaType { LatexFormula=0, MmlFormula=1 }; - - - QSizeF intrinsicSize(QTextDocument *doc, int posInDocument, - const QTextFormat &format); - void drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, - int posInDocument, const QTextFormat &format); - -}; - -#endif /* _FORMULATEXTOBJECT_H */ diff --git a/src/imageentry.cpp b/src/imageentry.cpp index 8822587f..1eee9f2b 100644 --- a/src/imageentry.cpp +++ b/src/imageentry.cpp @@ -1,451 +1,338 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- - Copyright (C) 2011 martin Kuettler + Copyright (C) 2012 martin Kuettler */ #include "imageentry.h" -#include "worksheet.h" +#include "worksheetimageitem.h" +#include "actionbar.h" -#ifdef LIBSPECTRE_FOUND - #include "libspectre/spectre.h" -#endif +#include +#include -#include -#include -#include -#include -#include -#include - -#include -#include - -ImageEntry::ImageEntry(QTextCursor position, Worksheet* parent) : - WorksheetEntry( position, parent ) +ImageEntry::ImageEntry(Worksheet* worksheet) : WorksheetEntry(worksheet) { - m_imagePath = QString(); + m_imageItem = 0; + m_textItem = new WorksheetTextItem(this); + m_imageWatcher = new QFileSystemWatcher(this); m_displaySize.width = -1; m_displaySize.height = -1; m_printSize.width = -1; m_printSize.height = -1; - m_displaySize.widthUnit = QString(); - m_displaySize.heightUnit = QString(); - m_printSize.widthUnit = QString(); - m_printSize.heightUnit = QString(); + m_displaySize.widthUnit = ImageSize::Auto; + m_displaySize.heightUnit = ImageSize::Auto; + m_printSize.widthUnit = ImageSize::Auto; + m_printSize.heightUnit = ImageSize::Auto; m_useDisplaySizeForPrinting = true; - m_settingsDialog = 0; - - connect(&m_imageWatcher, SIGNAL(fileChanged(const QString&)), this, SLOT(update())); - - /*QTextFrameFormat frameFormat = m_frame->frameFormat(); - frameFormat.setBorderStyle(QTextFrameFormat::BorderStyle_None); - m_frame->setFrameFormat(frameFormat);*/ + connect(m_imageWatcher, SIGNAL(fileChanged(const QString&)), + this, SLOT(updateEntry())); - update(); + setFlag(QGraphicsItem::ItemIsFocusable); + updateEntry(); } ImageEntry::~ImageEntry() { - if (m_settingsDialog != 0) - delete m_settingsDialog; -} - -int ImageEntry::type() -{ - return Type; } -bool ImageEntry::isEmpty() +void ImageEntry::populateMenu(KMenu *menu, const QPointF& pos) { - /* Are we empty? */ - return m_imagePath.isEmpty(); -} + menu->addAction(i18n("Configure Image"), this, SLOT(startConfigDialog())); + menu->addSeparator(); -QTextCursor ImageEntry::closestValidCursor(const QTextCursor& cursor) -{ - Q_UNUSED(cursor); - return firstValidCursorPosition(); -} - -QTextCursor ImageEntry::firstValidCursorPosition() -{ - return m_frame->lastCursorPosition(); + WorksheetEntry::populateMenu(menu, pos); } -QTextCursor ImageEntry::lastValidCursorPosition() -{ - return m_frame->lastCursorPosition(); -} - -bool ImageEntry::isValidCursor(const QTextCursor& cursor) +bool ImageEntry::isEmpty() { - Q_UNUSED(cursor); return false; } -bool ImageEntry::worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor) +int ImageEntry::type() const { - Q_UNUSED(cursor); - KMenu* defaultMenu = new KMenu(m_worksheet); - - defaultMenu->addAction(i18n("Configure Image"), this, SLOT(startConfigDialog())); - defaultMenu->addSeparator(); - - if(!m_worksheet->isRunning()) - defaultMenu->addAction(KIcon("system-run"),i18n("Evaluate Worksheet"),m_worksheet,SLOT(evaluate()),0); - else - defaultMenu->addAction(KIcon("process-stop"),i18n("Interrupt"),m_worksheet,SLOT(interrupt()),0); - defaultMenu->addSeparator(); - - defaultMenu->addAction(KIcon("edit-delete"),i18n("Remove Entry"), m_worksheet, SLOT(removeCurrentEntry())); - - createSubMenuInsert(defaultMenu); - - defaultMenu->popup(event->globalPos()); - - return true; - + return Type; } bool ImageEntry::acceptRichText() { return false; } -bool ImageEntry::acceptsDrop(const QTextCursor& cursor) -{ - // Maybe we will, someday. - Q_UNUSED(cursor); - return false; -} - void ImageEntry::setContent(const QString& content) { Q_UNUSED(content); return; } void ImageEntry::setContent(const QDomElement& content, const KZip& file) { Q_UNUSED(file); + static QStringList unitNames; + if (unitNames.isEmpty()) + unitNames << "(auto)" << "px" << "%"; QDomElement pathElement = content.firstChildElement("Path"); QDomElement displayElement = content.firstChildElement("Display"); QDomElement printElement = content.firstChildElement("Print"); m_imagePath = pathElement.text(); m_displaySize.width = displayElement.attribute("width").toDouble(); m_displaySize.height = displayElement.attribute("height").toDouble(); - m_displaySize.widthUnit = displayElement.attribute("widthUnit"); - m_displaySize.heightUnit = displayElement.attribute("heightUnit"); + m_displaySize.widthUnit = unitNames.indexOf(displayElement.attribute("widthUnit")); + m_displaySize.heightUnit = unitNames.indexOf(displayElement.attribute("heightUnit")); m_useDisplaySizeForPrinting = printElement.attribute("useDisplaySize").toInt(); m_printSize.width = printElement.attribute("width").toDouble(); m_printSize.height = printElement.attribute("height").toDouble(); - m_printSize.widthUnit = printElement.attribute("widthUnit"); - m_printSize.heightUnit = printElement.attribute("heightUnit"); - - update(); + m_printSize.widthUnit = unitNames.indexOf(printElement.attribute("widthUnit")); + m_printSize.heightUnit = unitNames.indexOf(printElement.attribute("heightUnit")); + updateEntry(); } QDomElement ImageEntry::toXml(QDomDocument& doc, KZip* archive) { Q_UNUSED(archive); - + + static QStringList unitNames; + if (unitNames.isEmpty()) + unitNames << "(auto)" << "px" << "%"; + QDomElement image = doc.createElement("Image"); QDomElement path = doc.createElement("Path"); QDomText pathText = doc.createTextNode(m_imagePath); path.appendChild(pathText); image.appendChild(path); QDomElement display = doc.createElement("Display"); display.setAttribute("width", m_displaySize.width); - display.setAttribute("widthUnit", m_displaySize.widthUnit); + display.setAttribute("widthUnit", unitNames[m_displaySize.widthUnit]); display.setAttribute("height", m_displaySize.height); - display.setAttribute("heightUnit", m_displaySize.heightUnit); + display.setAttribute("heightUnit", unitNames[m_displaySize.heightUnit]); image.appendChild(display); QDomElement print = doc.createElement("Print"); print.setAttribute("useDisplaySize", m_useDisplaySizeForPrinting); print.setAttribute("width", m_printSize.width); - print.setAttribute("widthUnit", m_printSize.widthUnit); + print.setAttribute("widthUnit", unitNames[m_printSize.widthUnit]); print.setAttribute("height", m_printSize.height); - print.setAttribute("heightUnit", m_printSize.heightUnit); + print.setAttribute("heightUnit", unitNames[m_printSize.heightUnit]); image.appendChild(print); - /* This element contains redundant information, but constructing - * the size string with xslt seems to be an unelegant solution to me. - */ + + // For the conversion to latex QDomElement latexSize = doc.createElement("LatexSizeString"); QString sizeString; - if (m_useDisplaySizeForPrinting) { - sizeString = makeLatexSizeString(m_displaySize); - } - else { - sizeString = makeLatexSizeString(m_printSize); - } + if (m_useDisplaySizeForPrinting) + sizeString = latexSizeString(m_displaySize); + else + sizeString = latexSizeString(m_printSize); QDomText latexSizeString = doc.createTextNode(sizeString); latexSize.appendChild(latexSizeString); image.appendChild(latexSize); - + return image; } -QString ImageEntry::toPlain(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq) +QString ImageEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) { Q_UNUSED(commandSep); - + return commentStartingSeq + "image: " + m_imagePath + commentEndingSeq; - } -void ImageEntry::interruptEvaluation() +QString ImageEntry::latexSizeString(const ImageSize& imgSize) { - return; + // We use the transformation 1 px = 1/72 in ( = 1 pt in Latex) + + QString sizeString=""; + if (imgSize.widthUnit == ImageSize::Auto && + imgSize.heightUnit == ImageSize::Auto) + return QString(""); + + if (imgSize.widthUnit == ImageSize::Percent) { + if (imgSize.heightUnit == ImageSize::Auto || + (imgSize.heightUnit == ImageSize::Percent && + imgSize.width == imgSize.height)) + return "[scale=" + QString::number(imgSize.width / 100) + "]"; + // else? We could set the size based on the actual image size + } else if (imgSize.widthUnit == ImageSize::Auto && + imgSize.heightUnit == ImageSize::Percent) { + return "[scale=" + QString::number(imgSize.height / 100) + "]"; + } + + if (imgSize.heightUnit == ImageSize::Pixel) + sizeString = "height=" + QString::number(imgSize.height) + "pt"; + if (imgSize.widthUnit == ImageSize::Pixel) { + if (!sizeString.isEmpty()) + sizeString += ","; + sizeString += "width=" + QString::number(imgSize.width) + "pt"; + } + return "[" + sizeString + "]"; } -bool ImageEntry::evaluate(bool current) +void ImageEntry::interruptEvaluation() { - Q_UNUSED(current); - return true; } -bool ImageEntry::worksheetKeyPressEvent(QKeyEvent* event, const QTextCursor& cursor) +bool ImageEntry::evaluate(EvaluationOption evalOp) { - if (WorksheetEntry::worksheetKeyPressEvent(event, cursor)) - { - return true; - } - + evaluateNext(evalOp); return true; } -void ImageEntry::update() +qreal ImageEntry::height() { - QTextCursor cursor(m_frame->firstCursorPosition()); - cursor.setPosition(m_frame->lastPosition(), QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - - if (m_imagePath.isEmpty()) - { - if (m_worksheet->isPrinting()) - { - QTextFrameFormat frameFormat = m_frame->frameFormat(); - frameFormat.setBorderStyle(QTextFrameFormat::BorderStyle_None); - - m_frame->setFrameFormat(frameFormat); - return; - } - QTextBlockFormat block(cursor.blockFormat()); - block.setAlignment(Qt::AlignCenter); - cursor.setBlockFormat(block); - - KColorScheme color = KColorScheme(QPalette::Normal, KColorScheme::View); - cursor.insertText(i18n("Right click here to insert image")); + if (m_imageItem && m_imageItem->isVisible()) + return m_imageItem->height(); + else + return m_textItem->height(); +} - return; +void ImageEntry::updateEntry() +{ + qreal oldHeight = height(); + if (m_imagePath.isEmpty()) { + m_textItem->setPlainText(i18n("Right click here to insert image")); + m_textItem->setVisible(true); + if (m_imageItem) + m_imageItem->setVisible(false); } - QImage img(m_imagePath); - - if (img.isNull()) - { - if (m_worksheet->isPrinting()) - { - QTextFrameFormat frameFormat = m_frame->frameFormat(); - frameFormat.setBorderStyle(QTextFrameFormat::BorderStyle_None); - - m_frame->setFrameFormat(frameFormat); - return; + else { + if (!m_imageItem) + m_imageItem = new WorksheetImageItem(this); + + if (m_imagePath.toLower().endsWith(".eps")) { + m_imageItem->setEps(m_imagePath); + } else { + QImage img(m_imagePath); + m_imageItem->setImage(img); } - QTextBlockFormat block(cursor.blockFormat()); - block.setAlignment(Qt::AlignCenter); - cursor.setBlockFormat(block); - - KColorScheme color = KColorScheme(QPalette::Normal, KColorScheme::View); - cursor.insertText(i18n("Cannot load image ")+m_imagePath); - - return; - } - QTextImageFormat imgFormat; - -#ifdef LIBSPECTRE_FOUND - // a better way to test for eps-files? - if (m_imagePath.endsWith(".eps")) - { - imgFormat = renderEps(m_printSize); - } - else -#endif - if (m_worksheet->isPrinting() && !m_useDisplaySizeForPrinting) - { - double w, h; - m_worksheet->document()->addResource(QTextDocument::ImageResource, QUrl(m_imagePath), QVariant(img)); - imgFormat.setName(m_imagePath); - calculateImageSize(img.width(), img.height(), m_printSize, w, h); - imgFormat.setWidth(w); - imgFormat.setHeight(h); - } - else - { - double w, h; - m_worksheet->document()->addResource(QTextDocument::ImageResource, QUrl(m_imagePath), QVariant(img)); - imgFormat.setName(m_imagePath); - calculateImageSize(img.width(), img.height(), m_displaySize, w, h); - imgFormat.setWidth(w); - imgFormat.setHeight(h); + if (!m_imageItem->imageIsValid()) { + const QString msg = i18n("Cannot load image %1").arg(m_imagePath); + m_textItem->setPlainText(msg); + m_textItem->setVisible(true); + m_imageItem->setVisible(false); + } else { + QSizeF size; + if (worksheet()->isPrinting() && ! m_useDisplaySizeForPrinting) + size = imageSize(m_printSize); + else + size = imageSize(m_displaySize); + // Hack: Eps images need to be scaled + if (m_imagePath.toLower().endsWith(".eps")) + size /= worksheet()->epsRenderer()->scale(); + m_imageItem->setSize(size); + kDebug() << size; + m_textItem->setVisible(false); + m_imageItem->setVisible(true); + } } - QTextBlockFormat block(cursor.blockFormat()); - block.setAlignment(Qt::AlignCenter); - cursor.setBlockFormat(block); - cursor.insertImage(imgFormat); + kDebug() << oldHeight << height(); + if (oldHeight != height()) + recalculateSize(); } -void ImageEntry::setImageData(const QString& path, const ImageSize& displaySize, const ImageSize& printSize, bool useDisplaySizeForPrinting) +QSizeF ImageEntry::imageSize(const ImageSize& imgSize) { - m_imageWatcher.removePath(m_imagePath); - m_imageWatcher.addPath(path); - - m_imagePath = path; - m_displaySize = displaySize; - m_printSize = printSize; - m_useDisplaySizeForPrinting = useDisplaySizeForPrinting; + const QSize& srcSize = m_imageItem->imageSize(); + qreal w, h; + if (imgSize.heightUnit == ImageSize::Percent) + h = srcSize.height() * imgSize.height / 100; + else if (imgSize.heightUnit == ImageSize::Pixel) + h = imgSize.height; + if (imgSize.widthUnit == ImageSize::Percent) + w = srcSize.width() * imgSize.width / 100; + else if (imgSize.widthUnit == ImageSize::Pixel) + w = imgSize.width; + + if (imgSize.widthUnit == ImageSize::Auto) { + if (imgSize.heightUnit == ImageSize::Auto) + return QSizeF(srcSize.width(), srcSize.height()); + else if (h == 0) + w = 0; + else + w = h / srcSize.height() * srcSize.width(); + } else if (imgSize.heightUnit == ImageSize::Auto) { + if (w == 0) + h = 0; + else + h = w / srcSize.width() * srcSize.height(); + } - update(); + return QSizeF(w,h); } void ImageEntry::startConfigDialog() { - if (m_settingsDialog == 0) - { - m_settingsDialog = new ImageSettingsDialog(m_worksheet); - m_settingsDialog->setData(m_imagePath, m_displaySize, m_printSize, m_useDisplaySizeForPrinting); - connect(m_settingsDialog, SIGNAL(dataChanged(const QString&, const ImageSize&, const ImageSize&, bool)), - this, SLOT(setImageData(const QString&, const ImageSize&, const ImageSize&, bool))); - } - - if (m_settingsDialog->isHidden()) - m_settingsDialog->show(); - else - m_settingsDialog->activateWindow(); -} - -void ImageEntry::calculateImageSize(int imgWidth, int imgHeight, const ImageSize& imageSize, double& newWidth, double& newHeight) -{ - if (imgWidth == 0 || imgHeight == 0) - { - newWidth = 0; - newHeight = 0; - return; - } - - if (imageSize.heightUnit == "%") - newHeight = imgHeight * imageSize.height / 100; - else if (imageSize.heightUnit == "px") - newHeight = imageSize.height; - if (imageSize.widthUnit == "%") - newWidth= imgWidth * imageSize.width / 100; - else if (imageSize.widthUnit == "px") - newWidth = imageSize.width; - - if (imageSize.widthUnit == "(auto)") - { - if (imageSize.heightUnit == "(auto)") - { - newWidth = imgWidth; - newHeight = imgHeight; - return; - } - else - newWidth = newHeight / imgHeight * imgWidth; - } - else if (imageSize.heightUnit == "(auto)") - newHeight = newWidth / imgWidth * imgHeight; + ImageSettingsDialog* dialog = new ImageSettingsDialog(worksheet()->worksheetView()); + dialog->setData(m_imagePath, m_displaySize, m_printSize, + m_useDisplaySizeForPrinting); + connect(dialog, SIGNAL(dataChanged(const QString&, const ImageSize&, + const ImageSize&, bool)), + this, SLOT(setImageData(const QString&, const ImageSize&, + const ImageSize&, bool))); + dialog->show(); } -QString ImageEntry::makeLatexSizeString(const ImageSize& imageSize) +void ImageEntry::setImageData(const QString& path, + const ImageSize& displaySize, + const ImageSize& printSize, + bool useDisplaySizeForPrinting) { - // We use the transformation 1 px = 1/72 in ( = 1 pt in Latex) - // TODO: Handle missing cases; that requires the image size to be known - // (so it can only be done when m_imagePath points to a valid image) - - QString sizeString=""; - if (imageSize.widthUnit == "(auto)" && imageSize.heightUnit == "(auto)") { - return QString(""); + if (path != m_imagePath) { + m_imageWatcher->removePath(m_imagePath); + m_imageWatcher->addPath(path); + m_imagePath = path; } - if (imageSize.widthUnit == "%" && (imageSize.heightUnit == "(auto)" || - (imageSize.heightUnit == "%" && - imageSize.width == imageSize.height))) { - return "[scale=" + QString::number(imageSize.width / 100) + "]"; - } - else if (imageSize.widthUnit == "(auto)" && imageSize.heightUnit == "%") { - return "[scale=" + QString::number(imageSize.height / 100) + "]"; - } + m_displaySize = displaySize; + m_printSize = printSize; + m_useDisplaySizeForPrinting = useDisplaySizeForPrinting; - if (imageSize.heightUnit == "px") - sizeString = "height=" + QString::number(imageSize.height) + "pt"; - if (imageSize.widthUnit == "px") { - if (!sizeString.isEmpty()) - sizeString += ","; - sizeString += "width=" + QString::number(imageSize.width) + "pt"; - } - return "[" + sizeString + "]"; + updateEntry(); } -#ifdef LIBSPECTRE_FOUND -QTextImageFormat ImageEntry::renderEps(const ImageSize& imageSize) +void ImageEntry::addActionsToBar(ActionBar* actionBar) { - QTextImageFormat epsCharFormat; - - SpectreDocument* doc=spectre_document_new();; - SpectreRenderContext* rc=spectre_render_context_new(); - - spectre_document_load(doc, m_imagePath.toUtf8()); + actionBar->addButton(KIcon("configure"), i18n("Configure Image"), + this, SLOT(startConfigDialog())); +} - int w, h; - spectre_document_get_page_size(doc, &w, &h); - kDebug()<<"dimension: "<isPrinting()) - scale=4.0; //4x for high resolution + if (m_imageItem && m_imageItem->isVisible()) + m_imageItem->setGeometry(0, 0, w, true); else - scale=1.0; - xScale = newWidth/w * scale; - yScale = newHeight/h * scale; - - unsigned char* data; - int rowLength; + m_textItem->setGeometry(0, 0, w, true); - spectre_render_context_set_scale(rc, xScale, yScale); - spectre_document_render_full(doc, rc, &data, &rowLength); - - QImage img(data, w*xScale, h*yScale, rowLength, QImage::Format_RGB32); - - m_worksheet->document()->addResource(QTextDocument::ImageResource, m_imagePath, QVariant(img) ); - epsCharFormat.setName(m_imagePath); - epsCharFormat.setWidth(newWidth); - epsCharFormat.setHeight(newHeight); + setSize(QSizeF(w, height() + VerticalMargin)); +} - spectre_document_free(doc); - spectre_render_context_free(rc); +bool ImageEntry::wantToEvaluate() +{ + return false; +} - return epsCharFormat; +bool ImageEntry::wantFocus() +{ + return false; } -#endif + +#include "imageentry.moc" diff --git a/src/imageentry.h b/src/imageentry.h index 54b831ad..5b16bf4a 100644 --- a/src/imageentry.h +++ b/src/imageentry.h @@ -1,92 +1,85 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- - Copyright (C) 2011 martin Kuettler + Copyright (C) 2012 Martin Kuettler */ -#ifndef _IMAGEENTRY_H -#define _IMAGEENTRY_H +#ifndef IMAGEENTRY_H +#define IMAGEENTRY_H #include "worksheetentry.h" #include "imagesettingsdialog.h" -#include -#include +#include #include class Worksheet; -class WorksheetEntry; +class ActionBar; +class WorksheetImageItem; class ImageEntry : public WorksheetEntry { - Q_OBJECT + Q_OBJECT; + public: - ImageEntry(QTextCursor position, Worksheet* parent); + ImageEntry(Worksheet* worksheet); ~ImageEntry(); - enum {Type = 4}; - - int type(); + enum {Type = UserType + 4}; + int type() const; bool isEmpty(); - - QTextCursor closestValidCursor(const QTextCursor& cursor); - QTextCursor firstValidCursorPosition(); - QTextCursor lastValidCursorPosition(); - bool isValidCursor(const QTextCursor& cursor); - - // Handlers for the worksheet input events affecting worksheetentries - bool worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor); - bool acceptRichText(); - bool acceptsDrop(const QTextCursor& cursor); - void setContent(const QString& content); void setContent(const QDomElement& content, const KZip& file); - QDomElement toXml(QDomDocument& doc, KZip* archive); - QString toPlain(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq); + QString toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq); + + QSizeF imageSize(const ImageSize& imgSize); void interruptEvaluation(); - bool worksheetKeyPressEvent(QKeyEvent* event, const QTextCursor& cursor); - - bool evaluate(bool current); + + void layOutForWidth(qreal w, bool force = false); public slots: - void update(); + bool evaluate(EvaluationOption evalOp = FocusNext); + void updateEntry(); + void populateMenu(KMenu *menu, const QPointF& pos); void startConfigDialog(); - void setImageData(const QString& path, const ImageSize& displaySize, const ImageSize& printSize, bool useDisplaySizeForPrinting); + void setImageData(const QString& path, const ImageSize& displaySize, + const ImageSize& printSize, bool useDisplaySizeForPrinting); - private: -#ifdef LIBSPECTRE_FOUND - QTextImageFormat renderEps(const ImageSize& imageSize); -#endif - void calculateImageSize(int imgWidth, int imgHeight, const ImageSize& imageSize, double& newWidth, double& newHeight); - QString makeLatexSizeString(const ImageSize&); + protected: + bool wantToEvaluate(); + bool wantFocus(); + qreal height(); + + QString latexSizeString(const ImageSize& imgSize); + void addActionsToBar(ActionBar* actionBar); private: QString m_imagePath; ImageSize m_displaySize; ImageSize m_printSize; bool m_useDisplaySizeForPrinting; - QFileSystemWatcher m_imageWatcher; - ImageSettingsDialog* m_settingsDialog; - + WorksheetImageItem* m_imageItem; + WorksheetTextItem* m_textItem; + QFileSystemWatcher* m_imageWatcher; }; -#endif /* _IMAGEENTRY_H */ +#endif /* IMAGEENTRY_H */ diff --git a/src/imageresultitem.cpp b/src/imageresultitem.cpp new file mode 100644 index 00000000..36c20cae --- /dev/null +++ b/src/imageresultitem.cpp @@ -0,0 +1,105 @@ +/* + 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 "commandentry.h" +#include "imageresultitem.h" +#include "lib/imageresult.h" +#include "lib/epsresult.h" + +#include +#include + +ImageResultItem::ImageResultItem(QGraphicsObject* parent) + : WorksheetImageItem(parent), ResultItem() +{ + connect(this, SIGNAL(removeResult()), parentEntry(), + SLOT(removeResult())); +} + +ImageResultItem::~ImageResultItem() +{ +} + +double ImageResultItem::setGeometry(double x, double y, double w) +{ + Q_UNUSED(w); + setPos(x,y); + return height(); +} + +void ImageResultItem::populateMenu(KMenu* menu, const QPointF& pos) +{ + addCommonActions(this, menu); + + menu->addSeparator(); + kDebug() << "populate Menu"; + emit menuCreated(menu, mapToParent(pos)); +} + +ResultItem* ImageResultItem::updateFromResult(Cantor::Result* result) +{ + switch(result->type()) { + case Cantor::ImageResult::Type: + setImage(result->data().value()); + return this; + case Cantor::EpsResult::Type: + setEps(result->data().toUrl()); + return this; + default: + deleteLater(); + return create(parentEntry(), result); + } +} + +QRectF ImageResultItem::boundingRect() const +{ + return QRectF(0, 0, width(), height()); +} + +void ImageResultItem::saveResult() +{ + Cantor::Result* res = result(); + const QString& filename=KFileDialog::getSaveFileName(KUrl(), res->mimeType(), worksheet()->worksheetView()); + kDebug()<<"saving result to "<save(filename); +} + +void ImageResultItem::deleteLater() +{ + WorksheetImageItem::deleteLater(); +} + +EpsRenderer* ImageResultItem::epsRenderer() +{ + return qobject_cast(scene())->epsRenderer(); +} + +CommandEntry* ImageResultItem::parentEntry() +{ + return qobject_cast(parentObject()); +} + +Cantor::Result* ImageResultItem::result() +{ + return parentEntry()->expression()->result(); +} + +#include "imageresultitem.moc" + diff --git a/src/resultcontextmenu.h b/src/imageresultitem.h similarity index 52% copy from src/resultcontextmenu.h copy to src/imageresultitem.h index 20fa55b1..d967b344 100644 --- a/src/resultcontextmenu.h +++ b/src/imageresultitem.h @@ -1,63 +1,55 @@ /* 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 _RESULTCONTEXTMENU_H -#define _RESULTCONTEXTMENU_H +#ifndef IMAGERESULTITEM_H +#define IMAGERESULTITEM_H -#include +#include "resultitem.h" +#include "worksheetimageitem.h" +#include "worksheetentry.h" +#include "epsrenderer.h" -class CommandEntry; -namespace Cantor{ - class Result; -} - -/** - * this is the Menu shown when Right clicking on a Result. - * It offers different options depending on the Type of the - * result. - **/ -class ResultContextMenu : public KMenu +class ImageResultItem : public WorksheetImageItem, public ResultItem { Q_OBJECT public: - ResultContextMenu( CommandEntry* entry, QWidget* parent); - ~ResultContextMenu(); + ImageResultItem(QGraphicsObject* parent); + ~ImageResultItem(); - CommandEntry* entry(); - Cantor::Result* result(); + double setGeometry(double x, double y, double w); + void populateMenu(KMenu* menu, const QPointF& pos); - public slots: - void saveResult(); - void removeResult(); - void latexToggleShowCode(); - void animationPause(); - void animationRestart(); + ResultItem* updateFromResult(Cantor::Result* result); + + QRectF boundingRect() const; - private: - void addGeneralActions(); - void addTypeSpecificActions(); + void deleteLater(); + EpsRenderer* epsRenderer(); + CommandEntry* parentEntry(); + Cantor::Result* result(); - private: - CommandEntry* m_entry; - Cantor::Result* m_result; + signals: + void removeResult(); + protected slots: + void saveResult(); }; -#endif /* _RESULTCONTEXTMENU_H */ +#endif // IMAGERESULTITEM_H diff --git a/src/imagesettingsdialog.cpp b/src/imagesettingsdialog.cpp index 0f2a384f..c12487b7 100644 --- a/src/imagesettingsdialog.cpp +++ b/src/imagesettingsdialog.cpp @@ -1,190 +1,177 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2011 Martin Kuettler */ #include "imagesettingsdialog.h" #include "qimagereader.h" #include "qfiledialog.h" #include "kurlcompletion.h" ImageSettingsDialog::ImageSettingsDialog(QWidget* parent) : KDialog(parent) { QWidget *w = new QWidget(this); m_ui.setupUi(w); setMainWidget(w); - this->setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Apply ); + setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Apply ); - m_units << "(auto)" << "px" << "%"; m_unitNames << i18n("(auto)") << i18n("px") << i18n("%"); m_ui.displayWidthCombo->addItems(m_unitNames); m_ui.displayHeightCombo->addItems(m_unitNames); m_ui.printWidthCombo->addItems(m_unitNames); m_ui.printHeightCombo->addItems(m_unitNames); KUrlCompletion* completer = new KUrlCompletion(KUrlCompletion::FileCompletion); completer->setCompletionMode(KGlobalSettings::CompletionMan); m_ui.pathEdit->setCompletionObject(completer); m_ui.pathEdit->setAutoDeleteCompletionObject( true ); m_ui.displayWidthInput->setMinimum(0); m_ui.displayHeightInput->setMinimum(0); m_ui.printWidthInput->setMinimum(0); m_ui.printHeightInput->setMinimum(0); m_ui.displayWidthInput->setSingleStep(1); m_ui.displayHeightInput->setSingleStep(1); m_ui.printWidthInput->setSingleStep(1); m_ui.printHeightInput->setSingleStep(1); connect(this, SIGNAL(okClicked()), this, SLOT(sendChangesAndClose())); connect(this, SIGNAL(applyClicked()), this, SLOT(sendChanges())); connect(this, SIGNAL(cancelClicked()), this, SLOT(close())); connect(m_ui.openDialogButton, SIGNAL(clicked()), this, SLOT(openDialog())); //connect(m_fileDialog, SIGNAL(accepted()), this, SLOT(updatePath())); connect(m_ui.pathEdit, SIGNAL(editingFinished()), this, SLOT(updatePreview())); connect(m_ui.displayWidthCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateInputWidgets())); connect(m_ui.displayHeightCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateInputWidgets())); connect(m_ui.printWidthCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateInputWidgets())); connect(m_ui.printHeightCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateInputWidgets())); connect(m_ui.useDisplaySize, SIGNAL(stateChanged(int)), this, SLOT(updatePrintingGroup(int))); } ImageSettingsDialog::~ImageSettingsDialog() { } void ImageSettingsDialog::setData(const QString& file, const ImageSize& displaySize, const ImageSize& printSize, bool useDisplaySizeForPrinting) { m_ui.pathEdit->setText(file); if (displaySize.width >= 0) m_ui.displayWidthInput->setValue(displaySize.width); if (displaySize.height >= 0) m_ui.displayHeightInput->setValue(displaySize.height); if (printSize.width >= 0) m_ui.printWidthInput->setValue(printSize.width); if (printSize.height >= 0) m_ui.printHeightInput->setValue(printSize.height); - if (displaySize.widthUnit.isEmpty()) - m_ui.displayWidthCombo->setCurrentIndex(0); - else - m_ui.displayWidthCombo->setCurrentIndex(m_units.indexOf(displaySize.widthUnit)); - if (displaySize.heightUnit.isEmpty()) - m_ui.displayHeightCombo->setCurrentIndex(0); - else - m_ui.displayHeightCombo->setCurrentIndex(m_units.indexOf(displaySize.heightUnit)); - if (printSize.widthUnit.isEmpty()) - m_ui.printWidthCombo->setCurrentIndex(0); - else - m_ui.printWidthCombo->setCurrentIndex(m_units.indexOf(printSize.widthUnit)); - if (printSize.heightUnit.isEmpty()) - m_ui.printHeightCombo->setCurrentIndex(0); - else - m_ui.printHeightCombo->setCurrentIndex(m_units.indexOf(printSize.heightUnit)); + m_ui.displayWidthCombo->setCurrentIndex(displaySize.widthUnit); + m_ui.displayHeightCombo->setCurrentIndex(displaySize.heightUnit); + m_ui.printWidthCombo->setCurrentIndex(printSize.widthUnit); + m_ui.printHeightCombo->setCurrentIndex(printSize.heightUnit); if (useDisplaySizeForPrinting) m_ui.useDisplaySize->setCheckState(Qt::Checked); else m_ui.useDisplaySize->setCheckState(Qt::Unchecked); updatePreview(); updatePrintingGroup(useDisplaySizeForPrinting); //updateInputWidgets(); } void ImageSettingsDialog::sendChangesAndClose() { sendChanges(); close(); } void ImageSettingsDialog::sendChanges() { ImageSize displaySize, printSize; displaySize.width = m_ui.displayWidthInput->value(); displaySize.height = m_ui.displayHeightInput->value(); - displaySize.widthUnit = m_units[m_ui.displayWidthCombo->currentIndex()]; - displaySize.heightUnit = m_units[m_ui.displayHeightCombo->currentIndex()]; + displaySize.widthUnit = m_ui.displayWidthCombo->currentIndex(); + displaySize.heightUnit = m_ui.displayHeightCombo->currentIndex(); printSize.width = m_ui.printWidthInput->value(); printSize.height = m_ui.printHeightInput->value(); - printSize.widthUnit = m_units[m_ui.printWidthCombo->currentIndex()]; - printSize.heightUnit = m_units[m_ui.printHeightCombo->currentIndex()]; + printSize.widthUnit = m_ui.printWidthCombo->currentIndex(); + printSize.heightUnit = m_ui.printHeightCombo->currentIndex(); emit dataChanged (m_ui.pathEdit->text(), displaySize, printSize, (m_ui.useDisplaySize->checkState() == Qt::Checked)); } void ImageSettingsDialog::openDialog() { QList formats = QImageReader::supportedImageFormats(); QString formatString = "Images("; foreach(QByteArray format, formats) { formatString += "*." + QString(format).toLower() + " "; } formatString += ")"; QString file = QFileDialog::getOpenFileName(this, i18n("Open image file"), m_ui.pathEdit->text(), formatString); if (!file.isEmpty()) { m_ui.pathEdit->setText(file); updatePreview(); } } void ImageSettingsDialog::updatePreview() { m_ui.imagePreview->showPreview(KUrl(m_ui.pathEdit->text())); } void ImageSettingsDialog::updateInputWidgets() { if (m_ui.displayWidthCombo->currentIndex() == 0) m_ui.displayWidthInput->setEnabled(false); else m_ui.displayWidthInput->setEnabled(true); if (m_ui.displayHeightCombo->currentIndex() == 0) m_ui.displayHeightInput->setEnabled(false); else m_ui.displayHeightInput->setEnabled(true); if (m_ui.printWidthCombo->currentIndex() == 0 || !m_ui.printWidthCombo->isEnabled()) m_ui.printWidthInput->setEnabled(false); else m_ui.printWidthInput->setEnabled(true); if (m_ui.printHeightCombo->currentIndex() == 0 || !m_ui.printHeightCombo->isEnabled()) m_ui.printHeightInput->setEnabled(false); else m_ui.printHeightInput->setEnabled(true); } void ImageSettingsDialog::updatePrintingGroup(int b) { m_ui.printWidthCombo->setEnabled(!b); m_ui.printHeightCombo->setEnabled(!b); updateInputWidgets(); } diff --git a/src/imagesettingsdialog.h b/src/imagesettingsdialog.h index bb2667c8..73e9fd68 100644 --- a/src/imagesettingsdialog.h +++ b/src/imagesettingsdialog.h @@ -1,64 +1,64 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2011 Martin Kuettler */ -#ifndef _IMAGESETTINGSDIALOG_H -#define _IMAGESETTINGSDIALOG_H +#ifndef IMAGESETTINGSDIALOG_H +#define IMAGESETTINGSDIALOG_H #include #include struct ImageSize { + enum {Auto = 0, Pixel = 1, Percent = 2}; double width; double height; - QString widthUnit; - QString heightUnit; + int widthUnit; + int heightUnit; }; class ImageSettingsDialog : public KDialog { Q_OBJECT public: ImageSettingsDialog(QWidget* parent); ~ImageSettingsDialog(); void setData(const QString& file, const ImageSize& displaySize, const ImageSize& printSize, bool useDisplaySizeForPrinting); signals: void dataChanged(const QString& file, const ImageSize& displaySize, const ImageSize& printSize, bool useDisplaySizeForPrinting); private slots: void sendChangesAndClose(); void sendChanges(); void openDialog(); void updatePreview(); void updateInputWidgets(); void updatePrintingGroup(int b); private: - /*static*/ QList m_units; - /*static*/ QList m_unitNames; + QList m_unitNames; Ui_ImageSettingsBase m_ui; }; -#endif //_IMAGESETTINGSDIALOG_H +#endif //IMAGESETTINGSDIALOG_H diff --git a/src/latexentry.cpp b/src/latexentry.cpp index 1985d86d..a076f60a 100644 --- a/src/latexentry.cpp +++ b/src/latexentry.cpp @@ -1,325 +1,370 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- - Copyright (C) 2011 Alexander Rieder + Copyright (C) 2009 Alexander Rieder + Copyright (C) 2012 Martin Kuettler */ #include "latexentry.h" #include "worksheetentry.h" #include "worksheet.h" -#include "resultproxy.h" +#include "epsrenderer.h" #include "lib/defaulthighlighter.h" #include "lib/latexrenderer.h" -#include "formulatextobject.h" - -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include - +#include +#include +#include -LatexEntry::LatexEntry(QTextCursor position, Worksheet* parent ) : WorksheetEntry( position, parent ) +LatexEntry::LatexEntry(Worksheet* worksheet) : WorksheetEntry(worksheet), m_textItem(new WorksheetTextItem(this, Qt::TextEditorInteraction)) { - QTextFrameFormat frameFormat = m_frame->frameFormat(); - frameFormat.setPadding(10); - m_frame->setFrameFormat(frameFormat); - QTextCharFormat format = firstCursorPosition().blockCharFormat(); - format.setProperty(Cantor::DefaultHighlighter::BlockTypeProperty, Cantor::DefaultHighlighter::NoHighlightBlock); - firstCursorPosition().setBlockCharFormat(format); + connect(m_textItem, SIGNAL(moveToPrevious(int, qreal)), + this, SLOT(moveToPreviousEntry(int, qreal))); + connect(m_textItem, SIGNAL(moveToNext(int, qreal)), + this, SLOT(moveToNextEntry(int, qreal))); + connect(m_textItem, SIGNAL(execute()), this, SLOT(evaluate())); + connect(m_textItem, SIGNAL(doubleClick()), this, SLOT(resolveImagesAtCursor())); } LatexEntry::~LatexEntry() { - -} - -int LatexEntry::type() -{ - return Type; -} - -QTextCursor LatexEntry::firstValidCursorPosition() -{ - return firstCursorPosition(); } -QTextCursor LatexEntry::lastValidCursorPosition() +void LatexEntry::populateMenu(KMenu *menu, const QPointF& pos) { - return lastCursorPosition(); -} - -QTextCursor LatexEntry::closestValidCursor(const QTextCursor& cursor) -{ - return QTextCursor(cursor); + bool imageSelected = false; + QTextCursor cursor = m_textItem->textCursor(); + const QChar repl = QChar::ObjectReplacementCharacter; + if (cursor.hasSelection()) { + QString selection = m_textItem->textCursor().selectedText(); + imageSelected = selection.contains(repl); + } else { + // we need to try both the current cursor and the one after the that + cursor = m_textItem->cursorForPosition(pos); + for (int i = 2; i; --i) { + int p = cursor.position(); + if (m_textItem->document()->characterAt(p-1) == repl && + cursor.charFormat().hasProperty(EpsRenderer::CantorFormula)) { + m_textItem->setTextCursor(cursor); + imageSelected = true; + break; + } + cursor.movePosition(QTextCursor::NextCharacter); + } + } + if (imageSelected) { + menu->addAction(i18n("Show LaTeX code"), this, SLOT(resolveImagesAtCursor())); + menu->addSeparator(); + } + WorksheetEntry::populateMenu(menu, pos); } -bool LatexEntry::isValidCursor(const QTextCursor& cursor) +int LatexEntry::type() const { - int pos = cursor.position(); - return (firstValidPosition() <= pos && pos <= lastValidPosition()); + return Type; } bool LatexEntry::isEmpty() { - QTextCursor cursor = firstValidCursorPosition(); - cursor.setPosition(lastValidPosition(), QTextCursor::KeepAnchor); - return cursor.selection().isEmpty(); -} - -bool LatexEntry::worksheetMouseDoubleClickEvent(QMouseEvent* event, const QTextCursor& /*cursor*/) -{ - if(!m_isShowingCode) - { - m_isShowingCode=true; - QTextCursor cursor=firstValidCursorPosition(); - QString code=qVariantValue(cursor.charFormat().property(FormulaTextObject::LatexCode)); - kDebug()<<"code: "<document()->isEmpty(); } bool LatexEntry::acceptRichText() { return false; } -bool LatexEntry::acceptsDrop(const QTextCursor& cursor) -{ - Q_UNUSED(cursor); - - return true; -} - -bool LatexEntry::worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor) +bool LatexEntry::focusEntry(int pos, qreal xCoord) { - Q_UNUSED(cursor); - KMenu* defaultMenu = new KMenu(m_worksheet); - - defaultMenu->addAction(KStandardAction::cut(m_worksheet)); - defaultMenu->addAction(KStandardAction::copy(m_worksheet)); - defaultMenu->addAction(KStandardAction::paste(m_worksheet)); - defaultMenu->addSeparator(); - - if(!m_worksheet->isRunning()) - defaultMenu->addAction(KIcon("system-run"),i18n("Evaluate Worksheet"),m_worksheet,SLOT(evaluate()),0); - else - defaultMenu->addAction(KIcon("process-stop"),i18n("Interrupt"),m_worksheet,SLOT(interrupt()),0); - defaultMenu->addSeparator(); - - defaultMenu->addAction(KIcon("edit-delete"),i18n("Remove Entry"), m_worksheet, SLOT(removeCurrentEntry())); - - createSubMenuInsert(defaultMenu); - - defaultMenu->popup(event->globalPos()); - + if (aboutToBeRemoved()) + return false; + m_textItem->setFocusAt(pos, xCoord); return true; } - void LatexEntry::setContent(const QString& content) { - firstValidCursorPosition().insertText(content); + m_textItem->setPlainText(content); } void LatexEntry::setContent(const QDomElement& content, const KZip& file) { - QString latexCode= content.text(); + QString latexCode = content.text(); kDebug() << latexCode; - QTextCursor cursor=firstValidCursorPosition(); - cursor.setPosition(lastValidPosition(), QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - cursor=firstValidCursorPosition(); + m_textItem->document()->clear(); + QTextCursor cursor = m_textItem->textCursor(); + cursor.movePosition(QTextCursor::Start); if(content.hasAttribute("filename")) { const KArchiveEntry* imageEntry=file.directory()->entry(content.attribute("filename")); if (imageEntry&&imageEntry->isFile()) { const KArchiveFile* imageFile=static_cast(imageEntry); QString dir=KGlobal::dirs()->saveLocation("tmp", "cantor/"); imageFile->copyTo(dir); QString imagePath=QString(dir+QLatin1Char('/')+imageFile->name()); KUrl internal=KUrl(imagePath); internal.setProtocol("internal"); - bool success=m_worksheet->resultProxy()->renderEpsToResource(imagePath); - kDebug()<<"rendering successfull? "<epsRenderer()->render(m_textItem->document(), imagePath); + kDebug()<<"rendering successfull? " << !format.name().isEmpty(); - QTextCharFormat formulaFormat; - formulaFormat.setObjectType(FormulaTextObject::FormulaTextFormat); - formulaFormat.setProperty( FormulaTextObject::Data,imagePath); - formulaFormat.setProperty( FormulaTextObject::ResourceUrl, internal); - formulaFormat.setProperty( FormulaTextObject::LatexCode, latexCode); - formulaFormat.setProperty( FormulaTextObject::FormulaType, Cantor::LatexRenderer::LatexMethod); //So far only latex is supported - cursor.insertText(QString(QChar::ObjectReplacementCharacter), formulaFormat); - m_isShowingCode=false; - }else + format.setProperty(EpsRenderer::CantorFormula, + EpsRenderer::LatexFormula); + format.setProperty(EpsRenderer::ImagePath, imagePath); + format.setProperty(EpsRenderer::Code, latexCode); + cursor.insertText(QString(QChar::ObjectReplacementCharacter), format); + } else { cursor.insertText(latexCode); - m_isShowingCode=true; } - }else + } else { cursor.insertText(latexCode); - m_isShowingCode=true; } } + + QDomElement LatexEntry::toXml(QDomDocument& doc, KZip* archive) { Q_UNUSED(archive); - QString html; QString image; - if(m_isShowingCode) - { - QTextCursor cursor = firstValidCursorPosition(); - cursor.setPosition(lastValidPosition(), QTextCursor::KeepAnchor); - html = cursor.selectedText(); - }else + + QString latex = latexCode(); + + if (isOneImageOnly()) { - QTextCursor cursor=firstValidCursorPosition(); - html=qVariantValue(cursor.charFormat().property(FormulaTextObject::LatexCode)); - if(cursor.charFormat().intProperty(FormulaTextObject::FormulaType)==FormulaTextObject::LatexFormula) - image=qVariantValue(cursor.charFormat().property(FormulaTextObject::Data)); + QTextCursor cursor = m_textItem->textCursor(); + + if(cursor.charFormat().hasProperty(EpsRenderer::CantorFormula)) + image = qVariantValue(cursor.charFormat().property(EpsRenderer::ImagePath)); } QDomElement el = doc.createElement("Latex"); if(!image.isNull()) { KUrl url(image); el.setAttribute("filename", url.fileName()); archive->addLocalFile(image, url.fileName()); } - kDebug() << html; - QDomText text=doc.createTextNode(html); + kDebug() << latex; + QDomText text = doc.createTextNode(latex); el.appendChild(text); return el; } -QString LatexEntry::toPlain(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq) +QString LatexEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) { Q_UNUSED(commandSep); if (commentStartingSeq.isEmpty()) return QString(); - QString text; - if(m_isShowingCode) - { - QTextCursor cursor = firstValidCursorPosition(); - cursor.setPosition(lastValidPosition(), QTextCursor::KeepAnchor); - text = cursor.selection().toPlainText(); - }else - { - QTextCursor cursor=firstValidCursorPosition(); - text=qVariantValue(cursor.charFormat().property(FormulaTextObject::LatexCode)); - } + + QString text = latexCode(); if (!commentEndingSeq.isEmpty()) return commentStartingSeq + text + commentEndingSeq + "\n"; return commentStartingSeq + text.replace("\n", "\n" + commentStartingSeq) + "\n"; } void LatexEntry::interruptEvaluation() { } -bool LatexEntry::evaluate(bool current) +bool LatexEntry::evaluate(EvaluationOption evalOp) { - Q_UNUSED(current); + if (isOneImageOnly()) + return true; // the image is rendered already - QTextDocument *doc = m_frame->document(); - QTextCursor cursor=firstValidCursorPosition(); - cursor.setPosition(lastValidPosition(), QTextCursor::KeepAnchor); - QString latexCode=cursor.selection().toPlainText(); - cursor.removeSelectedText(); + QString latex = latexCode(); - Cantor::LatexRenderer* renderer=new Cantor::LatexRenderer(this); - renderer->setLatexCode(latexCode); + Cantor::LatexRenderer* renderer = new Cantor::LatexRenderer(this); + renderer->setLatexCode(latex); renderer->setEquationOnly(false); renderer->setMethod(Cantor::LatexRenderer::LatexMethod); renderer->renderBlocking(); - bool success=m_worksheet->resultProxy()->renderEpsToResource(renderer->imagePath()); - kDebug()<<"rendering successfull? "<imagePath(); - KUrl internal=KUrl(path); - internal.setProtocol("internal"); + bool success; + QTextImageFormat formulaFormat; + if (renderer->renderingSuccessful()) { + EpsRenderer* epsRend = worksheet()->epsRenderer(); + formulaFormat = epsRend->render(m_textItem->document(), renderer); + success = !formulaFormat.name().isEmpty(); + } else { + success = false; + } - kDebug()<<"int: "<imagePath()); - formulaFormat.setProperty( FormulaTextObject::ResourceUrl, internal); - formulaFormat.setProperty( FormulaTextObject::LatexCode, latexCode); - formulaFormat.setProperty( FormulaTextObject::FormulaType, renderer->method()); + if (!success) { + delete renderer; + evaluateNext(evalOp); + return false; + } + QTextCursor cursor = m_textItem->textCursor(); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); cursor.insertText(QString(QChar::ObjectReplacementCharacter), formulaFormat); delete renderer; - m_isShowingCode=false; + evaluateNext(evalOp); return true; } -void LatexEntry::update() +void LatexEntry::updateEntry() { - if(!m_isShowingCode) + QTextCursor cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter)); + while (!cursor.isNull()) { kDebug()<<"found a formula... rendering the eps..."; - QTextCursor cursor=firstValidCursorPosition(); QTextCharFormat format=cursor.charFormat(); - QUrl url=qVariantValue(format.property(FormulaTextObject::Data)); - bool success=m_worksheet->resultProxy()->renderEpsToResource(url); - kDebug()<<"rendering successfull? "<(format.property(EpsRenderer::ImagePath)); + QSizeF s = worksheet()->epsRenderer()->renderToResource(m_textItem->document(), url); + kDebug()<<"rendering successfull? "<< !s.isValid(); //HACK: reinsert this image, to make sure the layout is updated to the new size - cursor.deletePreviousChar(); - cursor.insertText(QString(QChar::ObjectReplacementCharacter), format); + //cursor.removeSelectedText(); + //cursor.insertText(QString(QChar::ObjectReplacementCharacter), format); + cursor.movePosition(QTextCursor::NextCharacter); - cursor = m_worksheet->document()->find(QString(QChar::ObjectReplacementCharacter), cursor); + cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter), cursor); } } +void LatexEntry::resolveImagesAtCursor() +{ + QTextCursor cursor = m_textItem->textCursor(); + if (!cursor.hasSelection()) + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + + cursor.insertText(m_textItem->resolveImages(cursor)); +} + +QString LatexEntry::latexCode() +{ + QTextCursor cursor = m_textItem->textCursor(); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + + QString code = m_textItem->resolveImages(cursor); + code.replace(QChar::ParagraphSeparator, '\n'); //Replace the U+2029 paragraph break by a Normal Newline + code.replace(QChar::LineSeparator, '\n'); //Replace the line break by a Normal Newline + return code; +} +bool LatexEntry::isOneImageOnly() +{ + QTextCursor cursor = m_textItem->textCursor(); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + + return (cursor.selectionEnd() == 1 && cursor.selectedText() == QString(QChar::ObjectReplacementCharacter)); +} +int LatexEntry::searchText(QString text, QString pattern, + QTextDocument::FindFlags qt_flags) +{ + Qt::CaseSensitivity caseSensitivity; + if (qt_flags & QTextDocument::FindCaseSensitively) + caseSensitivity = Qt::CaseSensitive; + else + caseSensitivity = Qt::CaseInsensitive; + + int position; + if (qt_flags & QTextDocument::FindBackward) + position = text.lastIndexOf(pattern, -1, caseSensitivity); + else + position = text.indexOf(pattern, 0, caseSensitivity); + + return position; +} + +WorksheetCursor LatexEntry::search(QString pattern, unsigned flags, + QTextDocument::FindFlags qt_flags, + const WorksheetCursor& pos) +{ + if (!(flags & WorksheetEntry::SearchLaTeX)) + return WorksheetCursor(); + if (pos.isValid() && (pos.entry() != this || pos.textItem() != m_textItem)) + return WorksheetCursor(); + + QTextCursor textCursor = m_textItem->search(pattern, qt_flags, pos); + int position; + QString latex; + const QString repl = QString(QChar::ObjectReplacementCharacter); + QTextCursor latexCursor = m_textItem->search(repl, qt_flags, pos); + + while (!latexCursor.isNull()) { + latex = m_textItem->resolveImages(latexCursor); + position = searchText(latex, pattern, qt_flags); + if (position >= 0) { + break; + } + WorksheetCursor c(this, m_textItem, latexCursor); + latexCursor = m_textItem->search(repl, qt_flags, c); + } + + if (latexCursor.isNull()) { + if (textCursor.isNull()) + return WorksheetCursor(); + else + return WorksheetCursor(this, m_textItem, textCursor); + } else { + if (textCursor.isNull() || latexCursor < textCursor) { + int start = latexCursor.selectionStart(); + latexCursor.insertText(latex); + QTextCursor c = m_textItem->textCursor(); + c.setPosition(start + position); + c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, + pattern.length()); + return WorksheetCursor(this, m_textItem, c); + } else { + return WorksheetCursor(this, m_textItem, textCursor); + } + } +} + +void LatexEntry::layOutForWidth(double w, bool force) +{ + if (size().width() == w && !force) + return; + + m_textItem->setGeometry(0, 0, w); + setSize(QSizeF(w, m_textItem->height() + VerticalMargin)); +} + +bool LatexEntry::wantToEvaluate() +{ + return !isOneImageOnly(); +} diff --git a/src/latexentry.h b/src/latexentry.h index 1dc9cae1..f0cd2ad4 100644 --- a/src/latexentry.h +++ b/src/latexentry.h @@ -1,65 +1,78 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- - Copyright (C) 2011 Alexander Rieder + Copyright (C) 2009 Alexander Rieder + Copyright (C) 2012 Martin Kuettler */ -#ifndef _LATEXENTRY_H -#define _LATEXENTRY_H +#ifndef LATEXENTRY_H +#define LATEXENTRY_H #include "worksheetentry.h" +#include "worksheettextitem.h" class LatexEntry : public WorksheetEntry { + Q_OBJECT + public: - LatexEntry( QTextCursor position, Worksheet* parent); + LatexEntry(Worksheet* worksheet); ~LatexEntry(); - enum {Type = 5}; - int type(); + enum {Type = UserType + 5}; + int type() const; bool isEmpty(); - QTextCursor closestValidCursor(const QTextCursor& cursor); - QTextCursor firstValidCursorPosition(); - QTextCursor lastValidCursorPosition(); - bool isValidCursor(const QTextCursor& cursor); - - bool worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor); - bool worksheetMouseDoubleClickEvent(QMouseEvent* event, const QTextCursor& cursor); - bool acceptRichText(); - bool acceptsDrop(const QTextCursor& cursor); + + bool focusEntry(int pos = WorksheetTextItem::TopLeft, qreal xCoord = 0); void setContent(const QString& content); void setContent(const QDomElement& content, const KZip& file); QDomElement toXml(QDomDocument& doc, KZip* archive); - QString toPlain(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq); + QString toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq); void interruptEvaluation(); - bool evaluate(bool current); + void layOutForWidth(double w, bool force = false); + + int searchText(QString text, QString pattern, + QTextDocument::FindFlags qt_flags); + WorksheetCursor search(QString pattern, unsigned flags, + QTextDocument::FindFlags qt_flags, + const WorksheetCursor& pos = WorksheetCursor()); + public slots: - void update(); + bool evaluate(EvaluationOption evalOp = FocusNext); + void resolveImagesAtCursor(); + void updateEntry(); + void populateMenu(KMenu *menu, const QPointF& pos); + + protected: + bool wantToEvaluate(); private: - bool m_isShowingCode; + QString latexCode(); + bool isOneImageOnly(); + private: + WorksheetTextItem* m_textItem; }; -#endif /* _LATEXENTRY_H */ +#endif // LATEXENTRY_H diff --git a/src/lib/defaulthighlighter.cpp b/src/lib/defaulthighlighter.cpp index cd05d17d..e5dd57fe 100644 --- a/src/lib/defaulthighlighter.cpp +++ b/src/lib/defaulthighlighter.cpp @@ -1,386 +1,408 @@ /* 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) 2006 David Saxton */ #include "defaulthighlighter.h" #include -#include +#include +#include +#include #include #include #include #include using namespace Cantor; struct HighlightingRule { QRegExp regExp; QTextCharFormat format; }; bool operator==(const HighlightingRule& rule1, const HighlightingRule& rule2) { return rule1.regExp == rule2.regExp; } +struct PairOpener { + PairOpener() : position(-1), type(-1) { } + PairOpener(int p, int t) : position(p), type(t) { } + + int position; + int type; +}; + + class Cantor::DefaultHighlighterPrivate { public: - QTextEdit* parent; + QTextCursor cursor; //Character formats to use for the highlighing QTextCharFormat functionFormat; QTextCharFormat variableFormat; QTextCharFormat objectFormat; QTextCharFormat keywordFormat; QTextCharFormat numberFormat; QTextCharFormat operatorFormat; QTextCharFormat errorFormat; QTextCharFormat commentFormat; QTextCharFormat stringFormat; QTextCharFormat matchingPairFormat; + QTextCharFormat mismatchingPairFormat; int lastBlockNumber; int lastPosition; // each two consecutive items build a pair QList pairs; QList regExpRules; QHash wordRules; }; -DefaultHighlighter::DefaultHighlighter(QTextEdit* parent) +DefaultHighlighter::DefaultHighlighter(QObject* parent) : QSyntaxHighlighter(parent), d(new DefaultHighlighterPrivate) { - d->parent=parent; + d->cursor = QTextCursor(); d->lastBlockNumber=-1; d->lastPosition=-1; addPair('(', ')'); addPair('[', ']'); addPair('{', '}'); updateFormats(); connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), this, SLOT(updateFormats())); - connect(parent, SIGNAL(cursorPositionChanged()), this, SLOT(positionChanged())); } -DefaultHighlighter::~ DefaultHighlighter() +DefaultHighlighter::~DefaultHighlighter() { delete d; } +void DefaultHighlighter::setTextItem(QGraphicsTextItem* item) +{ + d->cursor = item->textCursor(); + setDocument(item->document()); + // make sure every item is connected only once + item->disconnect(this, SLOT(positionChanged(QTextCursor))); + // QGraphicsTextItem has no signal cursorPositionChanged, but item really + // is a WorksheetTextItem + connect(item, SIGNAL(cursorPositionChanged(QTextCursor)), + this, SLOT(positionChanged(QTextCursor))); + + d->lastBlockNumber = -1; + d->lastPosition = -1; +} + bool DefaultHighlighter::skipHighlighting(const QString& text) { - return (text.isEmpty() || currentBlockType() == NoHighlightBlock || currentBlockType() == UnknownBlock ); + return text.isEmpty(); } void DefaultHighlighter::highlightBlock(const QString& text) { - QTextCursor cursor; + kDebug() << text; + const QTextCursor& cursor = d->cursor; d->lastBlockNumber = cursor.blockNumber(); if (skipHighlighting(text)) return; highlightPairs(text); highlightWords(text); highlightRegExps(text); } void DefaultHighlighter::addPair(const QChar& openSymbol, const QChar& closeSymbol) { Q_ASSERT(!d->pairs.contains(openSymbol)); Q_ASSERT(!d->pairs.contains(closeSymbol)); d->pairs << openSymbol << closeSymbol; } void DefaultHighlighter::highlightPairs(const QString& text) { - const QTextCursor& cursor = d->parent->textCursor(); + kDebug() << text; + const QTextCursor& cursor = d->cursor; int cursorPos = -1; - if ( cursor.blockNumber() == currentBlock().blockNumber() ) { + if (cursor.blockNumber() == currentBlock().blockNumber() ) { cursorPos = cursor.position() - currentBlock().position(); // when text changes, this will be called before the positionChanged signal // gets emitted. Hence update the position so we don't highlight twice d->lastPosition = cursor.position(); } - // positions of opened pairs - // key: same index as the opener has in d->pairs - // value: position in text where it was opened - QHash* > opened; - QHash* >::iterator it; - - ///TODO: use setCurrentBlockUserData to keep track of matched pairs - /// of course, keep track of changes and update the cache properly - for ( int i = 0; i < text.size(); ++i ) { - int idx = d->pairs.indexOf(text[i]); - if ( idx != -1 ) { - if ( idx % 2 == 0 ) { - // opener of a pair - it = opened.find(idx); - if ( it == opened.end() ) { - it = opened.insert(idx, new QStack()); - } - (*it)->push(i); - } else { - // closer of a pair, find opener - it = opened.find(idx - 1); - if ( it == opened.end() || (*it)->isEmpty() ) { - // unmatched - setFormat(i, 1, errorFormat()); - } else { - // matched - int lastPos = (*it)->pop(); - // check if we have to highlight the matched pair - // at the current cursor position - if ( cursorPos != -1 && - ( lastPos == cursorPos || lastPos == cursorPos - 1 || - i == cursorPos || i == cursorPos - 1 ) ) - { - // yep, we want it highlighted - setFormat(lastPos, 1, matchingPairFormat()); - setFormat(i, 1, matchingPairFormat()); - } - } - } - } + QStack opened; + + for (int i = 0; i < text.size(); ++i) { + int idx = d->pairs.indexOf(text[i]); + if (idx == -1) + continue; + if (idx % 2 == 0) { //opener of a pair + opened.push(PairOpener(i, idx)); + } else if (opened.isEmpty()) { //closer with no previous opener + setFormat(i, 1, errorFormat()); + } else if (opened.top().type == idx - 1) { //closer with matched opener + int openPos = opened.pop().position; + if (cursorPos != -1 && + (openPos == cursorPos || openPos == cursorPos - 1 || + i == cursorPos || i == cursorPos - 1)) { + setFormat(openPos, 1, matchingPairFormat()); + setFormat(i, 1, matchingPairFormat()); + } + } else { //closer with mismatching opener + int openPos = opened.pop().position; + setFormat(openPos, 1, mismatchingPairFormat()); + setFormat(i, 1, mismatchingPairFormat()); + } } // handled unterminated pairs - foreach ( QStack* positions, opened ) { - while ( !positions->isEmpty() ) { - setFormat(positions->pop(), 1, errorFormat()); - } + while (!opened.isEmpty()) { + int position = opened.pop().position; + setFormat(position, 1, errorFormat()); } - qDeleteAll(opened.values()); + } void DefaultHighlighter::highlightWords(const QString& text) { kDebug() << "DefaultHighlighter::highlightWords"; const QStringList& words = text.split(QRegExp("\\b"), QString::SkipEmptyParts); int count; int pos = 0; const int n = words.size(); for (int i = 0; i < n; ++i) { count = words[i].size(); QString word = words[i]; //kind of a HACK: //look at previous words, if they end with allowed characters, //prepend them to the current word. This allows for example //to highlight words that start with a "Non-word"-character //e.g. %pi in the scilab backend. kDebug() << "nonSeparatingCharacters().isNull(): " << nonSeparatingCharacters().isNull(); if(!nonSeparatingCharacters().isNull()) { for(int j = i - 1; j >= 0; j--) { kDebug() << "j: " << j << "w: " << words[j]; const QString& w = words[j]; const QString exp = QString("(%1)*$").arg(nonSeparatingCharacters()); kDebug() << "exp: " << exp; int idx = w.indexOf(QRegExp(exp)); const QString& s = w.mid(idx); kDebug() << "s: " << s; if(s.size() > 0) { pos -= s.size(); count += s.size(); word = s + word; } else{ break; } } } word = word.trimmed(); kDebug() << "highlighing: " << word; if (d->wordRules.contains(word)) { setFormat(pos, count, d->wordRules[word]); } pos += count; } } void DefaultHighlighter::highlightRegExps(const QString& text) { foreach (const HighlightingRule& rule, d->regExpRules) { int index = rule.regExp.indexIn(text); while (index >= 0) { int length = rule.regExp.matchedLength(); setFormat(index, length, rule.format); index = rule.regExp.indexIn(text, index + length); } } } -DefaultHighlighter::BlockType DefaultHighlighter::currentBlockType() -{ - QTextBlock block=currentBlock(); - BlockType type=(BlockType) block.charFormat().intProperty(BlockTypeProperty); - - return type; -} - QTextCharFormat DefaultHighlighter::functionFormat() const { return d->functionFormat; } QTextCharFormat DefaultHighlighter::variableFormat() const { return d->variableFormat; } QTextCharFormat DefaultHighlighter::objectFormat() const { return d->objectFormat; } QTextCharFormat DefaultHighlighter::keywordFormat() const { return d->keywordFormat; } QTextCharFormat DefaultHighlighter::numberFormat() const { return d->numberFormat; } QTextCharFormat DefaultHighlighter::operatorFormat() const { return d->operatorFormat; } QTextCharFormat DefaultHighlighter::errorFormat() const { return d->errorFormat; } QTextCharFormat DefaultHighlighter::commentFormat() const { return d->commentFormat; } QTextCharFormat DefaultHighlighter::stringFormat() const { return d->stringFormat; } QTextCharFormat DefaultHighlighter::matchingPairFormat() const { return d->matchingPairFormat; } +QTextCharFormat DefaultHighlighter::mismatchingPairFormat() const +{ + return d->mismatchingPairFormat; +} + void DefaultHighlighter::updateFormats() { //initialize char-formats KColorScheme scheme(QPalette::Active); d->functionFormat.setForeground(scheme.foreground(KColorScheme::LinkText)); d->functionFormat.setFontWeight(QFont::DemiBold); d->variableFormat.setForeground(scheme.foreground(KColorScheme::ActiveText)); d->objectFormat.setForeground(scheme.foreground(KColorScheme::NormalText)); d->objectFormat.setFontWeight(QFont::Bold); d->keywordFormat.setForeground(scheme.foreground(KColorScheme::NeutralText)); d->keywordFormat.setFontWeight(QFont::Bold); d->numberFormat.setForeground(scheme.foreground(KColorScheme::NeutralText)); d->operatorFormat.setForeground(scheme.foreground(KColorScheme::NormalText)); d->operatorFormat.setFontWeight(QFont::Bold); d->errorFormat.setForeground(scheme.foreground(KColorScheme::NormalText)); d->errorFormat.setUnderlineColor(scheme.foreground(KColorScheme::NegativeText).color()); d->errorFormat.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline); d->commentFormat.setForeground(scheme.foreground(KColorScheme::InactiveText)); d->stringFormat.setForeground(scheme.foreground(KColorScheme::PositiveText)); d->matchingPairFormat.setForeground(scheme.foreground(KColorScheme::NeutralText)); d->matchingPairFormat.setBackground(scheme.background(KColorScheme::NeutralBackground)); + + d->mismatchingPairFormat.setForeground(scheme.foreground(KColorScheme::NegativeText)); + d->mismatchingPairFormat.setBackground(scheme.background(KColorScheme::NegativeBackground)); } -void DefaultHighlighter::positionChanged() -{ - const QTextCursor& cursor = d->parent->textCursor(); - if ( cursor.blockNumber() != d->lastBlockNumber ) { +void DefaultHighlighter::positionChanged(QTextCursor cursor) +{ + if (!cursor.isNull() && cursor.document() != document()) + // A new item notified us, but we did not yet change our document. + // We are waiting for that to happen. + return; + + d->cursor = cursor; + if ( (cursor.isNull() || cursor.blockNumber() != d->lastBlockNumber) && + d->lastBlockNumber >= 0 ) { // remove highlight from last focused block - rehighlightBlock(d->parent->document()->findBlockByNumber(d->lastBlockNumber)); + rehighlightBlock(document()->findBlockByNumber(d->lastBlockNumber)); + } + + if (cursor.isNull()) { + d->lastBlockNumber = -1; + d->lastPosition = -1; + return; } d->lastBlockNumber = cursor.blockNumber(); if ( d->lastPosition == cursor.position() ) { return; } rehighlightBlock(cursor.block()); d->lastPosition = cursor.position(); } void DefaultHighlighter::addRule(const QString& word, const QTextCharFormat& format) { d->wordRules[word] = format; } void DefaultHighlighter::addRule(const QRegExp& regexp, const QTextCharFormat& format) { HighlightingRule rule = { regexp, format }; d->regExpRules.removeAll(rule); d->regExpRules.append(rule); } void DefaultHighlighter::removeRule(const QString& word) { d->wordRules.remove(word); } void DefaultHighlighter::removeRule(const QRegExp& regexp) { HighlightingRule rule = { regexp, QTextCharFormat() }; d->regExpRules.removeAll(rule); } QString DefaultHighlighter::nonSeparatingCharacters() const { return QString(); } #include "defaulthighlighter.moc" diff --git a/src/lib/defaulthighlighter.h b/src/lib/defaulthighlighter.h index 7200ffd6..307fa0dd 100644 --- a/src/lib/defaulthighlighter.h +++ b/src/lib/defaulthighlighter.h @@ -1,213 +1,222 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #ifndef DEFAULTHIGHLIGHTER_H #define DEFAULTHIGHLIGHTER_H #include "cantor_export.h" #include +class QGraphicsTextItem; + namespace Cantor { class DefaultHighlighterPrivate; /** * The DefaultHighlighter is an implementation QSyntaxHighlighter. * It covers most common cases of syntax highlighting for Cantor's command entries. * * When creating a custom highlighter, for example for a new backend, you should use * the provided functions addPairs(), addRule() and/or addRules(). * * If you need more specific functionality, subclass highlightBlock(). Usually it's a good idea to also call * DefaultHighlighter's implementation from it. * * @author Alexander Rieder */ class CANTOR_EXPORT DefaultHighlighter : public QSyntaxHighlighter { Q_OBJECT public: - enum BlockType {UnknownBlock = 0, ErrorBlock = 1, ResultBlock = 2, CommandBlock = 3, NoHighlightBlock = 4}; - enum { BlockTypeProperty = QTextFormat::UserProperty +25 }; - DefaultHighlighter(QTextEdit* parent); + DefaultHighlighter(QObject* parent); ~DefaultHighlighter(); + /** + * Change the item beeing highlighted. + */ + void setTextItem(QGraphicsTextItem* item); + + public slots: + /** + * Called when the cursor moved. Rehighlights accordingly. + */ + void positionChanged(QTextCursor); + protected: /** * This method is called by Cantor's KTextEdit and is where all the highlighting must take place. * The default implementation calls highlightPairs(), highlightWords() and highlightRegExps(). * */ virtual void highlightBlock(const QString& text); bool skipHighlighting(const QString& text); - BlockType currentBlockType(); - QTextCharFormat functionFormat() const; QTextCharFormat variableFormat() const; QTextCharFormat objectFormat() const; QTextCharFormat keywordFormat() const; QTextCharFormat numberFormat() const; QTextCharFormat operatorFormat() const; QTextCharFormat errorFormat() const; QTextCharFormat commentFormat() const; QTextCharFormat stringFormat() const; QTextCharFormat matchingPairFormat() const; + QTextCharFormat mismatchingPairFormat() const; /** * Call this to add a pair of symbols for highlighting. * The default implementation of the class already adds (), {} and [], so no need to add those. * For example, if you wanted to highlight angle-brackets, you would use: * @code * addPair('<', '>'); * @endcode * @param openSymbol the opening symbol of the pair * @param closeSymbol the closing symbol of the pair * @sa highlightPairs */ void addPair(const QChar& openSymbol, const QChar& closeSymbol); /** * Highlights all instances of the @p word in the text with the specified @p format * @param word the word to highlight * @param format the format to be used for displaying the word */ void addRule(const QString& word, const QTextCharFormat& format); /** * Highlights all parts of the text matched by the regular expression @p regexp in the text * with the specified @p format * @param regexp the regular expression used to look for matches * @param format the format used to display the matching parts of the text */ void addRule(const QRegExp& regexp, const QTextCharFormat& format); /** * Convenience method, highlights all items in @p conditions with the specified @p format * @code * QStringList greenWords; * greenWords << "tree" << "forest" << "grass"; * addRules(greenWords, greenWordFormat); * @endcode * @param conditions any Qt container of QRegExp or QString. */ template void addRules(const Container& conditions, const QTextCharFormat& format); /** * Convenience method, equivalent to @code addRules(functions, functionFormat()) @endcode */ template void addFunctions(const Container& functions); /** * Convenience method, equivalent to @code addRules(variables, variableFormat()) @endcode */ template void addVariables(const Container& variables); /** * Convenience method, equivalent to @code addRules(keywords, keywordFormat()) @endcode */ template void addKeywords(const Container& keywords); /** * Removes any rules previously added for the word @p word */ void removeRule(const QString& word); /** * Removes any rules previously added for the regular expression @p regexp */ void removeRule(const QRegExp& regexp); /** * Convenience method, removes all rules with conditions from @p conditions * @sa removeRule, addRules */ template void removeRules(const Container& conditions); /** * Highlight pairs added with addPair() * @sa addPair */ void highlightPairs(const QString& text); /** * Highlights words added with addRule() * @sa addRule, addRules */ void highlightWords(const QString& text); /** * Highlights all matches from regular expressions added with addRule() * @sa addRule, addRules */ void highlightRegExps(const QString& text); /** * Returns a string that contains a regular expression that matches for characters thar are allowed inside * words for this backend. For example, maxima or scilab allow % at the beginning of variable names */ virtual QString nonSeparatingCharacters() const; private slots: - void positionChanged(); void updateFormats(); private: DefaultHighlighterPrivate* d; }; template void DefaultHighlighter::addRules(const Container& conditions, const QTextCharFormat& format) { typename Container::const_iterator i = conditions.constBegin(); typename Container::const_iterator end = conditions.constEnd(); for (;i != end; ++i) { addRule(*i, format); } } template void DefaultHighlighter::addFunctions(const Container& functions) { addRules(functions, functionFormat()); } template void DefaultHighlighter::addKeywords(const Container& keywords) { addRules(keywords, keywordFormat()); } template void DefaultHighlighter::addVariables(const Container& variables) { addRules(variables, variableFormat()); } template void DefaultHighlighter::removeRules(const Container& conditions) { typename Container::const_iterator i = conditions.constBegin(); typename Container::const_iterator end = conditions.constEnd(); for (;i != end; ++i) { removeRule(*i); } } } #endif diff --git a/src/lib/session.cpp b/src/lib/session.cpp index d4f3e0f2..73256c3d 100644 --- a/src/lib/session.cpp +++ b/src/lib/session.cpp @@ -1,125 +1,125 @@ /* 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 "session.h" using namespace Cantor; #include "backend.h" class Cantor::SessionPrivate { public: SessionPrivate() { backend=0; expressionCount=0; typesettingEnabled=false; } Backend* backend; Session::Status status; bool typesettingEnabled; int expressionCount; }; Session::Session( Backend* backend ) : QObject(backend), d(new SessionPrivate) { d->backend=backend; } Session::~Session() { delete d; } Expression* Session::evaluateExpression(const QString& command) { return evaluateExpression(command, Expression::DoNotDelete); } Backend* Session::backend() { return d->backend; } Cantor::Session::Status Session::status() { return d->status; } void Session::changeStatus(Session::Status newStatus) { d->status=newStatus; emit statusChanged(newStatus); } void Session::setTypesettingEnabled(bool enable) { d->typesettingEnabled=enable; } bool Session::isTypesettingEnabled() { return d->typesettingEnabled; } CompletionObject* Session::completionFor(const QString& cmd, int index) { Q_UNUSED(cmd); Q_UNUSED(index); //Return 0 per default, so Backends not offering tab completions don't have //to reimplement this. This method should only be called on backends with //the Completion Capability flag return 0; } SyntaxHelpObject* Session::syntaxHelpFor(const QString& cmd) { Q_UNUSED(cmd); //Return 0 per default, so Backends not offering tab completions don't have //to reimplement this. This method should only be called on backends with //the SyntaxHelp Capability flag return 0; } -QSyntaxHighlighter* Session::syntaxHighlighter(QTextEdit* parent) +QSyntaxHighlighter* Session::syntaxHighlighter(QObject* parent) { Q_UNUSED(parent); return 0; } QAbstractItemModel* Session::variableModel() { //Return 0 per default, so Backends not offering variable management don't //have to reimplement this. This method should only be called on backends with //VariableManagement Capability flag return 0; } int Session::nextExpressionId() { return d->expressionCount++; } #include "session.moc" diff --git a/src/lib/session.h b/src/lib/session.h index b6fdc844..0d83cbf0 100644 --- a/src/lib/session.h +++ b/src/lib/session.h @@ -1,191 +1,191 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- Copyright (C) 2009 Alexander Rieder */ #ifndef _SESSION_H #define _SESSION_H #include #include "cantor_export.h" #include "expression.h" class QTextEdit; class QSyntaxHighlighter; class QAbstractItemModel; /** * Namespace collecting all Classes of the Cantor Libraries */ namespace Cantor { class Backend; class SessionPrivate; class CompletionObject; class SyntaxHelpObject; /** * The Session object is the main class used to interact with a Backend. * It is used to evaluate Expressions, get completions, syntax highlighting, etc. * * @author Alexander Rieder */ class CANTOR_EXPORT Session : public QObject { Q_OBJECT public: enum Status { Running, ///< the session is busy, running some expression Done ///< the session has done all the jobs, and is now waiting for more }; /** * Create a new Session. This should not yet set up the complete session, * thats job of the login() function * @see login() */ Session( Backend* backend); /** * Destructor */ ~Session(); /** * Login to the Session. In this function you should do anything needed to set up * the session, and make it ready for usage. The method should be implemented non * blocking. If the loging in is completed, the ready() signal must be emitted */ virtual void login() = 0; /** * Log out of the Session. Destroy everything specific to a single session, e.g. * stop all the running processes etc. * NOTE: restaring the session consists of first logout() and then login() */ virtual void logout() = 0; /** * Passes the given command to the backend and returns a Pointer * to a new Expression object, which will emit the resultArrived() * signal as soon as the computation is done. The result will * then be acessible by Expression::result() * @param command the command that should be run by the backend. * @param finishingBehavior the FinishingBehaviour that should be used for this command. @see Expression::FinishingBehaviour * @return an Expression object, representing this command */ virtual Expression* evaluateExpression(const QString& command, Expression::FinishingBehavior finishingBehavior) = 0; /** * Reimplements evaluateExpression, setting the finishingBehaviour to DoNotDelete * @see evaluateExpressionconst(QString& command, Expression::FinishingBehavior finishingBehavior) */ Expression* evaluateExpression(const QString& command); /** * Interrupts all the running calculations in this session */ virtual void interrupt() = 0; /** * Returns tab-completion, for this command/command-part. * The return type is a CompletionObject. The fetching * of the completions works asynchronously, you'll have to * listen to the done() Signal of the returned object * @param cmd The partial command that should be completed * @param index The index (cursor position) at which completion * was invoked. Defaults to -1, indicating the end of the string. * @return a Completion object, representing this completion * @see CompletionObject */ virtual CompletionObject* completionFor(const QString& cmd, int index = -1); /** * Returns Syntax help, for this command. * It returns a SyntaxHelpObject, that will fetch the * needed information asynchroneousely. You need to listen * to the done() Signal of the Object * @param cmd the command, syntax help is requested for * @return SyntaxHelpObject, representing the help request * @see SyntaxHelpObject */ virtual SyntaxHelpObject* syntaxHelpFor(const QString& cmd); /** * returns a syntax highlighter for this session - * @param parent QTextEdit the Highlighter should be operating on + * @param parent QObject the Highlighter's parent * @return QSyntaxHighlighter doing the highlighting for this Session */ - virtual QSyntaxHighlighter* syntaxHighlighter(QTextEdit* parent); + virtual QSyntaxHighlighter* syntaxHighlighter(QObject* parent); /** * returns a Model to interact with the variables * @return QAbstractItemModel to interact with the variables */ virtual QAbstractItemModel* variableModel(); /** * Enables/disables Typesetting for this session. * For this setting to make effect, the Backend must support * LaTeX typesetting (as indicated by the capabilities() flag. * @param enable true to enable, false to disable typesetting */ virtual void setTypesettingEnabled(bool enable); /** * Returns the Backend, this Session is for * @return the Backend, this Session is for */ Backend* backend(); /** * Returns the status this Session has * @return the status this Session has */ Cantor::Session::Status status(); /** * Returns wether typesetting is enabled or not * @return wether typesetting is enabled or not */ bool isTypesettingEnabled(); /** * Returns the next available Expression id * It is basically a counter, incremented for * each new Expression * @return next Expression id */ int nextExpressionId(); protected: /** * Change the status of the Session. This will cause the * stausChanged signal to be emited * @param newStatus the new status of the session */ void changeStatus(Cantor::Session::Status newStatus); signals: void statusChanged(Cantor::Session::Status newStatus); void ready(); void error(const QString& msg); private: SessionPrivate* d; }; } #endif /* _SESSION_H */ diff --git a/src/pagebreakentry.cpp b/src/pagebreakentry.cpp index b818b861..02a48803 100644 --- a/src/pagebreakentry.cpp +++ b/src/pagebreakentry.cpp @@ -1,181 +1,156 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- - Copyright (C) 2011 Martin Kuettler + Copyright (C) 2012 Martin Kuettler */ #include "pagebreakentry.h" -#include "worksheet.h" -#include -#include -#include +#include +#include +#include +#include #include -#include -PageBreakEntry::PageBreakEntry(QTextCursor position, Worksheet* parent) : - WorksheetEntry( position, parent ) +PageBreakEntry::PageBreakEntry(Worksheet* worksheet) + : WorksheetEntry(worksheet) { - QTextFrameFormat frameFormat = m_frame->frameFormat(); + m_msgItem = new WorksheetTextItem(this); - frameFormat.setBorderStyle(QTextFrameFormat::BorderStyle_None); - frameFormat.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter); + QTextCursor cursor = m_msgItem->textCursor(); + KColorScheme color = KColorScheme(QPalette::Normal, KColorScheme::View); + QTextCharFormat cformat(cursor.charFormat()); + cformat.setForeground(color.foreground(KColorScheme::InactiveText)); - m_frame->setFrameFormat(frameFormat); + cursor.insertText(i18n("--- Page Break ---"), cformat); - // do I need to call this? - update(); + setFlag(QGraphicsItem::ItemIsFocusable); } PageBreakEntry::~PageBreakEntry() { - -} - -int PageBreakEntry::type() -{ - return Type; } bool PageBreakEntry::isEmpty() { - return true; -} - - -QTextCursor PageBreakEntry::closestValidCursor(const QTextCursor& cursor) -{ - Q_UNUSED(cursor); - return firstValidCursorPosition(); -} - -QTextCursor PageBreakEntry::firstValidCursorPosition() -{ - return m_frame->lastCursorPosition(); -} - -QTextCursor PageBreakEntry::lastValidCursorPosition() -{ - return m_frame->lastCursorPosition(); -} - -bool PageBreakEntry::isValidCursor(const QTextCursor& cursor) -{ - Q_UNUSED(cursor); return false; } -bool PageBreakEntry::worksheetKeyPressEvent(QKeyEvent* event, const QTextCursor& cursor) +int PageBreakEntry::type() const { - if (WorksheetEntry::worksheetKeyPressEvent(event, cursor)) - return true; - - // Are there any other keys we should allow here? - return true; + return Type; } -bool PageBreakEntry::worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor) +void PageBreakEntry::populateMenu(KMenu *menu, const QPointF& pos) { - Q_UNUSED(cursor); - KMenu* defaultMenu = new KMenu(m_worksheet); - - if(!m_worksheet->isRunning()) - defaultMenu->addAction(KIcon("system-run"),i18n("Evaluate Worksheet"),m_worksheet,SLOT(evaluate()),0); - else - defaultMenu->addAction(KIcon("process-stop"),i18n("Interrupt"),m_worksheet,SLOT(interrupt()),0); - defaultMenu->addSeparator(); - - defaultMenu->addAction(KIcon("edit-delete"),i18n("Remove Entry"), m_worksheet, SLOT(removeCurrentEntry())); - - createSubMenuInsert(defaultMenu); - - defaultMenu->popup(event->globalPos()); - - return true; + WorksheetEntry::populateMenu(menu, pos); } - bool PageBreakEntry::acceptRichText() { return false; } -bool PageBreakEntry::acceptsDrop(const QTextCursor& cursor) -{ - Q_UNUSED(cursor); - return false; -} - void PageBreakEntry::setContent(const QString& content) { Q_UNUSED(content); return; } void PageBreakEntry::setContent(const QDomElement& content, const KZip& file) { Q_UNUSED(content); Q_UNUSED(file); return; } QDomElement PageBreakEntry::toXml(QDomDocument& doc, KZip* archive) { Q_UNUSED(archive); - + QDomElement pgbrk = doc.createElement("PageBreak"); return pgbrk; } -QString PageBreakEntry::toPlain(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq) +QString PageBreakEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) { Q_UNUSED(commandSep); - + return commentStartingSeq + "page break" + commentEndingSeq; - } - void PageBreakEntry::interruptEvaluation() { return; } -bool PageBreakEntry::evaluate(bool current) +void PageBreakEntry::layOutForWidth(qreal w, bool force) { - Q_UNUSED(current); - return true; + if (size().width() == w && !force) + return; + + if (m_msgItem->isVisible()) { + m_msgItem->setGeometry(0, 0, w, true); + + setSize(QSizeF(w, m_msgItem->height() + VerticalMargin)); + } else { + setSize(QSizeF(w, 0)); + } } -void PageBreakEntry::update() +bool PageBreakEntry::evaluate(EvaluationOption evalOp) { - QTextCursor cursor(m_frame->firstCursorPosition()); - cursor.setPosition(m_frame->lastPosition(), QTextCursor::KeepAnchor); - cursor.removeSelectedText(); + evaluateNext(evalOp); + return true; +} - if (!m_worksheet->isPrinting()) - { - QTextBlockFormat block(cursor.blockFormat()); - block.setAlignment(Qt::AlignCenter); - cursor.setBlockFormat(block); - KColorScheme color = KColorScheme( QPalette::Normal, KColorScheme::View); - QTextCharFormat cformat(cursor.charFormat()); - cformat.setForeground(color.foreground(KColorScheme::InactiveText)); +void PageBreakEntry::updateEntry() +{ + if (worksheet()->isPrinting()) { + m_msgItem->setVisible(false); + recalculateSize(); + } else if (!m_msgItem->isVisible()) { + m_msgItem->setVisible(true); + recalculateSize(); + } + return; +} - cursor.insertText("--- Page Break ---", cformat); +/* +void PageBreakEntry::paint(QPainter* painter, const QStyleOptionGraphicsItem*, + QWidget*) +{ + if (worksheet()->isPrinting()) { + QPaintDevice* device = painter->paintEngine()->paintDevice(); + QPrinter* printer = qobject_cast(device); + if (printer) + printer->newPage(); } } +*/ + +bool PageBreakEntry::wantToEvaluate() +{ + return false; +} + +bool PageBreakEntry::wantFocus() +{ + return false; +} + +#include "pagebreakentry.moc" diff --git a/src/pagebreakentry.h b/src/pagebreakentry.h index 2440c18b..e35127ef 100644 --- a/src/pagebreakentry.h +++ b/src/pagebreakentry.h @@ -1,72 +1,67 @@ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --- - Copyright (C) 2011 Martin Kuettler + Copyright (C) 2012 Martin Kuettler */ -#ifndef _PAGEBREAKENTRY_H -#define _PAGEBREAKENTRY_H +#ifndef PAGEBREAKENTRY_H +#define PAGEBREAKENTRY_H #include "worksheetentry.h" -#include -class Worksheet; -class WorksheetEntry; - -/** - An entry in the worksheet that tells the printer to insert a page break. - **/ +class WorksheetTextItem; class PageBreakEntry : public WorksheetEntry { - Q_OBJECT + Q_OBJECT; + public: - - PageBreakEntry(QTextCursor position, Worksheet* parent); + PageBreakEntry(Worksheet* worksheet); ~PageBreakEntry(); - enum {Type = 3}; - int type(); - - bool isEmpty(); - - QTextCursor closestValidCursor(const QTextCursor& cursor); - QTextCursor firstValidCursorPosition(); - QTextCursor lastValidCursorPosition(); - bool isValidCursor(const QTextCursor& cursor); - - bool worksheetKeyPressEvent(QKeyEvent* event, const QTextCursor& cursor); - bool worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor); + enum {Type = UserType + 3}; + int type() const; + bool isEmpty(); bool acceptRichText(); - bool acceptsDrop(const QTextCursor& cursor); - void setContent(const QString& content); void setContent(const QDomElement& content, const KZip& file); - QDomElement toXml(QDomDocument& doc, KZip* archive); - QString toPlain(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq); + QString toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq); void interruptEvaluation(); - bool evaluate(bool current); + void layOutForWidth(qreal w, bool force = false); + + //void paint(QPainter* painter, const QStyleOptionGraphicsItem * option, + // QWidget * widget = 0); public slots: - void update(); + bool evaluate(EvaluationOption evalOp = FocusNext); + void updateEntry(); + void populateMenu(KMenu *menu, const QPointF& pos); + + protected: + bool wantToEvaluate(); + bool wantFocus(); + + private: + WorksheetTextItem* m_msgItem; }; -#endif /* _PAGEBREAKENTRY_H */ +#endif /* PAGEBREAKENTRY_H */ + diff --git a/src/placeholderentry.cpp b/src/placeholderentry.cpp new file mode 100644 index 00000000..60950320 --- /dev/null +++ b/src/placeholderentry.cpp @@ -0,0 +1,117 @@ +/* + 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 "placeholderentry.h" + +#include + +PlaceHolderEntry::PlaceHolderEntry(Worksheet* worksheet, QSizeF s) + : WorksheetEntry(worksheet) +{ + setSize(s); +} + +PlaceHolderEntry::~PlaceHolderEntry() +{ +} + +int PlaceHolderEntry::type() const +{ + return Type; +} + +bool PlaceHolderEntry::isEmpty() +{ + /* + // This is counter-intuitive. isEmpty() is used to find out whether a new + // CommandEntry needs to be appended, and a PlaceHolderEntry should never + // prevent that. + return false; + */ + return true; +} + +bool PlaceHolderEntry::acceptRichText() +{ + return false; +} + +void PlaceHolderEntry::setContent(const QString&) +{ +} + +void PlaceHolderEntry::setContent(const QDomElement&, const KZip&) +{ +} + +QDomElement PlaceHolderEntry::toXml(QDomDocument&, KZip*) +{ + return QDomElement(); +} + +QString PlaceHolderEntry::toPlain(const QString&, const QString&, const QString&){ + return QString(); +} + +void PlaceHolderEntry::interruptEvaluation() +{ + return; +} + +void PlaceHolderEntry::layOutForWidth(qreal w, bool force) +{ + Q_UNUSED(w); + Q_UNUSED(force); +} + +bool PlaceHolderEntry::evaluate(EvaluationOption evalOp) +{ + evaluateNext(evalOp); + return true; +} + +void PlaceHolderEntry::updateEntry() +{ +} + +bool PlaceHolderEntry::wantToEvaluate() +{ + return false; +} + +void PlaceHolderEntry::changeSize(QSizeF s) +{ + if (!worksheet()->animationsEnabled()) { + setSize(s); + worksheet()->updateEntrySize(this); + return; + } + if (aboutToBeRemoved()) + return; + + if (animationActive()) + endAnimation(); + + QPropertyAnimation* sizeAn = sizeChangeAnimation(s); + + sizeAn->setEasingCurve(QEasingCurve::InOutQuad); + sizeAn->start(QAbstractAnimation::DeleteWhenStopped); +} + diff --git a/src/placeholderentry.h b/src/placeholderentry.h new file mode 100644 index 00000000..0e306a69 --- /dev/null +++ b/src/placeholderentry.h @@ -0,0 +1,55 @@ +/* + 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 PLACEHOLDERENTRY_H +#define PLACEHOLDERENTRY_H + +#include "worksheetentry.h" + +class PlaceHolderEntry : public WorksheetEntry +{ + public: + PlaceHolderEntry(Worksheet* worksheet, QSizeF s); + ~PlaceHolderEntry(); + + enum {Type = UserType + 6}; + int type() const; + + bool isEmpty(); + bool acceptRichText(); + void setContent(const QString&); + void setContent(const QDomElement&, const KZip&); + QDomElement toXml(QDomDocument&, KZip*); + QString toPlain(const QString&, const QString&, const QString&); + void interruptEvaluation(); + + void layOutForWidth(qreal w, bool force = false); + + public slots: + bool evaluate(EvaluationOption evalOp = FocusNext); + void updateEntry(); + + void changeSize(QSizeF s); + + protected: + bool wantToEvaluate(); +}; + +#endif //PLACEHOLDERENTRY_H diff --git a/src/resultcontextmenu.cpp b/src/resultcontextmenu.cpp deleted file mode 100644 index 5ea03c04..00000000 --- a/src/resultcontextmenu.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - 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 "resultcontextmenu.h" - -#include "lib/result.h" -#include "lib/latexresult.h" -#include "lib/animationresult.h" -#include "commandentry.h" - -#include - -#include -#include -#include - -ResultContextMenu::ResultContextMenu(CommandEntry* entry, QWidget* parent) : KMenu(parent) -{ - setTitle(i18n("Result")); - m_entry=entry; - - addGeneralActions(); - addTypeSpecificActions(); -} - -ResultContextMenu::~ResultContextMenu() -{ - -} - -void ResultContextMenu::addGeneralActions() -{ - QAction* saveAction=addAction(i18n("Save result")); - connect(saveAction, SIGNAL(triggered()), this, SLOT(saveResult())); - - QAction* removeAction=addAction(KIcon("edit-delete"), i18n("Remove result")); - connect(removeAction, SIGNAL(triggered()), this, SLOT(removeResult())); -} - -void ResultContextMenu::addTypeSpecificActions() -{ - switch(result()->type()) - { - case Cantor::LatexResult::Type: - { - QAction* showCodeAction=0; - Cantor::LatexResult* lr=dynamic_cast(result()); - if(lr->isCodeShown()) - showCodeAction=addAction(i18n("Show Rendered")); - else - showCodeAction=addAction(i18n("Show Code")); - - connect(showCodeAction, SIGNAL(triggered()), this, SLOT(latexToggleShowCode())); - break; - } - case Cantor::AnimationResult::Type: - { - Cantor::AnimationResult* ar=dynamic_cast(result()); - QMovie* movie=static_cast(ar->data().value()); - if(!movie) - break; - - QAction* startStopAction=0; - if(movie->state()==QMovie::Running) - startStopAction=addAction(i18n("Pause Animation")); - else - startStopAction=addAction(i18n("Start Animation")); - - connect(startStopAction, SIGNAL(triggered()), this, SLOT(animationPause())); - - QAction* restartAction=addAction(i18n("Restart Animation")); - connect(restartAction, SIGNAL(triggered()), this, SLOT(animationRestart())); - } - } -} - -CommandEntry* ResultContextMenu::entry() -{ - return m_entry; -} - -Cantor::Result* ResultContextMenu::result() -{ - return m_entry->expression()->result(); -} - - -void ResultContextMenu::saveResult() -{ - const QString& filename=KFileDialog::getSaveFileName(KUrl(), result()->mimeType(), this); - kDebug()<<"saving result to "<save(filename); -} - -void ResultContextMenu::removeResult() -{ - entry()->removeResult(); -} - -void ResultContextMenu::latexToggleShowCode() -{ - Cantor::LatexResult* lr=dynamic_cast(result()); - if(lr->isCodeShown()) - lr->showRendered(); - else - lr->showCode(); - - entry()->update(); -} - -void ResultContextMenu::animationPause() -{ - Cantor::AnimationResult* ar=dynamic_cast(result()); - QMovie* movie=static_cast(ar->data().value()); - if(!movie) - return; - - if(movie->state()==QMovie::Running) - movie->setPaused(true); - else - movie->start(); -} - -void ResultContextMenu::animationRestart() -{ - Cantor::AnimationResult* ar=dynamic_cast(result()); - QMovie* movie=static_cast(ar->data().value()); - if(!movie) - return; - - movie->stop(); - movie->start(); -} diff --git a/src/resultitem.cpp b/src/resultitem.cpp new file mode 100644 index 00000000..75e1f7ff --- /dev/null +++ b/src/resultitem.cpp @@ -0,0 +1,86 @@ +/* + 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 "resultitem.h" +#include "textresultitem.h" +#include "imageresultitem.h" +#include "animationresultitem.h" +#include "worksheetentry.h" + +#include "lib/result.h" +#include "lib/textresult.h" +#include "lib/latexresult.h" +#include "lib/imageresult.h" +#include "lib/epsresult.h" +#include "lib/animationresult.h" + +#include + +#include +#include +#include + +ResultItem::ResultItem() +{ +} + +ResultItem::~ResultItem() +{ +} + +ResultItem* ResultItem::create(WorksheetEntry* parent, Cantor::Result* result) +{ + switch(result->type()) { + case Cantor::TextResult::Type: + case Cantor::LatexResult::Type: + { + TextResultItem* item = new TextResultItem(parent); + item->updateFromResult(result); + return item; + } + case Cantor::ImageResult::Type: + case Cantor::EpsResult::Type: + { + ImageResultItem* item = new ImageResultItem(parent); + item->updateFromResult(result); + return item; + } + case Cantor::AnimationResult::Type: + { + AnimationResultItem* item = new AnimationResultItem(parent); + item->updateFromResult(result); + return item; + } + default: + return 0; + } +} + +void ResultItem::addCommonActions(QObject* self, KMenu* menu) +{ + menu->addAction(i18n("Save result"), self, SLOT(saveResult())); + menu->addAction(KIcon("edit-delete"), i18n("Remove result"), + self, SIGNAL(removeResult())); +} + +QGraphicsObject* ResultItem::graphicsObject() +{ + return dynamic_cast(this); +} diff --git a/src/resultitem.h b/src/resultitem.h new file mode 100644 index 00000000..68c41e44 --- /dev/null +++ b/src/resultitem.h @@ -0,0 +1,67 @@ +/* + 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 RESULTITEM_H +#define RESULTITEM_H + +/* + * This is a common superclass of all result items. Unfortunately this class + * cannot inherit QGraphicsItem or QObject, because the subclasses inherit + * these from an other source (TextResultItem inherits WorksheetTextItem, for + * example). Therefore this class mainly offers the interface, and the + * implementations are done in each subclasses, even when the code is literally + * the same for them. + */ + +namespace Cantor { + class Result; +}; + +class WorksheetEntry; + +class KMenu; +class QObject; +class QPointF; +class QGraphicsObject; + +class ResultItem +{ + public: + ResultItem(); + virtual ~ResultItem(); + + static ResultItem* create(WorksheetEntry* parent, Cantor::Result* result); + + virtual double setGeometry(double x, double y, double w) = 0; + virtual void populateMenu(KMenu* menu, const QPointF& pos) = 0; + + virtual ResultItem* updateFromResult(Cantor::Result* result) = 0; + + virtual void deleteLater() = 0; + + virtual Cantor::Result* result() = 0; + + QGraphicsObject* graphicsObject(); + + protected: + static void addCommonActions(QObject* self, KMenu* menu); +}; + +#endif // RESULTITEM_H diff --git a/src/resultproxy.cpp b/src/resultproxy.cpp deleted file mode 100644 index a50e802d..00000000 --- a/src/resultproxy.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - 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 "resultproxy.h" - -#include - -#include "lib/result.h" -#include "lib/epsresult.h" -#include "lib/latexresult.h" -#include "lib/imageresult.h" -#include "lib/animationresult.h" -#include "animationhandler.h" -#include "animation.h" - -#ifdef LIBSPECTRE_FOUND - #include "libspectre/spectre.h" -#endif - -#include -#include -#include -#include -#include -#include -#include - -ResultProxy::ResultProxy(QTextDocument* parent) : QObject(parent) -{ - m_document=parent; - m_scale=1.0; - m_useHighRes=false; -} - -ResultProxy::~ResultProxy() -{ - -} - -void ResultProxy::setScale(qreal scale) -{ - m_scale=scale; -} - -void ResultProxy::scale(qreal value) -{ - m_scale*=value; -} - -qreal ResultProxy::scale() -{ - return m_scale; -} - -void ResultProxy::useHighResolution(bool use) -{ - m_useHighRes=use; - -} - -void ResultProxy::insertResult(QTextCursor& cursor, Cantor::Result* result) -{ - kDebug()<<"inserting new format"; - QTextCharFormat format; - switch(result->type()) - { - case Cantor::LatexResult::Type: - //if the lr is in Code-Mode, insert its html. - //otherwise fallthrough to the EpsRendering code - if(dynamic_cast(result)->isCodeShown()) - { - QString html=result->toHtml().trimmed(); - if(html.isEmpty()) - cursor.removeSelectedText(); - else - cursor.insertHtml(result->toHtml()); - - break; - } - case Cantor::EpsResult::Type: - format=renderEps(result); - if(format.isValid()) - cursor.insertText(QString(QChar::ObjectReplacementCharacter), format ); - else - cursor.insertText(i18n("Cannot render Eps file. You may need additional packages")); - - break; - case Cantor::AnimationResult::Type: - kDebug()<<"its an animation"; - format=renderGif(result); - if(format.isValid()) - { - cursor.insertText(QString(QChar::ObjectReplacementCharacter), format ); - AnimationHelperItem movie = format.property(AnimationHandler::MovieProperty).value(); - QTextCursor cursor2=cursor; - cursor2.setPosition(cursor.position()-1); - - movie.setPosition(cursor2); - } - break; - default: - QString html=result->toHtml().trimmed(); - if(html.isEmpty()) - cursor.removeSelectedText(); - else - cursor.insertHtml(result->toHtml()); - } -} - -//private result specific rendering methods -QTextCharFormat ResultProxy::renderEps(Cantor::Result* result) -{ - double scale; - if(m_useHighRes) - scale=1.2*4.0; //1.2 scaling factor, to make it look nice, 4x for high resolution - else - scale=1.8*m_scale; - - QTextImageFormat epsCharFormat; - - KUrl url=result->data().toUrl(); - - QSize s; - bool success=renderEpsToResource(url, &s); - - KUrl internal=url; - internal.setProtocol("internal"); - if(success) - { - epsCharFormat.setName(internal.url()); - if(m_useHighRes) - { - epsCharFormat.setWidth(s.width()*1.2); - epsCharFormat.setHeight(s.height()*1.2); - } - else - { - epsCharFormat.setWidth(s.width()*scale); - epsCharFormat.setHeight(s.height()*scale); - } - } - - return epsCharFormat; -} - -bool ResultProxy::renderEpsToResource(const KUrl& url, QSize* size) -{ -#ifdef LIBSPECTRE_FOUND - SpectreDocument* doc=spectre_document_new();; - SpectreRenderContext* rc=spectre_render_context_new(); - - kDebug()<<"rendering eps file: "<addResource(QTextDocument::ImageResource, internal, QVariant(img) ); - - spectre_document_free(doc); - spectre_render_context_free(rc); - - if(size) - *size=QSize(w, h); - - return true; -#else - return false; -#endif -} - -QTextCharFormat ResultProxy::renderGif(Cantor::Result* result) -{ - QTextImageFormat charFormat; - KUrl url=result->url(); - - AnimationHelperItem anim; - QMovie* movie=static_cast(result->data().value()); - anim.setMovie(movie); - - charFormat.setProperty(AnimationHandler::MovieProperty, qVariantFromValue(anim)); - charFormat.setName(url.toLocalFile()); - - charFormat.setName(url.url()); - - QTimer::singleShot(0, movie, SLOT(start())); - - return charFormat; -} diff --git a/src/resultproxy.h b/src/resultproxy.h deleted file mode 100644 index 3125dd8a..00000000 --- a/src/resultproxy.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - - --- - Copyright (C) 2009 Alexander Rieder - */ - -#ifndef _RESULTPROXY_H -#define _RESULTPROXY_H - -#include -#include -#include -#include - -namespace Cantor -{ - class Result; -} - -/** - This class is used to translate from the Cantor::Result classes, - which need to be indipendent of the rendering used, to the actually - used QTextDocument and the containing QTextObjects - **/ -class ResultProxy : public QObject -{ - Q_OBJECT - public: - ResultProxy( QTextDocument* parent ); - ~ResultProxy(); - - void insertResult(QTextCursor& pos, Cantor::Result* result); - - void setScale(qreal scale); - void scale(qreal value); - qreal scale(); - - void useHighResolution(bool use); - - //this doesn't belong here! - bool renderEpsToResource(const KUrl& url, QSize* size = 0); - - private: - QTextCharFormat renderEps(Cantor::Result* result); - QTextCharFormat renderGif(Cantor::Result* result); - - private: - QTextDocument* m_document; - qreal m_scale; - bool m_useHighRes; -}; - -#endif /* _RESULTPROXY_H */ diff --git a/src/searchbar.cpp b/src/searchbar.cpp new file mode 100644 index 00000000..8f3058a1 --- /dev/null +++ b/src/searchbar.cpp @@ -0,0 +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() && + !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()) { + searchForward(); + 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 && 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(aboutToBeDeleted()), this, + SLOT(invalidateStartCursor())); + if (cursor.entry()) + 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(aboutToBeDeleted()), this, + SLOT(invalidateCurrentCursor())); + if (cursor.entry()) + 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 new file mode 100644 index 00000000..7e0bf3de --- /dev/null +++ b/src/searchbar.h @@ -0,0 +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/settings.ui b/src/settings.ui index 0c6de8d3..dc8f4131 100644 --- a/src/settings.ui +++ b/src/settings.ui @@ -1,132 +1,114 @@ SettingsBase 0 0 398 400 Default Backend: true - - - - - - Completion Style: - - - - - - - - Popup - - - - - Inline - - - - - - When enabled, Cantor will automatically evaluate every entry below the current one. Reevaluate Entries automatically Defaults Enable LaTeX Typesetting Enable Syntax Highlighting Enable Completion Enable Line Numbers + + + + Enable Worksheet Animations + + + Qt::Vertical 20 40 KComboBox QComboBox
    kcombobox.h
    diff --git a/src/standardsearchbar.ui b/src/standardsearchbar.ui new file mode 100644 index 00000000..bbb1a002 --- /dev/null +++ b/src/standardsearchbar.ui @@ -0,0 +1,119 @@ + + + StandardSearchBar + + + + 0 + 0 + 736 + 34 + + + + SearchBar + + + + + + + + ... + + + true + + + + + + + Find: + + + + + + + + + + &Next + + + + + + + &Previous + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 13 + 13 + + + + + + + + &Match case + + + + + + + Qt::AlignCenter + + + + + + + ... + + + true + + + + + + + + + + KLineEdit + QLineEdit +
    klineedit.h
    +
    + + KSqueezedTextLabel + QLabel +
    ksqueezedtextlabel.h
    +
    +
    + + pattern + next + previous + matchCase + openExtended + close + + + +
    diff --git a/src/textentry.cpp b/src/textentry.cpp index 76df94e1..5f401c0c 100644 --- a/src/textentry.cpp +++ b/src/textentry.cpp @@ -1,335 +1,364 @@ /* 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) 2010 Raffaele De Feo + Copyright (C) 2009 Alexander Rieder + Copyright (C) 2012 Martin Kuettler */ #include "textentry.h" -#include "worksheetentry.h" -#include "worksheet.h" -#include "resultproxy.h" -#include "lib/defaulthighlighter.h" -#include "lib/latexrenderer.h" +#include "worksheettextitem.h" +#include "epsrenderer.h" +#include "latexrenderer.h" -#include "formulatextobject.h" - -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include - +#include +#include -TextEntry::TextEntry(QTextCursor position, Worksheet* parent ) : WorksheetEntry( position, parent ) +TextEntry::TextEntry(Worksheet* worksheet) : WorksheetEntry(worksheet), m_textItem(new WorksheetTextItem(this, Qt::TextEditorInteraction)) { - QTextFrameFormat frameFormat = m_frame->frameFormat(); - frameFormat.setPadding(10); - m_frame->setFrameFormat(frameFormat); - QTextCharFormat format = firstCursorPosition().blockCharFormat(); - format.setProperty(Cantor::DefaultHighlighter::BlockTypeProperty, Cantor::DefaultHighlighter::NoHighlightBlock); - firstCursorPosition().setBlockCharFormat(format); + m_textItem->enableRichText(true); + connect(m_textItem, SIGNAL(moveToPrevious(int, qreal)), + this, SLOT(moveToPreviousEntry(int, qreal))); + connect(m_textItem, SIGNAL(moveToNext(int, qreal)), + this, SLOT(moveToNextEntry(int, qreal))); + connect(m_textItem, SIGNAL(execute()), this, SLOT(evaluate())); + connect(m_textItem, SIGNAL(doubleClick()), this, SLOT(resolveImagesAtCursor())); } TextEntry::~TextEntry() { - -} - -int TextEntry::type() -{ - return Type; -} - -QTextCursor TextEntry::firstValidCursorPosition() -{ - return firstCursorPosition(); } -QTextCursor TextEntry::lastValidCursorPosition() +void TextEntry::populateMenu(KMenu *menu, const QPointF& pos) { - return lastCursorPosition(); -} - -QTextCursor TextEntry::closestValidCursor(const QTextCursor& cursor) -{ - return QTextCursor(cursor); -} - -bool TextEntry::isValidCursor(const QTextCursor& cursor) -{ - int pos = cursor.position(); - return (firstValidPosition() <= pos && pos <= lastValidPosition()); + bool imageSelected = false; + QTextCursor cursor = m_textItem->textCursor(); + const QChar repl = QChar::ObjectReplacementCharacter; + if (cursor.hasSelection()) { + QString selection = m_textItem->textCursor().selectedText(); + imageSelected = selection.contains(repl); + } else { + // we need to try both the current cursor and the one after the that + cursor = m_textItem->cursorForPosition(pos); + kDebug() << cursor.position(); + for (int i = 2; i; --i) { + int p = cursor.position(); + if (m_textItem->document()->characterAt(p-1) == repl && + cursor.charFormat().hasProperty(EpsRenderer::CantorFormula)) { + m_textItem->setTextCursor(cursor); + imageSelected = true; + break; + } + cursor.movePosition(QTextCursor::NextCharacter); + } + } + if (imageSelected) { + menu->addAction(i18n("Show LaTeX code"), this, SLOT(resolveImagesAtCursor())); + menu->addSeparator(); + } + WorksheetEntry::populateMenu(menu, pos); } bool TextEntry::isEmpty() { - QTextCursor cursor = firstValidCursorPosition(); - cursor.setPosition(lastValidPosition(), QTextCursor::KeepAnchor); - return cursor.selection().isEmpty(); + return m_textItem->document()->isEmpty(); } -bool TextEntry::worksheetMouseDoubleClickEvent(QMouseEvent* event, const QTextCursor& cursor) +int TextEntry::type() const { - QTextCursor c=cursor; - - for(int pos=cursor.selectionStart()+1;pos<=cursor.selectionEnd();pos++) - { - c.setPosition(pos); - if (c.charFormat().objectType() == FormulaTextObject::FormulaTextFormat) - showLatexCode(c); - } - return true; + return Type; } bool TextEntry::acceptRichText() { return true; } -bool TextEntry::acceptsDrop(const QTextCursor& cursor) +bool TextEntry::focusEntry(int pos, qreal xCoord) { - Q_UNUSED(cursor); - - return true; -} - -bool TextEntry::worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor) -{ - Q_UNUSED(cursor); - KMenu* defaultMenu = new KMenu(m_worksheet); - - defaultMenu->addAction(KStandardAction::cut(m_worksheet)); - defaultMenu->addAction(KStandardAction::copy(m_worksheet)); - defaultMenu->addAction(KStandardAction::paste(m_worksheet)); - defaultMenu->addSeparator(); - - if(!m_worksheet->isRunning()) - defaultMenu->addAction(KIcon("system-run"),i18n("Evaluate Worksheet"),m_worksheet,SLOT(evaluate()),0); - else - defaultMenu->addAction(KIcon("process-stop"),i18n("Interrupt"),m_worksheet,SLOT(interrupt()),0); - defaultMenu->addSeparator(); - - defaultMenu->addAction(KIcon("edit-delete"),i18n("Remove Entry"), m_worksheet, SLOT(removeCurrentEntry())); - - createSubMenuInsert(defaultMenu); - - defaultMenu->popup(event->globalPos()); - + if (aboutToBeRemoved()) + return false; + m_textItem->setFocusAt(pos, xCoord); return true; } void TextEntry::setContent(const QString& content) { - firstValidCursorPosition().insertText(content); + m_textItem->setPlainText(content); } void TextEntry::setContent(const QDomElement& content, const KZip& file) { Q_UNUSED(file); if(content.firstChildElement("body").isNull()) return; QDomDocument doc = QDomDocument(); QDomNode n = doc.importNode(content.firstChildElement("body"), true); doc.appendChild(n); QString html = doc.toString(); kDebug() << html; - firstValidCursorPosition().insertHtml(html); + m_textItem->setHtml(html); } QDomElement TextEntry::toXml(QDomDocument& doc, KZip* archive) { Q_UNUSED(archive); bool needsEval=false; //make sure that the latex code is shown instead of the rendered formulas - QTextCursor cursor = m_worksheet->document()->find(QString(QChar::ObjectReplacementCharacter), m_frame->firstCursorPosition()); - while(!cursor.isNull()&&cursor.position()<=m_frame->lastPosition()) + QTextCursor cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter)); + while(!cursor.isNull()) { - QTextCharFormat format=cursor.charFormat(); - if (format.objectType() == FormulaTextObject::FormulaTextFormat) + QTextCharFormat format = cursor.charFormat(); + if (format.hasProperty(EpsRenderer::CantorFormula)) { showLatexCode(cursor); needsEval=true; } - cursor = m_worksheet->document()->find(QString(QChar::ObjectReplacementCharacter), cursor); + cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter), cursor); } - - cursor = firstValidCursorPosition(); - cursor.setPosition(lastValidPosition(), QTextCursor::KeepAnchor); - - const QString& html = cursor.selection().toHtml(); + const QString& html = m_textItem->toHtml(); kDebug() << html; QDomElement el = doc.createElement("Text"); QDomDocument myDoc = QDomDocument(); myDoc.setContent(html); el.appendChild(myDoc.documentElement().firstChildElement("body")); - if(needsEval) - evaluate(false); + if (needsEval) + evaluate(); return el; } -QString TextEntry::toPlain(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq) +QString TextEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) { Q_UNUSED(commandSep); if (commentStartingSeq.isEmpty()) return QString(); - QTextCursor cursor = firstValidCursorPosition(); - cursor.setPosition(lastValidPosition(), QTextCursor::KeepAnchor); - QString text = cursor.selection().toPlainText(); + /* + // whould this be plain enought? + QTextCursor cursor = m_textItem->textCursor(); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + + QString text = m_textItem->resolveImages(cursor); + text.replace(QChar::ParagraphSeparator, '\n'); + text.replace(QChar::LineSeparator, '\n'); + */ + QString text = m_textItem->toPlainText(); if (!commentEndingSeq.isEmpty()) return commentStartingSeq + text + commentEndingSeq + "\n"; return commentStartingSeq + text.replace("\n", "\n" + commentStartingSeq) + "\n"; + } void TextEntry::interruptEvaluation() { - } -bool TextEntry::evaluate(bool current) +bool TextEntry::evaluate(EvaluationOption evalOp) { - Q_UNUSED(current); - - QTextDocument *doc = m_frame->document(); - //blahtex::Interface *translator = m_worksheet->translator(); - - QTextCursor cursor = findLatexCode(doc); + QTextCursor cursor = findLatexCode(); while (!cursor.isNull()) { QString latexCode = cursor.selectedText(); kDebug()<<"found latex: "<setLatexCode(latexCode); renderer->setEquationOnly(true); renderer->setEquationType(Cantor::LatexRenderer::InlineEquation); renderer->setMethod(Cantor::LatexRenderer::LatexMethod); renderer->renderBlocking(); -#if 0 - QTextCharFormat mmlCharFormat; - mmlCharFormat.setObjectType(MmlTextObject::MmlTextFormat); - mmlCharFormat.setProperty(MmlTextObject::LatexCode, latexCode); - - if (!translator->ProcessInput(latexCode.toStdWString())) - break; - QString mmlCode = QString::fromStdWString(translator->GetMathml()); - kDebug() << mmlCode; - if (mmlCode.isEmpty()) - break; - - QtMmlDocument renderer; - renderer.setBaseFontPointSize(cursor.charFormat().fontPointSize()); - renderer.setFontName(QtMmlWidget::NormalFont, "STIXGeneral"); - renderer.setContent(mmlCode); - - QImage mmlBufferImage(renderer.size(), QImage::Format_ARGB32); - mmlBufferImage.fill(QColor(0, 0, 0, 0).value()); - QPainter painter(&mmlBufferImage); - renderer.paint(&painter, mmlBufferImage.rect().topLeft ()); + bool success; + QTextImageFormat formulaFormat; + if (renderer->renderingSuccessful()) { + EpsRenderer* epsRend = worksheet()->epsRenderer(); + formulaFormat = epsRend->render(m_textItem->document(), renderer); + success = !formulaFormat.name().isEmpty(); + } else { + success = false; + } - mmlCharFormat.setProperty(MmlTextObject::MmlData, mmlBufferImage); - - cursor.removeSelectedText(); - cursor.insertText(QString(QChar::ObjectReplacementCharacter), mmlCharFormat); - -#endif - - bool success=m_worksheet->resultProxy()->renderEpsToResource(renderer->imagePath()); kDebug()<<"rendering successfull? "<imagePath(); - KUrl internal=KUrl(path); - internal.setProtocol("internal"); - kDebug()<<"int: "<imagePath()); - formulaFormat.setProperty( FormulaTextObject::ResourceUrl, internal); - formulaFormat.setProperty( FormulaTextObject::LatexCode, latexCode); - formulaFormat.setProperty( FormulaTextObject::FormulaType, renderer->method()); + formulaFormat.setProperty(EpsRenderer::Delimiter, "$$"); cursor.insertText(QString(QChar::ObjectReplacementCharacter), formulaFormat); delete renderer; - cursor = findLatexCode(doc); - + cursor = findLatexCode(cursor); } + evaluateNext(evalOp); + return true; } -void TextEntry::update() +void TextEntry::updateEntry() { - QTextCursor cursor = m_worksheet->document()->find(QString(QChar::ObjectReplacementCharacter), m_frame->firstCursorPosition()); - while(!cursor.isNull()&&cursor.position()<=m_frame->lastPosition()) + kDebug() << "update Entry"; + QTextCursor cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter)); + while(!cursor.isNull()) { - QTextCharFormat format=cursor.charFormat(); - if (format.objectType() == FormulaTextObject::FormulaTextFormat) + QTextCharFormat format = cursor.charFormat(); + if (format.hasProperty(EpsRenderer::CantorFormula)) { - kDebug()<<"found a formula... rendering the eps..."; - QUrl url=qVariantValue(format.property(FormulaTextObject::Data)); - bool success=m_worksheet->resultProxy()->renderEpsToResource(url); - kDebug()<<"rendering successfull? "<(format.property(EpsRenderer::ImagePath)); + QSizeF s = worksheet()->epsRenderer()->renderToResource(m_textItem->document(), url); + kDebug() << "rendering successfull? " << s.isValid(); + + //cursor.deletePreviousChar(); + //cursor.insertText(QString(QChar::ObjectReplacementCharacter), format); } - cursor = m_worksheet->document()->find(QString(QChar::ObjectReplacementCharacter), cursor); + cursor = m_textItem->document()->find(QString(QChar::ObjectReplacementCharacter), cursor); } } -QTextCursor TextEntry::findLatexCode(QTextDocument *doc) const +void TextEntry::resolveImagesAtCursor() +{ + QTextCursor cursor = m_textItem->textCursor(); + if (!cursor.hasSelection()) + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + cursor.insertText(m_textItem->resolveImages(cursor)); +} + +QTextCursor TextEntry::findLatexCode(QTextCursor cursor) const { - QTextCursor startCursor = doc->find("$$"); + QTextDocument *doc = m_textItem->document(); + QTextCursor startCursor; + if (cursor.isNull()) + startCursor = doc->find("$$"); + else + startCursor = doc->find("$$", cursor); if (startCursor.isNull()) return startCursor; const QTextCursor endCursor = doc->find("$$", startCursor); if (endCursor.isNull()) return endCursor; - startCursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 2); + startCursor.setPosition(startCursor.selectionStart()); startCursor.setPosition(endCursor.position(), QTextCursor::KeepAnchor); return startCursor; } -void TextEntry::showLatexCode(QTextCursor cursor) +QString TextEntry::showLatexCode(QTextCursor cursor) { - QString latexCode = qVariantValue(cursor.charFormat().property(FormulaTextObject::LatexCode)); + QString latexCode = qVariantValue(cursor.charFormat().property(EpsRenderer::Code)); cursor.deletePreviousChar(); - cursor.insertText("$$"+latexCode+"$$"); + latexCode = "$$"+latexCode+"$$"; + cursor.insertText(latexCode); + return latexCode; +} + +int TextEntry::searchText(QString text, QString pattern, + QTextDocument::FindFlags qt_flags) +{ + Qt::CaseSensitivity caseSensitivity; + if (qt_flags & QTextDocument::FindCaseSensitively) + caseSensitivity = Qt::CaseSensitive; + else + caseSensitivity = Qt::CaseInsensitive; + + int position; + if (qt_flags & QTextDocument::FindBackward) + position = text.lastIndexOf(pattern, -1, caseSensitivity); + else + position = text.indexOf(pattern, 0, caseSensitivity); + + return position; +} + +WorksheetCursor TextEntry::search(QString pattern, unsigned flags, + QTextDocument::FindFlags qt_flags, + const WorksheetCursor& pos) +{ + if (!(flags & WorksheetEntry::SearchText) || + (pos.isValid() && pos.entry() != this)) + return WorksheetCursor(); + + QTextCursor textCursor = m_textItem->search(pattern, qt_flags, pos); + int position; + QTextCursor latexCursor; + QString latex; + if (flags & WorksheetEntry::SearchLaTeX) { + const QString repl = QString(QChar::ObjectReplacementCharacter); + latexCursor = m_textItem->search(repl, qt_flags, pos); + while (!latexCursor.isNull()) { + latex = m_textItem->resolveImages(latexCursor); + position = searchText(latex, pattern, qt_flags); + if (position >= 0) { + break; + } + WorksheetCursor c(this, m_textItem, latexCursor); + latexCursor = m_textItem->search(repl, qt_flags, c); + } + } + + if (latexCursor.isNull()) { + if (textCursor.isNull()) + return WorksheetCursor(); + else + return WorksheetCursor(this, m_textItem, textCursor); + } else { + if (textCursor.isNull() || latexCursor < textCursor) { + int start = latexCursor.selectionStart(); + latexCursor.insertText(latex); + QTextCursor c = m_textItem->textCursor(); + c.setPosition(start + position); + c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, + pattern.length()); + return WorksheetCursor(this, m_textItem, c); + } else { + return WorksheetCursor(this, m_textItem, textCursor); + } + } +} + + +void TextEntry::layOutForWidth(double w, bool force) +{ + if (size().width() == w && !force) + return; + + m_textItem->setGeometry(0, 0, w); + setSize(QSizeF(w, m_textItem->height() + VerticalMargin)); +} + +bool TextEntry::wantToEvaluate() +{ + return !findLatexCode().isNull(); } diff --git a/src/textentry.h b/src/textentry.h index 1741b16c..9c2aaaa5 100644 --- a/src/textentry.h +++ b/src/textentry.h @@ -1,71 +1,88 @@ /* 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) 2010 Raffaele De Feo + Copyright (C) 2009 Alexander Rieder + Copyright (C) 2012 Martin Kuettler */ -#ifndef _TEXTENTRY_H -#define _TEXTENTRY_H +#ifndef TEXTENTRY_H +#define TEXTENTRY_H -#include "worksheetentry.h" -#include +#include +#include +#include +#include +#include -class Worksheet; -class WorksheetEntry; -class KZip; +#include "worksheetentry.h" +#include "worksheettextitem.h" class TextEntry : public WorksheetEntry { Q_OBJECT public: - TextEntry(QTextCursor position, Worksheet* parent); + TextEntry(Worksheet* worksheet); ~TextEntry(); - enum {Type = 1}; - int type(); + enum {Type = UserType + 1}; + int type() const; bool isEmpty(); - QTextCursor closestValidCursor(const QTextCursor& cursor); - QTextCursor firstValidCursorPosition(); - QTextCursor lastValidCursorPosition(); - bool isValidCursor(const QTextCursor& cursor); + bool acceptRichText(); - bool worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor); - bool worksheetMouseDoubleClickEvent(QMouseEvent* event, const QTextCursor& cursor); + bool focusEntry(int pos = WorksheetTextItem::TopLeft, qreal xCoord=0); - bool acceptRichText(); - bool acceptsDrop(const QTextCursor& cursor); + // do we need/get this? + //bool worksheetContextMenuEvent(...); + //void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); void setContent(const QString& content); void setContent(const QDomElement& content, const KZip& file); QDomElement toXml(QDomDocument& doc, KZip* archive); - QString toPlain(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq); + QString toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq); void interruptEvaluation(); - bool evaluate(bool current); + void layOutForWidth(double w, bool force = false); + + int searchText(QString text, QString pattern, + QTextDocument::FindFlags qt_flags); + WorksheetCursor search(QString pattern, unsigned flags, + QTextDocument::FindFlags qt_flags, + const WorksheetCursor& pos = WorksheetCursor()); + public slots: - void update(); + bool evaluate(EvaluationOption evalOp = FocusNext); + void resolveImagesAtCursor(); + void updateEntry(); + void populateMenu(KMenu *menu, const QPointF& pos); + + protected: + bool wantToEvaluate(); + + private: + QTextCursor findLatexCode(QTextCursor cursor = QTextCursor()) const; + QString showLatexCode(QTextCursor cursor); + private: - QTextCursor findLatexCode(QTextDocument *doc) const; - void showLatexCode(QTextCursor cursor); + WorksheetTextItem* m_textItem; }; -#endif /* _TEXTENTRY_H */ +#endif //TEXTENTRY_H diff --git a/src/textresultitem.cpp b/src/textresultitem.cpp new file mode 100644 index 00000000..006cf176 --- /dev/null +++ b/src/textresultitem.cpp @@ -0,0 +1,173 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) 2012 Martin Kuettler + */ + +#include "commandentry.h" +#include "textresultitem.h" +#include "lib/result.h" +#include "lib/textresult.h" +#include "lib/latexresult.h" + +#include + +#include +#include +#include +#include +#include + +TextResultItem::TextResultItem(QGraphicsObject* parent) + : WorksheetTextItem(parent), ResultItem() +{ + setTextInteractionFlags(Qt::TextSelectableByMouse); + connect(this, SIGNAL(removeResult()), parentEntry(), + SLOT(removeResult())); +} + +TextResultItem::~TextResultItem() +{ +} + +double TextResultItem::setGeometry(double x, double y, double w) +{ + return WorksheetTextItem::setGeometry(x, y, w); +} + +void TextResultItem::populateMenu(KMenu* menu, const QPointF& pos) +{ + KAction* copy = KStandardAction::copy(this, SLOT(copy()), menu); + if (!textCursor().hasSelection()) + copy->setEnabled(false); + menu->addAction(copy); + addCommonActions(this, menu); + + Cantor::Result* res = result(); + if (res->type() == Cantor::LatexResult::Type) { + QAction* showCodeAction = 0; + Cantor::LatexResult* lres = dynamic_cast(res); + if (lres->isCodeShown()) + showCodeAction = menu->addAction(i18n("Show Rendered")); + else + showCodeAction = menu->addAction(i18n("Show Code")); + + connect(showCodeAction, SIGNAL(triggered()), this, + SLOT(toggleLatexCode())); + } + + menu->addSeparator(); + kDebug() << "populate Menu"; + emit menuCreated(menu, mapToParent(pos)); +} + +ResultItem* TextResultItem::updateFromResult(Cantor::Result* result) +{ + switch(result->type()) { + case Cantor::TextResult::Type: + { + QTextCursor cursor = textCursor(); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + QString html = result->toHtml(); + if (html.isEmpty()) + cursor.removeSelectedText(); + else + cursor.insertHtml(html); + return this; + } + case Cantor::LatexResult::Type: + setLatex(dynamic_cast(result)); + return this; + default: + deleteLater(); + return create(parentEntry(), result); + } +} + +void TextResultItem::setLatex(Cantor::LatexResult* result) +{ + QTextCursor cursor = textCursor(); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + QString latex = result->toLatex().trimmed(); + if (latex.startsWith("\\begin{eqnarray*}") && + latex.endsWith("\\end{eqnarray*}")) { + latex = latex.mid(17); + latex = latex.left(latex.size() - 15); + } + if (result->isCodeShown()) { + if (latex.isEmpty()) + cursor.removeSelectedText(); + else + cursor.insertText(latex); + } else { + QTextImageFormat format; + format = epsRenderer()->render(cursor.document(), + result->data().toUrl()); + format.setProperty(EpsRenderer::CantorFormula, + EpsRenderer::LatexFormula); + format.setProperty(EpsRenderer::Code, latex); + format.setProperty(EpsRenderer::Delimiter, "$$"); + if(format.isValid()) + cursor.insertText(QString(QChar::ObjectReplacementCharacter), format); + else + cursor.insertText(i18n("Cannot render Eps file. You may need additional packages")); + } +} + +void TextResultItem::toggleLatexCode() +{ + Cantor::LatexResult* lr = dynamic_cast(result()); + if(lr->isCodeShown()) + lr->showRendered(); + else + lr->showCode(); + + parentEntry()->updateEntry(); +} + +void TextResultItem::saveResult() +{ + Cantor::Result* res = result(); + const QString& filename = KFileDialog::getSaveFileName(KUrl(), res->mimeType(), worksheet()->worksheetView()); + kDebug() << "saving result to " << filename; + res->save(filename); +} + +void TextResultItem::deleteLater() +{ + WorksheetTextItem::deleteLater(); +} + +EpsRenderer* TextResultItem::epsRenderer() +{ + return qobject_cast(scene())->epsRenderer(); +} + +CommandEntry* TextResultItem::parentEntry() +{ + return qobject_cast(parentObject()); +} + +Cantor::Result* TextResultItem::result() +{ + return parentEntry()->expression()->result(); +} + +#include "textresultitem.moc" + diff --git a/src/resultcontextmenu.h b/src/textresultitem.h similarity index 52% rename from src/resultcontextmenu.h rename to src/textresultitem.h index 20fa55b1..f1306a4d 100644 --- a/src/resultcontextmenu.h +++ b/src/textresultitem.h @@ -1,63 +1,58 @@ /* 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 _RESULTCONTEXTMENU_H -#define _RESULTCONTEXTMENU_H +#ifndef TEXTRESULTITEM_H +#define TEXTRESULTITEM_H -#include +#include "resultitem.h" +#include "worksheettextitem.h" +#include "worksheetentry.h" +#include "epsrenderer.h" -class CommandEntry; -namespace Cantor{ - class Result; -} +#include "lib/latexresult.h" -/** - * this is the Menu shown when Right clicking on a Result. - * It offers different options depending on the Type of the - * result. - **/ -class ResultContextMenu : public KMenu +class TextResultItem : public WorksheetTextItem, public ResultItem { Q_OBJECT public: - ResultContextMenu( CommandEntry* entry, QWidget* parent); - ~ResultContextMenu(); + TextResultItem(QGraphicsObject* parent); + ~TextResultItem(); - CommandEntry* entry(); - Cantor::Result* result(); + double setGeometry(double x, double y, double w); + void populateMenu(KMenu* menu, const QPointF& pos); - public slots: - void saveResult(); - void removeResult(); - void latexToggleShowCode(); - void animationPause(); - void animationRestart(); + ResultItem* updateFromResult(Cantor::Result* result); - private: - void addGeneralActions(); - void addTypeSpecificActions(); + void setLatex(Cantor::LatexResult* result); - private: - CommandEntry* m_entry; - Cantor::Result* m_result; + void deleteLater(); + EpsRenderer* epsRenderer(); + CommandEntry* parentEntry(); + Cantor::Result* result(); + signals: + void removeResult(); + + protected slots: + void toggleLatexCode(); + void saveResult(); }; -#endif /* _RESULTCONTEXTMENU_H */ +#endif //TEXTRESULTITEM_H diff --git a/src/worksheet.cpp b/src/worksheet.cpp index 6f2dd0b3..2f445ba9 100644 --- a/src/worksheet.cpp +++ b/src/worksheet.cpp @@ -1,946 +1,1546 @@ /* 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 "lib/backend.h" -#include "lib/session.h" -#include "lib/extension.h" -#include "lib/expression.h" -#include "lib/result.h" -#include "lib/helpresult.h" -#include "lib/latexresult.h" -#include "lib/epsresult.h" -#include "lib/imageresult.h" -#include "lib/defaulthighlighter.h" - -#include "worksheetentry.h" -#include "commandentry.h" -#include "textentry.h" -#include "imageentry.h" -#include "pagebreakentry.h" -#include "latexentry.h" - -#include "resultproxy.h" -#include "animationhandler.h" -#include - -#include -#include -#include +#include +#include +#include #include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config-cantor.h" #include "worksheet.h" #include "settings.h" -#include "formulatextobject.h" +#include "commandentry.h" +#include "textentry.h" +#include "latexentry.h" +#include "imageentry.h" +#include "pagebreakentry.h" +#include "placeholderentry.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) : KRichTextWidget(parent) +Worksheet::Worksheet(Cantor::Backend* backend, QWidget* parent) + : QGraphicsScene(parent) { - setAcceptRichText(true); - setWordWrapMode(QTextOption::WordWrap); - setRichTextSupport( FullTextFormattingSupport - | FullListSupport - | SupportAlignment - | SupportRuleLine - | SupportFormatPainting ); - - m_session=backend->createSession(); - - setFont(KGlobalSettings::fixedFont()); + m_session = backend->createSession(); + m_highlighter = 0; - QFontMetrics metrics(document()->defaultFont()); - setTabStopWidth(4*metrics.width(' ')); + m_firstEntry = 0; + m_lastEntry = 0; + m_focusItem = 0; + m_dragEntry = 0; + m_placeholderEntry = 0; + m_viewWidth = 0; + m_protrusion = 0; + m_actionBarTimer = 0; - m_highlighter=0; - - m_proxy=new ResultProxy(document()); - document()->documentLayout()->registerHandler(QTextFormat::ImageObject, new AnimationHandler(document())); - document()->documentLayout()->registerHandler(FormulaTextObject::FormulaTextFormat, new FormulaTextObject()); - - m_isPrinting=false; - //postpone login, until everything is set up correctly - m_loginFlag=true; + 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 ) +void Worksheet::print(QPrinter* printer) { - m_proxy->useHighResolution(true); + m_epsRenderer.useHighResolution(true); m_isPrinting = true; - foreach(WorksheetEntry* e, m_entries) - e->update(); - + 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(); + } - KTextEdit::print(printer); + //render(&painter); + painter.end(); m_isPrinting = false; - - m_proxy->useHighResolution(false); - foreach(WorksheetEntry* e, m_entries) - e->update(); - + 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; } -bool Worksheet::event(QEvent* event) +void Worksheet::setViewSize(qreal w, qreal h, qreal s, bool forceUpdate) { - if (event->type() == QEvent::ShortcutOverride) - { - //ignore the shortcuts that are used to trigger valid KActions - //for the current WorksheetEntry - WorksheetEntry* entry = currentEntry(); - QKeyEvent* ke = static_cast( event ); - if (entry && entry->worksheetShortcutOverrideEvent( ke, textCursor() )) - { - event->ignore(); - return false; - } - } + Q_UNUSED(h); - return KRichTextWidget::event(event); + 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::setCurrentEntry(WorksheetEntry * entry, bool moveCursor) +void Worksheet::updateLayout() { - if (!entry) - return; - bool rt = entry->acceptRichText(); - setActionsEnabled(rt); - setAcceptRichText(rt); - m_currentEntry = entry; - entry->setActive(true, moveCursor); - ensureCursorVisible(); -} + bool cursorRectVisible = false; + bool atEnd = worksheetView()->isAtEnd(); + if (currentTextItem()) { + QRectF cursorRect = currentTextItem()->cursorRect(); + cursorRectVisible = worksheetView()->isVisible(cursorRect); + } -void Worksheet::moveToPreviousEntry() -{ - int index=m_entries.indexOf(currentEntry()); - kDebug()<<"index: "<0) - setCurrentEntry(m_entries[index-1]); + 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::moveToNextEntry() +void Worksheet::addProtrusion(qreal width) { - int index=m_entries.indexOf(currentEntry()); - kDebug()<<"index: "< m_protrusion) { + m_protrusion = width; + qreal y = lastEntry()->size().height() + lastEntry()->y(); + setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y)); + } } -void Worksheet::keyPressEvent(QKeyEvent* event) +void Worksheet::updateProtrusion(qreal oldWidth, qreal newWidth) { - WorksheetEntry *entry = entryAt(textCursor()); - if (entry && !entry->worksheetKeyPressEvent(event, textCursor())) - KRichTextWidget::keyPressEvent(event); + removeProtrusion(oldWidth); + addProtrusion(newWidth); } -void Worksheet::mousePressEvent(QMouseEvent* event) +void Worksheet::removeProtrusion(qreal width) { - kDebug()<<"mousePressEvent"; - const QTextCursor cursor = cursorForPosition(event->pos()); - WorksheetEntry *entry = entryAt(cursor); - if (entry) - { - if (!entry->worksheetMousePressEvent(event, cursor)) - KRichTextWidget::mousePressEvent(event); - if (entry != m_currentEntry) - setCurrentEntry(entry); + 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)); + } } } -void Worksheet::contextMenuEvent(QContextMenuEvent *event) +bool Worksheet::isEmpty() { - kDebug() << "contextMenuEvent"; - const QTextCursor cursor = cursorForPosition(event->pos()); - WorksheetEntry* entry = entryAt(cursor); - - if (entry && entry != m_currentEntry) - setCurrentEntry(entry); - if (!entry || !entry->worksheetContextMenuEvent(event, cursor)) - { - KMenu* defaultMenu = new KMenu(this); - - if(!isRunning()) - defaultMenu->addAction(KIcon("system-run"),i18n("Evaluate Worksheet"),this,SLOT(evaluate()),0); - else - defaultMenu->addAction(KIcon("process-stop"),i18n("Interrupt"),this,SLOT(interrupt()),0); - - defaultMenu ->addSeparator(); - - if(m_entries.last()->lastPosition() < cursor.position()) - { - defaultMenu->addAction(i18n("Append Command Entry"),this,SLOT(appendCommandEntry()),0); - defaultMenu->addAction(i18n("Append Text Entry"),this,SLOT(appendTextEntry()),0); - defaultMenu->addAction(i18n("Append Latex Entry"),this,SLOT(appendLatexEntry()),0); - defaultMenu->addAction(i18n("Append Image"),this,SLOT(appendImageEntry()),0); - defaultMenu->addAction(i18n("Append Page Break"),this,SLOT(appendPageBreakEntry()),0); - - } - else - { - setCurrentEntry(entryNextTo(cursor)); - defaultMenu->addAction(i18n("Insert Command Entry"),this,SLOT(insertCommandEntryBefore()),0); - defaultMenu->addAction(i18n("Insert Text Entry"),this,SLOT(insertTextEntryBefore()),0); - defaultMenu->addAction(i18n("Insert Latex Entry"),this,SLOT(insertLatexEntryBefore()),0); - defaultMenu->addAction(i18n("Insert Image"),this,SLOT(insertImageEntryBefore()),0); - defaultMenu->addAction(i18n("Insert Page Break"),this,SLOT(insertPageBreakEntryBefore()),0); - } - - defaultMenu->popup(event->globalPos()); - } + return !m_firstEntry; } -void Worksheet::mouseReleaseEvent(QMouseEvent* event) +void Worksheet::makeVisible(WorksheetEntry* entry) { - QTextCursor oldCursor=textCursor(); - KTextEdit::mouseMoveEvent(event); - if(!currentEntry()) - setTextCursor(oldCursor); + QRectF r = entry->boundingRect(); + r = entry->mapRectToScene(r); + r.adjust(0, -10, 0, 10); + worksheetView()->makeVisible(r); } -void Worksheet::mouseDoubleClickEvent(QMouseEvent* event) +void Worksheet::makeVisible(const WorksheetCursor& cursor) { - kDebug()<<"mouseDoubleClickEvent"; - const QTextCursor cursor = cursorForPosition(event->pos()); - WorksheetEntry *entry = entryAt(cursor); - if (!entry) - return; - KRichTextWidget::mouseDoubleClickEvent(event); - entry->worksheetMouseDoubleClickEvent(event, textCursor()); - if (entry != m_currentEntry) - setCurrentEntry(entry); + 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); } -void Worksheet::dragMoveEvent(QDragMoveEvent *event) +WorksheetView* Worksheet::worksheetView() { - const QPoint pos = event->answerRect().topLeft(); - const QTextCursor cursor = cursorForPosition(event->pos()); - WorksheetEntry *entry = entryAt(cursor); - if (entry && entry->acceptsDrop(cursor)) - event->setAccepted(true); - else - event->setAccepted(false); + return qobject_cast(views()[0]); } -void Worksheet::dropEvent(QDropEvent *event) +void Worksheet::setModified() { - const QTextCursor cursor = cursorForPosition(event->pos()); - WorksheetEntry *entry = entryAt(cursor); - if (entry != m_currentEntry) - setCurrentEntry(entry, false); - KRichTextWidget::dropEvent(event); + emit modified(); } -void Worksheet::evaluate() +WorksheetCursor Worksheet::worksheetCursor() { - kDebug()<<"evaluate worksheet"; - foreach(WorksheetEntry* entry, m_entries) - { - entry->evaluate(false); - } + WorksheetEntry* entry = currentEntry(); + WorksheetTextItem* item = currentTextItem(); - emit modified(); + if (!entry || !item) + return WorksheetCursor(); + return WorksheetCursor(entry, item, item->textCursor()); } -void Worksheet::evaluateCurrentEntry() +void Worksheet::setWorksheetCursor(const WorksheetCursor& cursor) { - kDebug() << "evaluation requested..."; - WorksheetEntry* entry = currentEntry(); - if(!entry) - return; - if (!entry->evaluate(true)) - return; - if(Settings::self()->autoEval()) - { - QList::iterator it=m_entries.begin(); - while((*it)!=entry&&it!=m_entries.end()) - ++it; + if (!cursor.isValid()) + return; - it++; + QGraphicsItem* item = m_focusItem; + while (item && item->type() != WorksheetTextItem::Type) + item = item->parentItem(); - for(;it!=m_entries.end();++it) - { - //kDebug()<<"evaluate"<command(); - (*it)->evaluate(false); - } - if(!m_entries.last()->isEmpty()) - appendCommandEntry(); - else - setCurrentEntry(m_entries.last()); - } + 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 - { - if (entry == m_entries.last()) - appendCommandEntry(); - else - moveToNextEntry(); + 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; } - emit modified(); + return 0; } -bool Worksheet::completionEnabled() +WorksheetEntry* Worksheet::firstEntry() { - return m_completionEnabled; + return m_firstEntry; } -void Worksheet::showCompletion() +WorksheetEntry* Worksheet::lastEntry() { - WorksheetEntry* current=currentEntry(); - current->showCompletion(); + return m_lastEntry; } -WorksheetEntry* Worksheet::currentEntry() +void Worksheet::setFirstEntry(WorksheetEntry* entry) { - return m_currentEntry; + 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); } -WorksheetEntry* Worksheet::entryAt(const QTextCursor& cursor) +void Worksheet::setLastEntry(WorksheetEntry* entry) { - foreach(WorksheetEntry* entry, m_entries) - { - if(entry->contains(cursor)) - return 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); +} - return 0; +void Worksheet::invalidateFirstEntry() +{ + if (m_firstEntry) + setFirstEntry(m_firstEntry->next()); } -WorksheetEntry* Worksheet::entryAt(int row) +void Worksheet::invalidateLastEntry() { - if(row>=0&&rowprevious()); } -WorksheetEntry* Worksheet::insertEntryAt(const int type, const QTextCursor& cursor) +WorksheetEntry* Worksheet::entryAt(qreal x, qreal y) { - WorksheetEntry* entry; + 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; +} - switch(type) - { - case TextEntry::Type: - entry = new TextEntry(cursor, this); - break; - case CommandEntry::Type: - entry = new CommandEntry(cursor, this); - break; - case ImageEntry::Type: - entry = new ImageEntry(cursor, this); - break; - case PageBreakEntry::Type: - entry = new PageBreakEntry(cursor, this); - break; - case LatexEntry::Type: - entry = new LatexEntry(cursor,this); - break; - default: - entry = 0; +WorksheetEntry* Worksheet::entryAt(QPointF p) +{ + return entryAt(p.x(), p.y()); +} + +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) +{ + m_dragEntry = entry; + WorksheetEntry* prev = entry->previous(); + WorksheetEntry* next = entry->next(); + m_placeholderEntry = new PlaceHolderEntry(this, entry->size()); + m_placeholderEntry->setPrevious(prev); + m_placeholderEntry->setNext(next); + if (prev) + prev->setNext(m_placeholderEntry); + else + setFirstEntry(m_placeholderEntry); + if (next) + next->setPrevious(m_placeholderEntry); + else + setLastEntry(m_placeholderEntry); + m_dragEntry->hide(); + Qt::DropAction action = drag->exec(); + + kDebug() << action; + if (action == Qt::MoveAction && m_placeholderEntry) { + kDebug() << "insert in new position"; + prev = m_placeholderEntry->previous(); + next = m_placeholderEntry->next(); + } + m_dragEntry->setPrevious(prev); + m_dragEntry->setNext(next); + if (prev) + prev->setNext(m_dragEntry); + else + setFirstEntry(m_dragEntry); + if (next) + next->setPrevious(m_dragEntry); + else + setLastEntry(m_dragEntry); + m_dragEntry->show(); + m_dragEntry->focusEntry(); + const QPointF scenePos = worksheetView()->sceneCursorPos(); + if (entryAt(scenePos) != m_dragEntry) + m_dragEntry->hideActionBar(); + updateLayout(); + if (m_placeholderEntry) { + m_placeholderEntry->setPrevious(0); + m_placeholderEntry->setNext(0); + m_placeholderEntry->hide(); + m_placeholderEntry->deleteLater(); + m_placeholderEntry = 0; } + m_dragEntry = 0; +} - return entry; +void Worksheet::evaluate() +{ + kDebug()<<"evaluate worksheet"; + firstEntry()->evaluate(WorksheetEntry::EvaluateNext); + + emit modified(); } -WorksheetEntry* Worksheet::entryNextTo(const QTextCursor& cursor) +void Worksheet::evaluateCurrentEntry() { - WorksheetEntry* entry=0; - foreach(entry, m_entries) - { - if (entry->lastPosition() > cursor.position()) - break; - } + kDebug() << "evaluation requested..."; + WorksheetEntry* entry = currentEntry(); + if(!entry) + return; + entry->evaluateCurrentItem(); +} - return entry; +bool Worksheet::completionEnabled() +{ + return m_completionEnabled; } +void Worksheet::showCompletion() +{ + WorksheetEntry* current = currentEntry(); + current->showCompletion(); +} WorksheetEntry* Worksheet::appendEntry(const int type) { - QTextCursor cursor=document()->rootFrame()->lastCursorPosition(); - WorksheetEntry* entry = insertEntryAt(type, cursor); + WorksheetEntry* entry = WorksheetEntry::create(type, this); + if (entry) { kDebug() << "Entry Appended"; - m_entries.append(entry); - setCurrentEntry(entry); + entry->setPrevious(lastEntry()); + if (lastEntry()) + lastEntry()->setNext(entry); + if (!firstEntry()) + 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=m_entries.last(); + WorksheetEntry* entry = lastEntry(); if(!entry->isEmpty()) { - entry=appendCommandEntry(); + entry = appendCommandEntry(); } if (entry) { - setCurrentEntry(entry); + focusEntry(entry); entry->setContent(text); evaluateCurrentEntry(); } } -WorksheetEntry* Worksheet::insertEntry(int type) +WorksheetEntry* Worksheet::insertEntry(const int type) { - WorksheetEntry* current=currentEntry(); - if(current) - { - int index=m_entries.indexOf(current); - WorksheetEntry* nextE = entryAt(index+1); + WorksheetEntry *current = currentEntry(); - if(!nextE || nextE->type() != type || !nextE->isEmpty()) - { - QTextCursor cursor = QTextCursor(document()); - cursor.setPosition(current->lastPosition()+1); - nextE = insertEntryAt(type, cursor); + if (!current) + return appendEntry(type); - m_entries.insert(index+1, nextE); + WorksheetEntry *next = current->next(); + WorksheetEntry *entry = 0; - } - setCurrentEntry(nextE); - return nextE; + if (!next || next->type() != type || !next->isEmpty()) + { + entry = WorksheetEntry::create(type, this); + entry->setPrevious(current); + entry->setNext(next); + current->setNext(entry); + if (next) + next->setPrevious(entry); + else + setLastEntry(entry); + updateLayout(); } - return 0; + focusEntry(entry); + makeVisible(entry); + return entry; } WorksheetEntry* Worksheet::insertTextEntry() { return insertEntry(TextEntry::Type); } -WorksheetEntry* Worksheet::insertImageEntry() +WorksheetEntry* Worksheet::insertCommandEntry() { - return insertEntry(ImageEntry::Type); + return insertEntry(CommandEntry::Type); } -WorksheetEntry* Worksheet::insertCommandEntry() +WorksheetEntry* Worksheet::insertImageEntry() { - return insertEntry(CommandEntry::Type); + 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) - { - int index=m_entries.indexOf(current); - WorksheetEntry* prevE = entryAt(index-1); + WorksheetEntry *current = currentEntry(); - if(!prevE || prevE->type() != type || !prevE->isEmpty()) - { - QTextCursor cursor = QTextCursor(document()); - cursor.setPosition(current->firstPosition()-1); - prevE = insertEntryAt(type, cursor); - - m_entries.insert(index, prevE); + if (!current) + return 0; - } + WorksheetEntry *prev = current->previous(); + WorksheetEntry *entry = 0; - setCurrentEntry(prevE); - return prevE; + if(!prev || prev->type() != type || !prev->isEmpty()) + { + entry = WorksheetEntry::create(type, this); + entry->setNext(current); + entry->setPrevious(prev); + current->setPrevious(entry); + if (prev) + prev->setNext(entry); + else + setFirstEntry(entry); + updateLayout(); } - return 0; + 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(highlight) { if(m_highlighter) m_highlighter->deleteLater(); m_highlighter=session()->syntaxHighlighter(this); if(!m_highlighter) - { m_highlighter=new Cantor::DefaultHighlighter(this); - } - }else - { + + // 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); - foreach( WorksheetEntry* entry, m_entries ) + 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( this, i18n( "Cannot write file %1." , filename ), + 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); - foreach(WorksheetEntry * const entry, m_entries) + 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: "<expression() ) -// { -// Cantor::Result* result=entry->expression()->result(); -// if(!result) -// continue; -// KUrl dest(filename); -// dest.setFileName(result->url().fileName()); -// kDebug()<<"saving image to "<type()==Cantor::ImageResult::Type||result->type()==Cantor::EpsResult::Type) -// result->save(dest.toLocalFile()); -// } -// } -// } - } - void Worksheet::load(const QString& filename ) { // m_file is always local so we can use QFile on it KZip file(filename); if (!file.open(QIODevice::ReadOnly)) return ; const KArchiveEntry* contentEntry=file.directory()->entry("content.xml"); if (!contentEntry->isFile()) { kDebug()<<"error"; } const KArchiveFile* content=static_cast(contentEntry); QByteArray data=content->data(); kDebug()<<"read: "<isEnabled()) { - KMessageBox::information(this, i18n("There are some problems with the %1 backend,\n"\ + 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; - foreach(WorksheetEntry* entry, m_entries) + for(WorksheetEntry* entry = firstEntry(); entry; entry = entry->next()) delete entry; clear(); - m_entries.clear(); + 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") + } else if (tag == "Text") { entry = appendTextEntry(); entry->setContent(expressionChild, file); - }else if (tag == "Latex") + } else if (tag == "Latex") { entry = appendLatexEntry(); entry->setContent(expressionChild, file); - } - else if (tag == "PageBreak") + } 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::removeEntry(QObject* object) -{ - kDebug()<<"removing entry"; - WorksheetEntry* entry=static_cast(object); - m_entries.removeAll(entry); - if(m_entries.isEmpty()) - appendCommandEntry(); -} - void Worksheet::removeCurrentEntry() { kDebug()<<"removing current entry"; WorksheetEntry* entry=currentEntry(); if(!entry) return; - int index=m_entries.indexOf(entry); + // In case we just removed this + m_focusItem = 0; + entry->startRemoving(); +} - int position=entry->firstPosition(); - kDebug()<lastPosition() + 1, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); +EpsRenderer* Worksheet::epsRenderer() +{ + return &m_epsRenderer; +} - delete entry; - m_entries.removeAll(entry); +KMenu* Worksheet::createContextMenu() +{ + KMenu *menu = new KMenu(worksheetView()); + connect(menu, SIGNAL(aboutToHide()), menu, SLOT(deleteLater())); - entry = entryAt(index); - if (!entry) - entry = entryAt(index + 1); - if (!entry) - entry = appendCommandEntry(); - setCurrentEntry(entry); + return menu; } +void Worksheet::populateMenu(KMenu *menu, const QPointF& pos) +{ + WorksheetEntry* entry = entryAt(pos); + 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::checkEntriesForSanity() +void Worksheet::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { - foreach(WorksheetEntry* e, m_entries) - { - e->checkForSanity(); + // 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()); } } -bool Worksheet::showExpressionIds() +void Worksheet::focusOutEvent(QFocusEvent* focusEvent) { - return m_showExpressionIds; + QGraphicsItem* item = focusItem(); + if (item) + m_focusItem = item; + QGraphicsScene::focusOutEvent(focusEvent); } -void Worksheet::enableExpressionNumbering(bool enable) +void Worksheet::mousePressEvent(QGraphicsSceneMouseEvent* event) { - m_showExpressionIds=enable; - emit updatePrompt(); + QGraphicsScene::mousePressEvent(event); + if (event->button() == Qt::LeftButton && !focusItem() && lastEntry() && + event->scenePos().y() > lastEntry()->y() + lastEntry()->size().height()) + lastEntry()->focusEntry(WorksheetTextItem::BottomRight); } -void Worksheet::zoomIn(int range) +void Worksheet::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { - KTextEdit::zoomIn(range); + QGraphicsScene::mouseMoveEvent(event); - m_proxy->scale(1+range/10.0); //Scale images for 10% + if (m_actionBarTimer) { + delete m_actionBarTimer; + m_actionBarTimer = 0; + } + WorksheetEntry* entry = entryAt(event->scenePos()); + if (entry) { + m_actionBarTimer = new QTimer(this); + m_actionBarTimer->setInterval(400); + m_actionBarTimer->setSingleShot(true); + connect(m_actionBarTimer, SIGNAL(timeout()), entry, + SLOT(showActionBar())); + m_actionBarTimer->start(); + } + + WorksheetEntry* oldEntry = entryAt(event->lastScenePos()); + if (oldEntry && oldEntry != entry) + oldEntry->hideActionBar(); +} + +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) +{ + WorksheetTextItem* oldItem = currentTextItem(); + + 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(WorksheetEntry* e, m_entries) - e->update(); + foreach(QWidget* widget, m_fontSizeAction->createdWidgets()) { + widget->setEnabled(b); + } + */ } -void Worksheet::zoomOut(int range) +WorksheetTextItem* Worksheet::currentTextItem() { - KTextEdit::zoomOut(range); - m_proxy->scale(1-range/10.0); //Scale images for 10% + QGraphicsItem* item = focusItem(); + if (!item) + item = m_focusItem; + while (item && item->type() != WorksheetTextItem::Type) + item = item->parentItem(); - foreach(WorksheetEntry* e, m_entries) - e->update(); + return qgraphicsitem_cast(item); } -ResultProxy* Worksheet::resultProxy() +void Worksheet::setTextForegroundColor() { - return m_proxy; + 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) +{ + kDebug() << "enter"; + if (m_dragEntry) + event->accept(); + else + QGraphicsScene::dragEnterEvent(event); +} + +void Worksheet::dragLeaveEvent(QGraphicsSceneDragDropEvent* event) +{ + if (!m_dragEntry) { + QGraphicsScene::dragLeaveEvent(event); + return; + } + + kDebug() << "leave"; + event->accept(); + if (m_placeholderEntry) { + m_placeholderEntry->startRemoving(); + m_placeholderEntry = 0; + } +} + +void Worksheet::dragMoveEvent(QGraphicsSceneDragDropEvent* event) +{ + if (!m_dragEntry) { + QGraphicsScene::dragMoveEvent(event); + return; + } + + QPointF pos = event->scenePos(); + WorksheetEntry* entry = entryAt(pos); + WorksheetEntry* prev = 0; + WorksheetEntry* next = 0; + if (entry) { + if (pos.y() < entry->y() + entry->size().height()/2) { + prev = entry->previous(); + next = entry; + } else if (pos.y() >= entry->y() + entry->size().height()/2) { + prev = entry; + next = entry->next(); + } + } else { + WorksheetEntry* last = lastEntry(); + if (last && pos.y() > last->y() + last->size().height()) { + prev = last; + next = 0; + } + } + + if (prev || next) { + PlaceHolderEntry* oldPlaceHolder = m_placeholderEntry; + if (prev && prev->type() == PlaceHolderEntry::Type && + (!prev->aboutToBeRemoved() || prev->stopRemoving())) { + m_placeholderEntry = qgraphicsitem_cast(prev); + m_placeholderEntry->changeSize(m_dragEntry->size()); + } else if (next && next->type() == PlaceHolderEntry::Type && + (!next->aboutToBeRemoved() || next->stopRemoving())) { + m_placeholderEntry = qgraphicsitem_cast(next); + m_placeholderEntry->changeSize(m_dragEntry->size()); + } else { + m_placeholderEntry = new PlaceHolderEntry(this, QSizeF(0,0)); + m_placeholderEntry->setPrevious(prev); + m_placeholderEntry->setNext(next); + if (prev) + prev->setNext(m_placeholderEntry); + else + setFirstEntry(m_placeholderEntry); + if (next) + next->setPrevious(m_placeholderEntry); + else + setLastEntry(m_placeholderEntry); + m_placeholderEntry->changeSize(m_dragEntry->size()); + } + if (oldPlaceHolder && oldPlaceHolder != m_placeholderEntry) + oldPlaceHolder->startRemoving(); + updateLayout(); + } + event->accept(); +} + +void Worksheet::dropEvent(QGraphicsSceneDragDropEvent* event) +{ + if (!m_dragEntry) + QGraphicsScene::dropEvent(event); + event->accept(); +} + + #include "worksheet.moc" diff --git a/src/worksheet.h b/src/worksheet.h index 2e8893c8..53406071 100644 --- a/src/worksheet.h +++ b/src/worksheet.h @@ -1,145 +1,254 @@ /* 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 +#ifndef WORKSHEET_H +#define WORKSHEET_H -#include -#include +#include +#include +#include +#include -namespace Cantor{ +#include +#include + +#include "worksheetview.h" +#include "epsrenderer.h" +#include "worksheetcursor.h" + +namespace Cantor { class Backend; class Session; class Expression; } + class WorksheetEntry; -class ResultProxy; -class TextEntry; +class PlaceHolderEntry; +class WorksheetTextItem; -class Worksheet : public KRichTextWidget +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::Backend* backend, QWidget* parent); ~Worksheet(); Cantor::Session* session(); bool isRunning(); bool showExpressionIds(); - - ResultProxy* resultProxy(); + 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(); + WorksheetTextItem* currentTextItem(); + + 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 setCurrentEntry(WorksheetEntry * entry, bool moveCursor = true); - void moveToPreviousEntry(); - void moveToNextEntry(); + void updateLayout(); + void updateEntrySize(WorksheetEntry* entry); + + void focusEntry(WorksheetEntry * entry); void evaluate(); void evaluateCurrentEntry(); void interrupt(); void interruptCurrentEntryEvaluation(); bool completionEnabled(); - void showCompletion(); + //void showCompletion(); + + void highlightItem(WorksheetTextItem*); void enableHighlighting(bool highlight); void enableCompletion(bool enable); void enableExpressionNumbering(bool enable); - - void zoomIn(int range=1); - void zoomOut(int range=1); + 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: - bool event(QEvent* event); - void keyPressEvent(QKeyEvent *event); - void contextMenuEvent(QContextMenuEvent *event); - void mousePressEvent(QMouseEvent* event); - void mouseReleaseEvent(QMouseEvent* event); - void mouseDoubleClickEvent(QMouseEvent* event); - void dragMoveEvent(QDragMoveEvent* event); - void dropEvent(QDropEvent *event); + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); + void focusOutEvent(QFocusEvent* focusEvent); + void mousePressEvent(QGraphicsSceneMouseEvent* event); + void mouseMoveEvent(QGraphicsSceneMouseEvent* event); + + void dragEnterEvent(QGraphicsSceneDragDropEvent* event); + void dragLeaveEvent(QGraphicsSceneDragDropEvent* event); + void dragMoveEvent(QGraphicsSceneDragDropEvent* event); + void dropEvent(QGraphicsSceneDragDropEvent* event); private slots: void loginToSession(); - void removeEntry(QObject* object); - void checkEntriesForSanity(); + void showCompletion(); + //void checkEntriesForSanity(); - WorksheetEntry* insertEntryAt(int type, const QTextCursor& cursor); WorksheetEntry* appendEntry(int type); WorksheetEntry* insertEntry(int type); WorksheetEntry* insertEntryBefore(int type); + private: - WorksheetEntry* currentEntry(); - WorksheetEntry* entryAt(const QTextCursor& cursor); + WorksheetEntry* entryAt(qreal x, qreal y); + WorksheetEntry* entryAt(QPointF p); WorksheetEntry* entryAt(int row); - WorksheetEntry* entryNextTo(const QTextCursor& cursor); + int entryCount(); + private: + static const double LeftMargin; + static const double RightMargin; + static const double TopMargin; Cantor::Session *m_session; - ResultProxy* m_proxy; QSyntaxHighlighter* m_highlighter; - QList m_entries; - WorksheetEntry* m_currentEntry; + EpsRenderer m_epsRenderer; + WorksheetEntry* m_firstEntry; + WorksheetEntry* m_lastEntry; + WorksheetEntry* m_dragEntry; + PlaceHolderEntry* m_placeholderEntry; + QGraphicsItem* m_focusItem; + QTimer* m_actionBarTimer; + + 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 */ +#endif // WORKSHEET_H diff --git a/src/animationhandler.h b/src/worksheetcursor.cpp similarity index 50% copy from src/animationhandler.h copy to src/worksheetcursor.cpp index 3b9cb61b..5445660d 100644 --- a/src/animationhandler.h +++ b/src/worksheetcursor.cpp @@ -1,44 +1,61 @@ /* 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 _ANIMATIONHANDLER_H -#define _ANIMATIONHANDLER_H +#include "worksheetcursor.h" -#include -#include +WorksheetCursor::WorksheetCursor() +{ + m_entry = 0; + m_textItem = 0; + m_textCursor = QTextCursor(); +} -class AnimationHandler : public QObject, public QTextObjectInterface +WorksheetCursor::WorksheetCursor(WorksheetEntry* entry, WorksheetTextItem* item, + const QTextCursor& cursor) { - Q_OBJECT - Q_INTERFACES(QTextObjectInterface) + m_entry = entry; + m_textItem = item; + m_textCursor = cursor; +} -public: - enum {MovieProperty = QTextFormat::UserProperty+10}; - AnimationHandler(QTextDocument *doc); +WorksheetCursor::~WorksheetCursor() +{ +} - QSizeF intrinsicSize(QTextDocument *doc, int posInDoc, const QTextFormat &format); +WorksheetEntry* WorksheetCursor::entry() const +{ + return m_entry; +} - void drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDoc, const QTextFormat &format); +WorksheetTextItem* WorksheetCursor::textItem() const +{ + return m_textItem; +} -private: - QTextObjectInterface *m_defaultAnimationHandler; -}; +QTextCursor WorksheetCursor::textCursor() const +{ + return m_textCursor; +} + +bool WorksheetCursor::isValid() const +{ + return m_entry && m_textItem; +} -#endif /* _ANIMATIONHANDLER_H */ diff --git a/src/animationhandler.h b/src/worksheetcursor.h similarity index 51% rename from src/animationhandler.h rename to src/worksheetcursor.h index 3b9cb61b..763a5d83 100644 --- a/src/animationhandler.h +++ b/src/worksheetcursor.h @@ -1,44 +1,48 @@ /* 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 _ANIMATIONHANDLER_H -#define _ANIMATIONHANDLER_H +#ifndef WORKSHEETCURSOR_H +#define WORKSHEETCURSOR_H -#include -#include +#include -class AnimationHandler : public QObject, public QTextObjectInterface -{ - Q_OBJECT - Q_INTERFACES(QTextObjectInterface) - -public: - enum {MovieProperty = QTextFormat::UserProperty+10}; - AnimationHandler(QTextDocument *doc); - - QSizeF intrinsicSize(QTextDocument *doc, int posInDoc, const QTextFormat &format); +class WorksheetEntry; +class WorksheetTextItem; - void drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDoc, const QTextFormat &format); - -private: - QTextObjectInterface *m_defaultAnimationHandler; +class WorksheetCursor +{ + public: + WorksheetCursor(); + WorksheetCursor(WorksheetEntry*, WorksheetTextItem*, const QTextCursor&); + ~WorksheetCursor(); + + WorksheetEntry* entry() const; + WorksheetTextItem* textItem() const; + QTextCursor textCursor() const; + + bool isValid() const; + + private: + WorksheetEntry* m_entry; + WorksheetTextItem* m_textItem; + QTextCursor m_textCursor; }; -#endif /* _ANIMATIONHANDLER_H */ +#endif // WORKSHEETCURSOR_H diff --git a/src/worksheetentry.cpp b/src/worksheetentry.cpp index a0c12a52..ad3c1b46 100644 --- a/src/worksheetentry.cpp +++ b/src/worksheetentry.cpp @@ -1,246 +1,772 @@ /* 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) 2010 Raffaele De Feo + Copyright (C) 2012 Martin Kuettler */ #include "worksheetentry.h" -#include "worksheet.h" +#include "commandentry.h" +#include "textentry.h" +#include "latexentry.h" +#include "imageentry.h" +#include "pagebreakentry.h" +#include "settings.h" +#include "actionbar.h" +#include "worksheettoolbutton.h" + +#include +#include +#include +#include + +#include +#include +#include -#include -#include -#include -#include +struct AnimationData +{ + QAnimationGroup* animation; + QPropertyAnimation* sizeAnimation; + QPropertyAnimation* opacAnimation; + QPropertyAnimation* posAnimation; + const char* slot; + QGraphicsObject* item; +}; -#include -#include -#include +const qreal WorksheetEntry::VerticalMargin = 4; -WorksheetEntry::WorksheetEntry(QTextCursor position, Worksheet* parent ) : QObject( parent ) +WorksheetEntry::WorksheetEntry(Worksheet* worksheet) : QGraphicsObject() { - m_worksheet = parent; + m_next = 0; + m_prev = 0; + m_animation = 0; + m_actionBar = 0; + m_actionBarAnimation = 0; + m_aboutToBeRemoved = false; + worksheet->addItem(this); +} - QTextFrameFormat frameFormat; - frameFormat.setBorderStyle(QTextFrameFormat::BorderStyle_Solid); - frameFormat.setBorder(1); - frameFormat.setLeftMargin(6); - frameFormat.setRightMargin(6); +WorksheetEntry::~WorksheetEntry() +{ + emit aboutToBeDeleted(); + if (next()) + next()->setPrevious(previous()); + if (previous()) + previous()->setNext(next()); + if (m_animation) { + m_animation->animation->deleteLater(); + delete m_animation; + } +} - connect(this, SIGNAL(destroyed(QObject*)), m_worksheet, SLOT(removeEntry(QObject*))); - connect(this, SIGNAL(leftmostValidPositionReached()), m_worksheet, SLOT(moveToPreviousEntry())); - connect(this, SIGNAL(rightmostValidPositionReached()), m_worksheet, SLOT(moveToNextEntry())); - connect(this, SIGNAL(topmostValidLineReached()), m_worksheet, SLOT(moveToPreviousEntry())); - connect(this, SIGNAL(bottommostValidLineReached()), m_worksheet, SLOT(moveToNextEntry())); +int WorksheetEntry::type() const +{ + return Type; +} - m_frame = position.insertFrame(frameFormat); +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; + } } -WorksheetEntry::~WorksheetEntry() +void WorksheetEntry::showCompletion() { +} +WorksheetEntry* WorksheetEntry::next() const +{ + return m_next; } -int WorksheetEntry::type() +WorksheetEntry* WorksheetEntry::previous() const { - return Type; + return m_prev; } -void WorksheetEntry::setActive(bool active, bool moveCursor) +void WorksheetEntry::setNext(WorksheetEntry* n) { - if (active && moveCursor && !isValidCursor(m_worksheet->textCursor())) - m_worksheet->setTextCursor(firstValidCursorPosition()); + m_next = n; } -QTextCursor WorksheetEntry::firstCursorPosition() +void WorksheetEntry::setPrevious(WorksheetEntry* p) { - if(m_frame) - return m_frame->firstCursorPosition(); - return QTextCursor(); + m_prev = p; } -QTextCursor WorksheetEntry::lastCursorPosition() +void WorksheetEntry::startDrag(const QPointF& grabPos) { - if(m_frame) - return m_frame->lastCursorPosition(); - return QTextCursor(); + QDrag* drag = new QDrag(worksheetView()); + kDebug() << size(); + const qreal scale = worksheet()->epsRenderer()->scale(); + QPixmap pixmap(size().toSize()*scale); + pixmap.fill(QColor(0, 0, 0, 0)); + QPainter painter(&pixmap); + painter.scale(scale, scale); + QStyleOptionGraphicsItem styleOptions; + styleOptions.rect = pixmap.rect(); + styleOptions.exposedRect = pixmap.rect(); + styleOptions.direction = worksheetView()->viewport()->layoutDirection(); + paint(&painter, &styleOptions, worksheetView()->viewport()); + + // paint all our descendents + QList indexStack; + QList itemStack; + QList children = childItems(); + int i = 0; + bool done = false; + QGraphicsItem* parent = this; + while (true) { + while (i == children.size()) { + if (itemStack.isEmpty()) { + done = true; + break; + } + painter.restore(); + i = indexStack.takeLast(); + parent = itemStack.takeLast(); + children = parent->childItems(); + } + if (done) + break; + painter.save(); + QGraphicsItem* child = children[i]; + if (child->isVisible()) { + painter.translate(child->x(), child->y()); + child->paint(&painter, &styleOptions, worksheetView()->viewport()); + } + if (!child->childItems().isEmpty()) { + indexStack.append(++i); + itemStack.append(parent); + parent = child; + children = child->childItems(); + i = 0; + } else { + ++i; + painter.restore(); + } + } + drag->setPixmap(pixmap); + if (grabPos.isNull()) { + const QPointF scenePos = worksheetView()->sceneCursorPos(); + drag->setHotSpot(mapFromScene(scenePos).toPoint() * scale); + } else { + drag->setHotSpot(grabPos.toPoint() * scale); + } + drag->setMimeData(new QMimeData()); + + worksheet()->startDrag(this, drag); } -int WorksheetEntry::firstPosition() + +QRectF WorksheetEntry::boundingRect() const { - if(m_frame) - return m_frame->firstCursorPosition().position(); - return -1; + return QRectF(QPointF(0,0), m_size); } -int WorksheetEntry::lastPosition() +void WorksheetEntry::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { - if(m_frame) - return m_frame->lastCursorPosition().position(); - return -1; + Q_UNUSED(painter); + Q_UNUSED(option); + Q_UNUSED(widget); } -bool WorksheetEntry::contains(const QTextCursor& cursor) +bool WorksheetEntry::focusEntry(int pos, qreal xCoord) { - if(!m_frame) - return false; + Q_UNUSED(pos); + Q_UNUSED(xCoord); - if(cursor.position()>=firstPosition() && cursor.position()<=lastPosition()) - return true; + if (flags() & QGraphicsItem::ItemIsFocusable) { + setFocus(); + return true; + } return false; } -int WorksheetEntry::firstValidPosition() +void WorksheetEntry::moveToPreviousEntry(int pos, qreal x) { - return firstValidCursorPosition().position(); + WorksheetEntry* entry = previous(); + while (entry && !(entry->wantFocus() && entry->focusEntry(pos, x))) + entry = entry->previous(); } -int WorksheetEntry::lastValidPosition() +void WorksheetEntry::moveToNextEntry(int pos, qreal x) { - return lastValidCursorPosition().position(); + WorksheetEntry* entry = next(); + while (entry && !(entry->wantFocus() && entry->focusEntry(pos, x))) + entry = entry->next(); } -bool WorksheetEntry::worksheetShortcutOverrideEvent(QKeyEvent* event, const QTextCursor& cursor) +Worksheet* WorksheetEntry::worksheet() { - Q_UNUSED(cursor); + return qobject_cast(scene()); +} - //tell Worksheet to ignore the following shortcuts, - //so they can be used as a Shortcut for a KAction: - //Shift+Return - //Shift+Delete +WorksheetView* WorksheetEntry::worksheetView() +{ + return worksheet()->worksheetView(); +} - int key = event->key(); +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); - int modifiers = event->modifiers(); - if (modifiers == Qt::ShiftModifier && (key == Qt::Key_Return || key == Qt::Key_Enter)) - return true; - else if (modifiers == Qt::ShiftModifier && key == Qt::Key_Delete) - return true; + return WorksheetCursor(); +} - return false; +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(); + } } -bool WorksheetEntry::worksheetKeyPressEvent(QKeyEvent* event, const QTextCursor& cursor) +void WorksheetEntry::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { - int key = event->key(); - int position = cursor.position(); + KMenu *menu = worksheet()->createContextMenu(); + populateMenu(menu, event->pos()); - if (key == Qt::Key_Left && position == firstValidPosition()) - { - emit leftmostValidPositionReached(); - kDebug()<<"Reached leftmost valid position"; - return true; + 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(); } - else if (key == Qt::Key_Right && position == lastValidPosition()) - { - emit rightmostValidPositionReached(); - kDebug()<<"Reached rightmost valid position"; - return true; +} +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(); } - else if (key == Qt::Key_Up) - { - QTextCursor c = QTextCursor(cursor); - c.setPosition(firstValidPosition(), QTextCursor::KeepAnchor); - QString txt = c.selectedText(); - - if(!(txt.contains(QChar::ParagraphSeparator)|| - txt.contains(QChar::LineSeparator)|| - txt.contains('\n'))) //there's still a newline above the cursor, so move only one line up - { - emit topmostValidLineReached(); - kDebug()<<"Reached topmost valid line"; - return true; - } - } - else if (key == Qt::Key_Down ) - { - QTextCursor c = QTextCursor(cursor); - c.setPosition(lastValidPosition(), QTextCursor::KeepAnchor); - QString txt = c.selectedText(); - - if(!(txt.contains(QChar::ParagraphSeparator)|| - txt.contains(QChar::LineSeparator)|| - txt.contains('\n'))) //there's still a newline under the cursor, so move only one line down - { - emit bottommostValidLineReached(); - kDebug()<<"Reached bottommost valid line"; - return true; - } + kDebug() << oldSize << newSize; + + QPropertyAnimation* sizeAn = new QPropertyAnimation(this, "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; } - return false; + 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(); } -bool WorksheetEntry::worksheetMousePressEvent(QMouseEvent* event, const QTextCursor& cursor) +void WorksheetEntry::fadeInItem(QGraphicsObject* item, const char* slot) { - Q_UNUSED(event); - Q_UNUSED(cursor); + 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(); +} - return false; +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(); } -bool WorksheetEntry::worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor) +void WorksheetEntry::endAnimation() { - Q_UNUSED(event); - Q_UNUSED(cursor); + 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; +} - return false; +bool WorksheetEntry::animationActive() +{ + return m_animation; } -bool WorksheetEntry::worksheetMouseDoubleClickEvent(QMouseEvent* event, const QTextCursor& cursor) +void WorksheetEntry::updateSizeAnimation(const QSizeF& size) { - Q_UNUSED(event); - Q_UNUSED(cursor); + // 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); + } +} - return false; +void WorksheetEntry::invokeSlotOnObject(const char* slot, QObject* 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); } -void WorksheetEntry::checkForSanity() +bool WorksheetEntry::aboutToBeRemoved() { + return m_aboutToBeRemoved; +} +void WorksheetEntry::startRemoving() +{ + if (!worksheet()->animationsEnabled()) { + m_aboutToBeRemoved = true; + remove(); + return; + } + if (m_aboutToBeRemoved) + return; + + if (focusItem()) { + 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, "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::showCompletion() +bool WorksheetEntry::stopRemoving() +{ + if (!m_aboutToBeRemoved) + return true; + + if (m_animation->animation->state() == QAbstractAnimation::Stopped) + // we are too late to stop the deletion + return false; + + m_aboutToBeRemoved = false; + m_animation->animation->stop(); + m_animation->animation->deleteLater(); + delete m_animation; + m_animation = 0; + return true; +} + +void WorksheetEntry::remove() +{ + if (!m_aboutToBeRemoved) + return; + + if (previous() && previous()->next() == this) + previous()->setNext(next()); + else + worksheet()->setFirstEntry(next()); + if (next() && next()->previous() == this) + next()->setPrevious(previous()); + else + worksheet()->setLastEntry(previous()); + + // make the entry invisible to QGraphicsScene's itemAt() function + hide(); + worksheet()->updateLayout(); + deleteLater(); +} + +void WorksheetEntry::setSize(QSizeF size) +{ + prepareGeometryChange(); + if (m_actionBar && size != m_size) + m_actionBar->updatePosition(size); + m_size = size; +} + +QSizeF WorksheetEntry::size() +{ + return m_size; +} + +bool WorksheetEntry::hasActionBar() +{ + return m_actionBar; +} + +void WorksheetEntry::showActionBar() { + if (m_actionBar && !m_actionBarAnimation) + return; + + if (m_actionBarAnimation) { + if (m_actionBarAnimation->endValue().toReal() == 1) + return; + m_actionBarAnimation->stop(); + delete m_actionBarAnimation; + m_actionBarAnimation = 0; + } + + if (!m_actionBar) { + m_actionBar = new ActionBar(this); + + m_actionBar->addButton(KIcon("edit-delete"), i18n("Remove Entry"), + this, SLOT(startRemoving())); + WorksheetToolButton* dragButton; + dragButton = m_actionBar->addButton(KIcon("transform-move"), + i18n("Drag Entry")); + connect(dragButton, SIGNAL(pressed()), this, SLOT(startDrag())); + + if (wantToEvaluate()) { + QString toolTip = i18n("Evaluate Entry"); + m_actionBar->addButton(KIcon("view-refresh"), toolTip, + this, SLOT(evaluate())); + } + + m_actionBar->addSpace(); + + addActionsToBar(m_actionBar); + } + + if (worksheet()->animationsEnabled()) { + m_actionBarAnimation = new QPropertyAnimation(m_actionBar, "opacity", + this); + m_actionBarAnimation->setStartValue(0); + m_actionBarAnimation->setEndValue(1); + m_actionBarAnimation->setEasingCurve(QEasingCurve::Linear); + m_actionBarAnimation->setDuration(200); + connect(m_actionBarAnimation, SIGNAL(finished()), this, + SLOT(deleteActionBarAnimation())); + + m_actionBarAnimation->start(); + } } -void WorksheetEntry::createSubMenuInsert(KMenu* menu) +void WorksheetEntry::hideActionBar() { - KMenu* subMenuInsert = new KMenu(menu); - KMenu* subMenuInsertBefore = new KMenu(menu); + if (!m_actionBar) + return; + + if (m_actionBarAnimation) { + if (m_actionBarAnimation->endValue().toReal() == 0) + return; + m_actionBarAnimation->stop(); + delete m_actionBarAnimation; + m_actionBarAnimation = 0; + } - subMenuInsert->addAction(i18n("Command Entry"), m_worksheet, SLOT(insertCommandEntry())); - subMenuInsert->addAction(i18n("Text Entry"), m_worksheet, SLOT(insertTextEntry())); - subMenuInsert->addAction(i18n("Image Entry"), m_worksheet, SLOT(insertImageEntry())); - subMenuInsert->addAction(i18n("Page Break"), m_worksheet, SLOT(insertPageBreakEntry())); + if (worksheet()->animationsEnabled()) { + m_actionBarAnimation = new QPropertyAnimation(m_actionBar, "opacity", + this); + m_actionBarAnimation->setEndValue(0); + m_actionBarAnimation->setEasingCurve(QEasingCurve::Linear); + m_actionBarAnimation->setDuration(200); + connect(m_actionBarAnimation, SIGNAL(finished()), this, + SLOT(deleteActionBar())); + + m_actionBarAnimation->start(); + } else { + deleteActionBar(); + } +} + +void WorksheetEntry::deleteActionBarAnimation() +{ + if (m_actionBarAnimation) { + delete m_actionBarAnimation; + m_actionBarAnimation = 0; + } +} - subMenuInsertBefore->addAction(i18n("Command Entry"), m_worksheet, SLOT(insertCommandEntryBefore())); - subMenuInsertBefore->addAction(i18n("Text Entry"), m_worksheet, SLOT(insertTextEntryBefore())); - subMenuInsertBefore->addAction(i18n("Image Entry"), m_worksheet, SLOT(insertImageEntryBefore())); - subMenuInsertBefore->addAction(i18n("Page Break"), m_worksheet, SLOT(insertPageBreakEntryBefore())); +void WorksheetEntry::deleteActionBar() +{ + if (m_actionBar) { + delete m_actionBar; + m_actionBar = 0; + } - subMenuInsert->setTitle(i18n("Insert Entry")); - subMenuInsertBefore->setTitle(i18n("Insert Entry Before")); - menu->addSeparator(); - menu->addMenu(subMenuInsert); - menu->addMenu(subMenuInsertBefore); + deleteActionBarAnimation(); } +void WorksheetEntry::addActionsToBar(ActionBar*) +{ +} + +WorksheetTextItem* WorksheetEntry::highlightItem() +{ + return 0; +} + +bool WorksheetEntry::wantFocus() +{ + return true; +} #include "worksheetentry.moc" diff --git a/src/worksheetentry.h b/src/worksheetentry.h index 68820f34..810b7ab3 100644 --- a/src/worksheetentry.h +++ b/src/worksheetentry.h @@ -1,110 +1,172 @@ /* 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) 2010 Raffaele De Feo + Copyright (C) 2012 Martin Kuettler */ -#ifndef _WORKSHEETENTRY_H -#define _WORKSHEETENTRY_H - -#include -#include -#include -#include -#include -#include "lib/expression.h" - -namespace Cantor{ - class Expression; - class Result; - class CompletionObject; - class SyntaxHelpObject; -} -class Worksheet; -class KCompletionBox; - -class WorksheetEntry : public QObject +#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 ActionBar; + +class QPainter; +class QWidget; +class QPropertyAnimation; + +struct AnimationData; + +class WorksheetEntry : public QGraphicsObject { Q_OBJECT public: - WorksheetEntry(QTextCursor position, Worksheet* parent); - ~WorksheetEntry(); + WorksheetEntry(Worksheet* worksheet); + virtual ~WorksheetEntry(); - enum {Type = 0}; + enum {Type = UserType}; - virtual int type(); + virtual int type() const; virtual bool isEmpty()=0; - virtual void setActive(bool active, bool moveCursor); + static WorksheetEntry* create(int t, Worksheet* worksheet); - int firstPosition(); - int lastPosition(); - QTextCursor firstCursorPosition(); - QTextCursor lastCursorPosition(); - bool contains(const QTextCursor& cursor); + WorksheetEntry* next() const; + WorksheetEntry* previous() const; - virtual QTextCursor closestValidCursor(const QTextCursor& cursor)=0; - virtual QTextCursor firstValidCursorPosition()=0; - virtual QTextCursor lastValidCursorPosition()=0; - int firstValidPosition(); - int lastValidPosition(); - virtual bool isValidCursor(const QTextCursor& cursor)=0; + void setNext(WorksheetEntry*); + void setPrevious(WorksheetEntry*); - // Handlers for the worksheet input events affecting worksheetentries - virtual bool worksheetShortcutOverrideEvent(QKeyEvent* event, const QTextCursor& cursor); - virtual bool worksheetKeyPressEvent(QKeyEvent* event, const QTextCursor& cursor); - virtual bool worksheetMousePressEvent(QMouseEvent* event, const QTextCursor& cursor); - virtual bool worksheetContextMenuEvent(QContextMenuEvent* event, const QTextCursor& cursor); - virtual bool worksheetMouseDoubleClickEvent(QMouseEvent* event, const QTextCursor& cursor); + QRectF boundingRect() const; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); - virtual bool acceptRichText()=0; - virtual bool acceptsDrop(const QTextCursor& cursor)=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(QString& commandSep, QString& commentStartingSeq, QString& commentEndingSeq)=0; + virtual QString toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq)=0; virtual void interruptEvaluation()=0; - virtual bool evaluate(bool current)=0; + virtual void showCompletion(); - virtual void checkForSanity(); + virtual bool focusEntry(int pos = WorksheetTextItem::TopLeft, qreal xCoord = 0); - virtual void showCompletion(); + virtual qreal setGeometry(qreal x, qreal y, qreal w); + virtual void layOutForWidth(qreal w, bool force = false) = 0; + QPropertyAnimation* sizeChangeAnimation(QSizeF s = QSizeF()); - signals: - void leftmostValidPositionReached(); - void rightmostValidPositionReached(); - void topmostValidLineReached(); - void bottommostValidLineReached(); + virtual void populateMenu(KMenu *menu, const QPointF& pos); + + bool aboutToBeRemoved(); + QSizeF size(); + + enum EvaluationOption { + DoNothing, FocusNext, EvaluateNext + }; + + virtual WorksheetTextItem* highlightItem(); + + bool hasActionBar(); + + 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 void update()=0; + virtual bool evaluate(EvaluationOption evalOp = FocusNext) = 0; + virtual bool evaluateCurrentItem(); + virtual void updateEntry() = 0; + virtual void sizeAnimated(); + virtual void startRemoving(); + bool stopRemoving(); + 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(); + + void showActionBar(); + void hideActionBar(); + + void startDrag(const QPointF& grabPos = QPointF()); + + signals: + void aboutToBeDeleted(); protected: - void createSubMenuInsert(KMenu* menu); + Worksheet* worksheet(); + WorksheetView* worksheetView(); + 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 void addActionsToBar(ActionBar* actionBar); + + virtual bool wantToEvaluate() = 0; + virtual bool wantFocus(); + + protected slots: + virtual void remove(); + void deleteActionBar(); + void deleteActionBarAnimation(); protected: - QTextFrame* m_frame; - Worksheet* m_worksheet; + static const qreal VerticalMargin; + + private: + QSizeF m_size; + WorksheetEntry* m_prev; + WorksheetEntry* m_next; + Q_PROPERTY(QSizeF size READ size WRITE setSize); + AnimationData* m_animation; + ActionBar* m_actionBar; + QPropertyAnimation* m_actionBarAnimation; + bool m_aboutToBeRemoved; }; -#endif /* _WORKSHEETENTRY_H */ +#endif // WORKSHEETENTRY_H diff --git a/src/worksheetimageitem.cpp b/src/worksheetimageitem.cpp new file mode 100644 index 00000000..0fa08211 --- /dev/null +++ b/src/worksheetimageitem.cpp @@ -0,0 +1,163 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) 2012 Martin Kuettler + */ + +#include "worksheetimageitem.h" +#include "worksheet.h" + +#include +#include +#include +#include +#include +#include + +WorksheetImageItem::WorksheetImageItem(QGraphicsObject* parent) + : QGraphicsObject(parent) +{ + connect(this, SIGNAL(menuCreated(KMenu*, const QPointF&)), parent, + SLOT(populateMenu(KMenu*, const QPointF&)), Qt::DirectConnection); + m_maxWidth = 0; +} + +WorksheetImageItem::~WorksheetImageItem() +{ + if (worksheet() && m_maxWidth > 0 && width() > m_maxWidth) + worksheet()->removeProtrusion(width() - m_maxWidth); +} + +int WorksheetImageItem::type() const +{ + return Type; +} + +bool WorksheetImageItem::imageIsValid() +{ + return !m_pixmap.isNull(); +} + +qreal WorksheetImageItem::setGeometry(qreal x, qreal y, qreal w, bool centered) +{ + if (width() <= w && centered) { + setPos(x + w/2 - width()/2, y); + } else { + setPos(x, y); + if (m_maxWidth < width()) + worksheet()->updateProtrusion(width() - m_maxWidth, width() - w); + else + worksheet()->addProtrusion(width() - w); + } + m_maxWidth = w; + + return height(); +} + +qreal WorksheetImageItem::height() const +{ + return m_size.height(); +} + +qreal WorksheetImageItem::width() const +{ + return m_size.width(); +} + +QSizeF WorksheetImageItem::size() +{ + return m_size; +} + +void WorksheetImageItem::setSize(QSizeF size) +{ + qreal oldProtrusion = x() + m_size.width() - m_maxWidth; + qreal newProtrusion = x() + size.width() - m_maxWidth; + if (oldProtrusion > 0) { + if (newProtrusion > 0) + worksheet()->updateProtrusion(oldProtrusion, newProtrusion); + else + worksheet()->removeProtrusion(oldProtrusion); + } else { + if (newProtrusion > 0) + worksheet()->addProtrusion(newProtrusion); + } + m_size = size; +} + +QSize WorksheetImageItem::imageSize() +{ + return m_pixmap.size(); +} + +QRectF WorksheetImageItem::boundingRect() const +{ + return QRectF(QPointF(0, 0), m_size); +} + +#include + +void WorksheetImageItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + kDebug() << option->exposedRect << option->direction << option->palette + << option->rect << option->state << option->type; + kDebug() << widget; + painter->drawPixmap(QRectF(QPointF(0,0), m_size), m_pixmap, + m_pixmap.rect()); +} + +void WorksheetImageItem::setEps(const KUrl& url) +{ + const QImage img = worksheet()->epsRenderer()->renderToImage(url, &m_size); + m_pixmap = QPixmap::fromImage(img.convertToFormat(QImage::Format_ARGB32)); +} + +void WorksheetImageItem::setImage(QImage img) +{ + m_pixmap = QPixmap::fromImage(img); + setSize(m_pixmap.size()); +} + +void WorksheetImageItem::setPixmap(QPixmap pixmap) +{ + m_pixmap = pixmap; +} + +void WorksheetImageItem::populateMenu(KMenu *menu, const QPointF& pos) +{ + emit menuCreated(menu, mapToParent(pos)); +} + +void WorksheetImageItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + KMenu *menu = worksheet()->createContextMenu(); + populateMenu(menu, event->pos()); + + menu->popup(event->screenPos()); +} + +Worksheet* WorksheetImageItem::worksheet() +{ + return qobject_cast(scene()); +} + +#include "worksheetimageitem.moc" + diff --git a/src/worksheetimageitem.h b/src/worksheetimageitem.h new file mode 100644 index 00000000..eb439c61 --- /dev/null +++ b/src/worksheetimageitem.h @@ -0,0 +1,80 @@ +/* + 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 WORKSHEETIMAGEITEM_H +#define WORKSHEETIMAGEITEM_H + +#include +#include + +class Worksheet; +class QMovie; +class QImage; +class QGraphicsSceneContextMenuEvent; +class KUrl; +class KMenu; + +class WorksheetImageItem : public QGraphicsObject +{ + Q_OBJECT + public: + WorksheetImageItem(QGraphicsObject* parent); + ~WorksheetImageItem(); + + enum {Type = UserType + 101}; + + int type() const; + + bool imageIsValid(); + + virtual qreal setGeometry(qreal x, qreal y, qreal w, bool centered=false); + + qreal height() const; + qreal width() const; + QSizeF size(); + void setSize(QSizeF size); + QSize imageSize(); + + QRectF boundingRect() const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget = 0); + + void setEps(const KUrl& url); + void setImage(QImage img); + void setPixmap(QPixmap pixmap); + + virtual void populateMenu(KMenu *menu, const QPointF& pos); + Worksheet* worksheet(); + + signals: + void sizeChanged(); + void menuCreated(KMenu*, const QPointF&); + + protected: + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); + + private: + QPixmap m_pixmap; + QSizeF m_size; + qreal m_maxWidth; +}; + +#endif //WORKSHEETIMAGEITEM_H diff --git a/src/worksheettextitem.cpp b/src/worksheettextitem.cpp new file mode 100644 index 00000000..af4b09ed --- /dev/null +++ b/src/worksheettextitem.cpp @@ -0,0 +1,853 @@ +/* + 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 "worksheettextitem.h" +#include "worksheet.h" +#include "worksheetentry.h" +#include "epsrenderer.h" +#include "worksheetcursor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +WorksheetTextItem::WorksheetTextItem(QGraphicsObject* parent, Qt::TextInteractionFlags ti) + : QGraphicsTextItem(parent) +{ + setTextInteractionFlags(ti); + if (ti & Qt::TextEditable) { + setCursor(Qt::IBeamCursor); + connect(this, SIGNAL(sizeChanged()), parent, + SLOT(recalculateSize())); + } + m_completionEnabled = false; + m_completionActive = false; + m_itemDragable = false; + m_richTextEnabled = false; + m_size = document()->size();; + m_maxWidth = -1; + setAcceptDrops(true); + setFont(KGlobalSettings::fixedFont()); + //connect(document(), SIGNAL(contentsChange(int, int, int)), + // this, SLOT(setHeight())); + connect(document(), SIGNAL(contentsChanged()), + this, SLOT(testSize())); + connect(this, SIGNAL(menuCreated(KMenu*, const QPointF&)), parent, + SLOT(populateMenu(KMenu*, const QPointF&)), Qt::DirectConnection); + connect(this, SIGNAL(deleteEntry()), parent, SLOT(startRemoving())); + connect(this, SIGNAL(cursorPositionChanged(QTextCursor)), this, + SLOT(updateRichTextActions(QTextCursor))); +} + +WorksheetTextItem::~WorksheetTextItem() +{ + if (worksheet() && this == worksheet()->currentTextItem()) + worksheet()->updateFocusedTextItem(0); + if (worksheet() && m_maxWidth > 0 && width() > m_maxWidth) + worksheet()->removeProtrusion(width() - m_maxWidth); +} + +int WorksheetTextItem::type() const +{ + return Type; +} + +/* +void WorksheetTextItem::setHeight() +{ + m_height = height(); +} +*/ + +void WorksheetTextItem::testSize() +{ + qreal h = document()->size().height(); + if (h != m_size.height()) { + emit sizeChanged(); + m_size.setHeight(h); + } + + qreal w = document()->size().width(); + if (w != m_size.width()) { + if (m_maxWidth > 0) { + qreal oldDiff = m_size.width() - m_maxWidth; + qreal newDiff = w - m_maxWidth; + if (w > m_maxWidth) { + if (m_size.width() > m_maxWidth) + worksheet()->updateProtrusion(oldDiff, newDiff); + else + worksheet()->addProtrusion(newDiff); + } else if (m_size.width() > m_maxWidth) { + worksheet()->removeProtrusion(oldDiff); + } + } + m_size.setWidth(w); + } +} + +qreal WorksheetTextItem::setGeometry(qreal x, qreal y, qreal w, bool centered) +{ + if (m_size.width() < w && centered) + setPos(x + w/2 - m_size.width()/2, y); + else + setPos(x,y); + + qreal oldDiff = 0; + if (m_maxWidth > 0 && width() > m_maxWidth) + oldDiff = width() - m_maxWidth; + m_maxWidth = w; + setTextWidth(w); + m_size = document()->size(); + + if (oldDiff) { + if (m_size.width() > m_maxWidth) { + qreal newDiff = m_size.width() - m_maxWidth; + worksheet()->updateProtrusion(oldDiff, newDiff); + } else { + worksheet()->removeProtrusion(oldDiff); + } + } else if (m_size.width() > m_maxWidth) { + qreal newDiff = m_size.width() - m_maxWidth; + worksheet()->addProtrusion(newDiff); + } + + return m_size.height(); +} + +void WorksheetTextItem::populateMenu(KMenu *menu, const QPointF& pos) +{ + kDebug() << "populate Menu"; + KAction* cut = KStandardAction::cut(this, SLOT(cut()), menu); + KAction* copy = KStandardAction::copy(this, SLOT(copy()), menu); + KAction* paste = KStandardAction::paste(this, SLOT(paste()), menu); + if (!textCursor().hasSelection()) { + cut->setEnabled(false); + copy->setEnabled(false); + } + if (QApplication::clipboard()->text().isEmpty()) { + paste->setEnabled(false); + } + if (isEditable()) + menu->addAction(cut); + if (!m_itemDragable) + menu->addAction(copy); + if (isEditable()) + menu->addAction(paste); + menu->addSeparator(); + + emit menuCreated(menu, mapToParent(pos)); +} + +QKeyEvent* WorksheetTextItem::eventForStandardAction(KStandardAction::StandardAction actionID) +{ + // there must be a better way to get the shortcut... + KAction* action = KStandardAction::create(actionID, this, 0, this); + QKeySequence keySeq = action->shortcut().primary(); + // we do not support key sequences with multiple keys here + int code = keySeq[0]; + const int ModMask = Qt::ShiftModifier | Qt::ControlModifier | + Qt::AltModifier | Qt::MetaModifier; + const int KeyMask = ~ModMask; + QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, code & KeyMask, + QFlags(code & ModMask)); + delete action; + return event; +} + +void WorksheetTextItem::cut() +{ + if (richTextEnabled()) { + QKeyEvent* event = eventForStandardAction(KStandardAction::Cut); + QApplication::sendEvent(worksheet(), event); + delete event; + } else { + copy(); + textCursor().removeSelectedText(); + } +} + +void WorksheetTextItem::paste() +{ + if (richTextEnabled()) { + QKeyEvent* event = eventForStandardAction(KStandardAction::Paste); + QApplication::sendEvent(worksheet(), event); + delete event; + } else { + textCursor().insertText(QApplication::clipboard()->text()); + } +} + +void WorksheetTextItem::copy() +{ + if (richTextEnabled()) { + QKeyEvent* event = eventForStandardAction(KStandardAction::Copy); + QApplication::sendEvent(worksheet(), event); + delete event; + } else { + if (!textCursor().hasSelection()) + return; + QApplication::clipboard()->setText(resolveImages(textCursor())); + } +} + +QString WorksheetTextItem::resolveImages(const QTextCursor& cursor) +{ + int start = cursor.selectionStart(); + int end = cursor.selectionEnd(); + + const QString repl = QString(QChar::ObjectReplacementCharacter); + QString result; + QTextCursor cursor1 = textCursor(); + cursor1.setPosition(start); + QTextCursor cursor2 = document()->find(repl, cursor1); + + for (; !cursor2.isNull() && cursor2.selectionEnd() <= end; + cursor2 = document()->find(repl, cursor1)) { + cursor1.setPosition(cursor2.selectionStart(), QTextCursor::KeepAnchor); + result += cursor1.selectedText(); + QVariant var = cursor2.charFormat().property(EpsRenderer::Delimiter); + QString delim; + if (var.isValid()) + delim = qVariantValue(var); + else + delim = ""; + result += delim + qVariantValue(cursor2.charFormat().property(EpsRenderer::Code)) + delim; + cursor1.setPosition(cursor2.selectionEnd()); + } + + cursor1.setPosition(end, QTextCursor::KeepAnchor); + result += cursor1.selectedText(); + return result; +} + +void WorksheetTextItem::setCursorPosition(const QPointF& pos) +{ + QTextCursor cursor = cursorForPosition(pos); + setTextCursor(cursor); + emit cursorPositionChanged(cursor); + //setLocalCursorPosition(mapFromParent(pos)); +} + +QPointF WorksheetTextItem::cursorPosition() const +{ + return mapToParent(localCursorPosition()); +} + +void WorksheetTextItem::setLocalCursorPosition(const QPointF& pos) +{ + int p = document()->documentLayout()->hitTest(pos, Qt::FuzzyHit); + QTextCursor cursor = textCursor(); + cursor.setPosition(p); + setTextCursor(cursor); + emit cursorPositionChanged(cursor); +} + +QPointF WorksheetTextItem::localCursorPosition() const +{ + QTextCursor cursor = textCursor(); + QTextBlock block = cursor.block(); + int p = cursor.position() - block.position(); + QTextLine line = block.layout()->lineForTextPosition(p); + if (!line.isValid()) // can this happen? + return block.layout()->position(); + return QPointF(line.cursorToX(p), line.y() + line.height()); +} + +QRectF WorksheetTextItem::cursorRect(QTextCursor cursor) const +{ + if (cursor.isNull()) + cursor = textCursor(); + QTextCursor startCursor = cursor; + startCursor.setPosition(cursor.selectionStart()); + QTextBlock block = startCursor.block(); + int p = startCursor.position() - block.position(); + QTextLine line = block.layout()->lineForTextPosition(p); + QRectF r1(line.cursorToX(p), line.y(), 1, line.height()+line.leading()); + r1 = mapRectToScene(r1); + + if (!cursor.hasSelection()) + return r1; + + QTextCursor endCursor = cursor; + endCursor.setPosition(cursor.selectionEnd()); + block = endCursor.block(); + p = endCursor.position() - block.position(); + line = block.layout()->lineForTextPosition(p); + QRectF r2(line.cursorToX(p), line.y(), 1, line.height()+line.leading()); + r2 = mapRectToScene(r2); + + if (r1.y() == r2.y()) + return r1.united(r2); + else + return QRectF(x(), qMin(r1.y(), r2.y()), boundingRect().width(), + qMax(r1.y() + r1.height(), r2.y() + r2.height())); +} + +QTextCursor WorksheetTextItem::cursorForPosition(const QPointF& pos) const +{ + QPointF lpos = mapFromParent(pos); + int p = document()->documentLayout()->hitTest(lpos, Qt::FuzzyHit); + QTextCursor cursor = textCursor(); + cursor.setPosition(p); + return cursor; +} + +bool WorksheetTextItem::isEditable() +{ + return textInteractionFlags() & Qt::TextEditable; +} + +bool WorksheetTextItem::richTextEnabled() +{ + return m_richTextEnabled; +} + +void WorksheetTextItem::enableCompletion(bool b) +{ + m_completionEnabled = b; +} + +void WorksheetTextItem::activateCompletion(bool b) +{ + m_completionActive = b; +} + +void WorksheetTextItem::setItemDragable(bool b) +{ + m_itemDragable = b; +} + +void WorksheetTextItem::enableRichText(bool b) +{ + m_richTextEnabled = b; +} + +void WorksheetTextItem::setFocusAt(int pos, qreal xCoord) +{ + QTextCursor cursor = textCursor(); + if (pos == TopLeft) { + cursor.movePosition(QTextCursor::Start); + } else if (pos == BottomRight) { + cursor.movePosition(QTextCursor::End); + } else { + QTextLine line; + if (pos == TopCoord) { + line = document()->firstBlock().layout()->lineAt(0); + } else { + QTextLayout* layout = document()->lastBlock().layout(); + kDebug() << document()->blockCount() << "blocks"; + kDebug() << document()->lastBlock().lineCount() << "lines in last block"; + line = layout->lineAt(document()->lastBlock().lineCount()-1); + } + qreal x = mapFromScene(xCoord, 0).x(); + int p = line.xToCursor(x); + cursor.setPosition(p); + // Hack: The code for selecting the last line above does not work. + // This is a workaround + if (pos == BottomCoord) + while (cursor.movePosition(QTextCursor::Down)) + ; + } + setTextCursor(cursor); + emit cursorPositionChanged(cursor); + setFocus(); +} + +Cantor::Session* WorksheetTextItem::session() +{ + return worksheet()->session(); +} + +void WorksheetTextItem::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier) + { + if (!richTextEnabled()) + copy(); + else + QGraphicsTextItem::keyPressEvent(event); + return; + } + + if (!isEditable()) + return; + + switch (event->key()) { + case Qt::Key_Left: + if (event->modifiers() == Qt::NoModifier && textCursor().atStart()) { + emit moveToPrevious(BottomRight, 0); + kDebug()<<"Reached leftmost valid position"; + return; + } + break; + case Qt::Key_Right: + if (event->modifiers() == Qt::NoModifier && textCursor().atEnd()) { + emit moveToNext(TopLeft, 0); + kDebug()<<"Reached rightmost valid position"; + return; + } + break; + case Qt::Key_Up: + if (event->modifiers() == Qt::NoModifier && !textCursor().movePosition(QTextCursor::Up)) { + qreal x = mapToScene(localCursorPosition()).x(); + emit moveToPrevious(BottomCoord, x); + kDebug()<<"Reached topmost valid position" << localCursorPosition().x(); + return; + } + break; + case Qt::Key_Down: + if (event->modifiers() == Qt::NoModifier && !textCursor().movePosition(QTextCursor::Down)) { + qreal x = mapToScene(localCursorPosition()).x(); + emit moveToNext(TopCoord, x); + kDebug()<<"Reached bottommost valid position" << localCursorPosition().x(); + return; + } + break; + case Qt::Key_Enter: + case Qt::Key_Return: + if (event->modifiers() == Qt::ShiftModifier) { + emit execute(); + return; + } else if (event->modifiers() == Qt::NoModifier && m_completionActive) { + emit applyCompletion(); + return; + } + break; + case Qt::Key_Delete: + if (event->modifiers() == Qt::ShiftModifier) { + emit deleteEntry(); + return; + } + break; + /* Call our custom functions for cut and paste, unless richtext is + enabled */ + case Qt::Key_X: + if (event->modifiers() == Qt::ControlModifier && !richTextEnabled()) { + cut(); + return; + } + break; + case Qt::Key_V: + if (event->modifiers() == Qt::ControlModifier && !richTextEnabled()) { + paste(); + return; + } + break; + default: + break; + } + int p = textCursor().position(); + QGraphicsTextItem::keyPressEvent(event); + if (p != textCursor().position()) + emit cursorPositionChanged(textCursor()); +} + +bool WorksheetTextItem::sceneEvent(QEvent *event) +{ + // QGraphicsTextItem's TabChangesFocus feature prevents calls to + // keyPressEvent for Tab, even when it's turned off. So we got to catch + // that here. + if (event->type() == QEvent::KeyPress) { + QKeyEvent* kev = dynamic_cast(event); + if (kev->key() == Qt::Key_Tab && kev->modifiers() == Qt::NoModifier) { + QTextCursor cursor = textCursor(); + // maybe we can do something smart with selections here, + // but for now we just ignore them. + cursor.clearSelection(); + cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); + QString sel = cursor.selectedText(); + bool spacesOnly = true; + for (QString::iterator it = sel.begin(); it != sel.end(); ++it) { + if (*it != ' ') { + spacesOnly = false; + break; + } + } + + if (spacesOnly || !worksheet()->completionEnabled()) { + cursor.setPosition(cursor.selectionEnd()); + while (document()->characterAt(cursor.position()) == ' ') + cursor.movePosition(QTextCursor::NextCharacter); + setTextCursor(cursor); + insertTab(); + } else if (m_completionEnabled) { + emit tabPressed(); + } + return true; + } else if ((kev->key() == Qt::Key_Tab && + kev->modifiers() == Qt::ShiftModifier) || + kev->key() == Qt::Key_Backtab) { + emit backtabPressed(); + return true; + } + } + return QGraphicsTextItem::sceneEvent(event); +} + +void WorksheetTextItem::focusInEvent(QFocusEvent *event) +{ + QGraphicsTextItem::focusInEvent(event); + //parentItem()->ensureVisible(QRectF(), 0, 0); + WorksheetEntry* entry = qobject_cast(parentObject()); + WorksheetCursor c(entry, this, textCursor()); + worksheet()->makeVisible(c); + worksheet()->setAcceptRichText(richTextEnabled()); + worksheet()->updateFocusedTextItem(this); + emit receivedFocus(this); + emit cursorPositionChanged(textCursor()); +} + +void WorksheetTextItem::focusOutEvent(QFocusEvent *event) +{ + QGraphicsTextItem::focusOutEvent(event); + emit cursorPositionChanged(QTextCursor()); +} + +void WorksheetTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + int p = textCursor().position(); + + QGraphicsTextItem::mousePressEvent(event); + + if (isEditable() && event->button() == Qt::MiddleButton && + QApplication::clipboard()->supportsSelection() && + !event->isAccepted()) + event->accept(); + + if (m_itemDragable && event->button() == Qt::LeftButton) + event->accept(); + + if (p != textCursor().position()) + emit cursorPositionChanged(textCursor()); +} + +void WorksheetTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) +{ + const QPointF buttonDownPos = event->buttonDownPos(Qt::LeftButton); + if (m_itemDragable && event->buttons() == Qt::LeftButton && + contains(buttonDownPos) && + (event->pos() - buttonDownPos).manhattanLength() >= QApplication::startDragDistance()) { + ungrabMouse(); + emit drag(mapToParent(buttonDownPos), mapToParent(event->pos())); + event->accept(); + } else { + QGraphicsTextItem::mouseMoveEvent(event); + } +} + +void WorksheetTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + int p = textCursor().position(); + + // custom middle-click paste that does not copy rich text + if (isEditable() && event->button() == Qt::MiddleButton && + QApplication::clipboard()->supportsSelection() && + !richTextEnabled()) { + setLocalCursorPosition(mapFromScene(event->scenePos())); + const QString& text = QApplication::clipboard()->text(QClipboard::Selection); + textCursor().insertText(text); + } else { + QGraphicsTextItem::mouseReleaseEvent(event); + } + + if (p != textCursor().position()) + emit cursorPositionChanged(textCursor()); +} + +void WorksheetTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + QTextCursor cursor = textCursor(); + const QChar repl = QChar::ObjectReplacementCharacter; + + if (!cursor.hasSelection()) { + // We look at the current cursor and the next cursor for a + // ObjectReplacementCharacter + for (int i = 2; i; --i) { + if (document()->characterAt(cursor.position()-1) == repl) { + setTextCursor(cursor); + emit doubleClick(); + return; + } + cursor.movePosition(QTextCursor::NextCharacter); + } + } else if (cursor.selectedText().contains(repl)) { + emit doubleClick(); + return; + } + + QGraphicsTextItem::mouseDoubleClickEvent(event); +} + +void WorksheetTextItem::dragEnterEvent(QGraphicsSceneDragDropEvent* event) +{ + if (isEditable() && event->mimeData()->hasFormat("text/plain")) { + if (event->proposedAction() & (Qt::CopyAction | Qt::MoveAction)) { + event->acceptProposedAction(); + } else if (event->possibleActions() & Qt::CopyAction) { + event->setDropAction(Qt::CopyAction); + event->accept(); + } else if (event->possibleActions() & Qt::MoveAction) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->ignore(); + } + } else { + event->ignore(); + } +} + +void WorksheetTextItem::dragMoveEvent(QGraphicsSceneDragDropEvent* event) +{ + if (isEditable() && event->mimeData()->hasFormat("text/plain")) + setLocalCursorPosition(mapFromScene(event->scenePos())); +} + +void WorksheetTextItem::dropEvent(QGraphicsSceneDragDropEvent* event) +{ + if (isEditable()) { + if (richTextEnabled() && event->mimeData()->hasFormat("text/html")) + textCursor().insertHtml(event->mimeData()->html()); + else + textCursor().insertText(event->mimeData()->text()); + event->accept(); + } +} + +void WorksheetTextItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + KMenu *menu = worksheet()->createContextMenu(); + populateMenu(menu, event->pos()); + + menu->popup(event->screenPos()); +} + +void WorksheetTextItem::insertTab() +{ + QTextLayout *layout = textCursor().block().layout(); + QTextCursor cursor = textCursor(); + if (!layout) { + cursor.insertText(" "); + } else { + cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); + int i = cursor.selectionEnd() - cursor.selectionStart(); + i = ((i+4) & (~3)) - i; + cursor.setPosition(cursor.selectionEnd()); + cursor.insertText(QString(' ').repeated(i)); + } + // without this line subsequent cursor movement up or down uses the old + // position + setTextCursor(cursor); + emit cursorPositionChanged(textCursor()); +} + +double WorksheetTextItem::width() +{ + return m_size.width(); +} + +double WorksheetTextItem::height() +{ + return m_size.height(); +} + +Worksheet* WorksheetTextItem::worksheet() +{ + return qobject_cast(scene()); +} + +WorksheetView* WorksheetTextItem::worksheetView() +{ + return worksheet()->worksheetView(); +} + +void WorksheetTextItem::clearSelection() +{ + QTextCursor cursor = textCursor(); + cursor.clearSelection(); + setTextCursor(cursor); +} + + +QTextCursor WorksheetTextItem::search(QString pattern, + QTextDocument::FindFlags qt_flags, + const WorksheetCursor& pos) +{ + if (pos.isValid() && pos.textItem() != this) + return QTextCursor(); + + QTextDocument* doc = document(); + QTextCursor cursor; + if (pos.isValid()) { + cursor = doc->find(pattern, pos.textCursor(), qt_flags); + } else { + cursor = textCursor(); + if (qt_flags & QTextDocument::FindBackward) + cursor.movePosition(QTextCursor::End); + else + cursor.movePosition(QTextCursor::Start); + cursor = doc->find(pattern, cursor, qt_flags); + } + + return cursor; +} + +// RichText + +void WorksheetTextItem::updateRichTextActions(QTextCursor cursor) +{ + if (cursor.isNull()) + return; + Worksheet::RichTextInfo info; + QTextCharFormat fmt = cursor.charFormat(); + info.bold = (fmt.fontWeight() == QFont::Bold); + info.italic = fmt.fontItalic(); + info.underline = fmt.fontUnderline(); + info.strikeOut = fmt.fontStrikeOut(); + info.font = fmt.fontFamily(); + info.fontSize = fmt.font().pointSize(); + + QTextBlockFormat bfmt = cursor.blockFormat(); + info.align = bfmt.alignment(); + + worksheet()->setRichTextInformation(info); +} + +void WorksheetTextItem::mergeFormatOnWordOrSelection(const QTextCharFormat &format) +{ + kDebug() << format; + QTextCursor cursor = textCursor(); + QTextCursor wordStart(cursor); + QTextCursor wordEnd(cursor); + + wordStart.movePosition(QTextCursor::StartOfWord); + wordEnd.movePosition(QTextCursor::EndOfWord); + + //cursor.beginEditBlock(); + if (!cursor.hasSelection() && cursor.position() != wordStart.position() && cursor.position() != wordEnd.position()) + cursor.select(QTextCursor::WordUnderCursor); + cursor.mergeCharFormat(format); + //q->mergeCurrentCharFormat(format); + //cursor.endEditBlock(); + setTextCursor(cursor); +} + +void WorksheetTextItem::setTextForegroundColor() +{ + QTextCharFormat fmt = textCursor().charFormat(); + QColor color = fmt.foreground().color(); + + int result = KColorDialog::getColor(color, KColorScheme(QPalette::Active, KColorScheme::View).foreground().color(), worksheetView()); + if (!color.isValid()) + color = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color(); + if (result != QDialog::Accepted) + return; + + QTextCharFormat newFmt; + newFmt.setForeground(color); + mergeFormatOnWordOrSelection(newFmt); +} + +void WorksheetTextItem::setTextBackgroundColor() +{ + QTextCharFormat fmt = textCursor().charFormat(); + QColor color = fmt.background().color(); + + int result = KColorDialog::getColor(color, KColorScheme(QPalette::Active, KColorScheme::View).background().color(), worksheetView()); + if (!color.isValid()) + color = KColorScheme(QPalette::Active, KColorScheme::View).background().color() ; + if (result != QDialog::Accepted) + return; + + QTextCharFormat newFmt; + newFmt.setBackground(color); + mergeFormatOnWordOrSelection(newFmt); +} + +void WorksheetTextItem::setTextBold(bool b) +{ + QTextCharFormat fmt; + fmt.setFontWeight(b ? QFont::Bold : QFont::Normal); + mergeFormatOnWordOrSelection(fmt); +} + +void WorksheetTextItem::setTextItalic(bool b) +{ + QTextCharFormat fmt; + fmt.setFontItalic(b); + mergeFormatOnWordOrSelection(fmt); +} + +void WorksheetTextItem::setTextUnderline(bool b) +{ + QTextCharFormat fmt; + fmt.setFontUnderline(b); + mergeFormatOnWordOrSelection(fmt); +} + +void WorksheetTextItem::setTextStrikeOut(bool b) +{ + QTextCharFormat fmt; + fmt.setFontStrikeOut(b); + mergeFormatOnWordOrSelection(fmt); +} + +void WorksheetTextItem::setAlignment(Qt::Alignment a) +{ + QTextBlockFormat fmt; + fmt.setAlignment(a); + QTextCursor cursor = textCursor(); + cursor.mergeBlockFormat(fmt); + setTextCursor(cursor); +} + + +void WorksheetTextItem::setFontFamily(const QString& font) +{ + if (!richTextEnabled()) + return; + QTextCharFormat fmt; + fmt.setFontFamily(font); + mergeFormatOnWordOrSelection(fmt); +} + +void WorksheetTextItem::setFontSize(int size) +{ + if (!richTextEnabled()) + return; + QTextCharFormat fmt; + fmt.setFontPointSize(size); + mergeFormatOnWordOrSelection(fmt); +} + +#include "worksheettextitem.moc" diff --git a/src/worksheettextitem.h b/src/worksheettextitem.h new file mode 100644 index 00000000..88ea2760 --- /dev/null +++ b/src/worksheettextitem.h @@ -0,0 +1,156 @@ +/* + 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 WORKSHEET_TEXT_ITEM_H +#define WORKSHEET_TEXT_ITEM_H + +#include +#include +#include + +#include +#include + +class Worksheet; +class WorksheetView; +class WorksheetCursor; + +namespace Cantor { + class Session; +} + +class QTextCharFormat; + +class WorksheetTextItem : public QGraphicsTextItem +{ + Q_OBJECT + public: + WorksheetTextItem(QGraphicsObject* parent, + Qt::TextInteractionFlags ti = Qt::NoTextInteraction); + ~WorksheetTextItem(); + + void setCursorPosition(const QPointF& pos); + QPointF cursorPosition() const; + QTextCursor cursorForPosition(const QPointF& pos) const; + QRectF cursorRect(QTextCursor cursor = QTextCursor()) const; + + enum {TopLeft, BottomRight, TopCoord, BottomCoord}; + enum {Type = UserType + 100}; + + int type() const; + + void setFocusAt(int pos = TopLeft, qreal xCoord = 0); + + void enableCompletion(bool b); + void activateCompletion(bool b); + void setItemDragable(bool b); + void enableRichText(bool b); + + virtual void populateMenu(KMenu *menu, const QPointF& pos); + QString resolveImages(const QTextCursor& cursor); + + bool isEditable(); + bool richTextEnabled(); + double width(); + double height(); + virtual qreal setGeometry(qreal x, qreal y, qreal w, bool centered=false); + + Worksheet* worksheet(); + WorksheetView* worksheetView(); + + void clearSelection(); + + // richtext stuff + void setTextForegroundColor(); + void setTextBackgroundColor(); + void setTextBold(bool b); + void setTextItalic(bool b); + void setTextUnderline(bool b); + void setTextStrikeOut(bool b); + void setAlignment(Qt::Alignment a); + void setFontFamily(const QString& font); + void setFontSize(int size); + + QTextCursor search(QString pattern, + QTextDocument::FindFlags qt_flags, + const WorksheetCursor& pos); + + signals: + void moveToPrevious(int pos, qreal xCoord); + void moveToNext(int pos, qreal xCoord); + void cursorPositionChanged(QTextCursor); + void receivedFocus(WorksheetTextItem*); + void tabPressed(); + void backtabPressed(); + void applyCompletion(); + void doubleClick(); + void execute(); + void deleteEntry(); + void sizeChanged(); + void menuCreated(KMenu*, const QPointF&); + void drag(const QPointF&, const QPointF&); + + public slots: + void insertTab(); + void cut(); + void copy(); + void paste(); + + protected: + void keyPressEvent(QKeyEvent *event); + void focusInEvent(QFocusEvent *event); + void focusOutEvent(QFocusEvent *event); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent* event); + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); + void dragEnterEvent(QGraphicsSceneDragDropEvent* event); + //void dragLeaveEvent(QGraphicsSceneDragDropEvent* event); + void dragMoveEvent(QGraphicsSceneDragDropEvent* event); + void dropEvent(QGraphicsSceneDragDropEvent* event); + bool sceneEvent(QEvent *event); + + private slots: + //void setHeight(); + void testSize(); + void updateRichTextActions(QTextCursor cursor); + + private: + void setLocalCursorPosition(const QPointF& pos); + QPointF localCursorPosition() const; + + QKeyEvent* eventForStandardAction(KStandardAction::StandardAction actionID); + Cantor::Session* session(); + + // richtext + void mergeFormatOnWordOrSelection(const QTextCharFormat &format); + + private: + QSizeF m_size; + qreal m_maxWidth; + bool m_completionEnabled; + bool m_completionActive; + bool m_itemDragable; + bool m_richTextEnabled; +}; + +#endif // WORKSHEET_TEXT_ITEM_H diff --git a/src/worksheettoolbutton.cpp b/src/worksheettoolbutton.cpp new file mode 100644 index 00000000..33fd57f7 --- /dev/null +++ b/src/worksheettoolbutton.cpp @@ -0,0 +1,88 @@ +/* + 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 "worksheettoolbutton.h" +#include +#include +#include +#include + +WorksheetToolButton::WorksheetToolButton(QGraphicsItem* parent) + : QGraphicsObject(parent) +{ + m_size = QSize(16, 16); + setCursor(QCursor(Qt::ArrowCursor)); + m_scale = 0; +} + +WorksheetToolButton::~WorksheetToolButton() +{ +} + +void WorksheetToolButton::setIcon(const KIcon& icon) +{ + m_icon = icon; +} + +qreal WorksheetToolButton::width() +{ + return m_size.width(); +} + +qreal WorksheetToolButton::height() +{ + return m_size.height(); +} + +QRectF WorksheetToolButton::boundingRect() const +{ + return QRectF(0, 0, m_size.width(), m_size.height()); +} + +void WorksheetToolButton::setIconScale(qreal scale) +{ + m_scale = scale; + m_pixmap = m_icon.pixmap(m_size * m_scale); +} + +void WorksheetToolButton::paint(QPainter* painter, + const QStyleOptionGraphicsItem* option, + QWidget* widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + if (m_scale == 0) + setIconScale(1); + QRectF rect(QPointF(0,0), m_size); + painter->drawPixmap(rect, m_pixmap, m_pixmap.rect()); +} + +void WorksheetToolButton::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + Q_UNUSED(event); + + emit pressed(); +} + +void WorksheetToolButton::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) +{ + if (boundingRect().contains(event->pos())) + emit clicked(); +} diff --git a/src/worksheettoolbutton.h b/src/worksheettoolbutton.h new file mode 100644 index 00000000..d307eb23 --- /dev/null +++ b/src/worksheettoolbutton.h @@ -0,0 +1,60 @@ +/* + 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 WORKSHEETTOOLBUTTON_H +#define WORKSHEETTOOLBUTTON_H + +#include +#include + +#include + +class WorksheetToolButton : public QGraphicsObject +{ + Q_OBJECT + public: + WorksheetToolButton(QGraphicsItem* parent); + ~WorksheetToolButton(); + + void setIcon(const KIcon& icon); + + qreal width(); + qreal height(); + QRectF boundingRect() const; + void setIconScale(qreal scale); + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, + QWidget* widget = 0); + + signals: + void clicked(); + void pressed(); + + protected: + void mousePressEvent(QGraphicsSceneMouseEvent* event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event); + + private: + QSize m_size; + QPixmap m_pixmap; + KIcon m_icon; + qreal m_scale; +}; + +#endif //WORKSHEETTOOLBUTTON_H diff --git a/src/worksheetview.cpp b/src/worksheetview.cpp new file mode 100644 index 00000000..99318ceb --- /dev/null +++ b/src/worksheetview.cpp @@ -0,0 +1,245 @@ +/* + 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 "worksheetview.h" +#include "worksheet.h" + +#include +#include +#include +#include + +WorksheetView::WorksheetView(Worksheet* scene, QWidget* parent) + : QGraphicsView(scene, parent) +{ + m_scale = 1; + m_animation = 0; + m_hAnimation = 0; + m_vAnimation = 0; + m_worksheet = qobject_cast(scene); + setAlignment(Qt::AlignLeft | Qt::AlignTop); + //setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); +} + +WorksheetView::~WorksheetView() +{ +} + +void WorksheetView::makeVisible(const QRectF& rect) +{ + const qreal w = viewport()->width() / m_scale; + const qreal h = viewport()->height() / m_scale; + + qreal x,y; + if (m_animation) { + x = m_hAnimation->endValue().toReal(); + y = m_vAnimation->endValue().toReal(); + + if (QRectF(x,y,w,h).contains(rect)) + return; + } + + if (horizontalScrollBar()) + x = horizontalScrollBar()->value(); + else + x = 0; + if (verticalScrollBar()) + y = verticalScrollBar()->value(); + else + y = 0; + + kDebug() << rect << QRectF(x,y,w,h); + + if (!m_animation && QRectF(x,y,w,h).contains(rect)) + return; + + qreal nx, ny; + if (y > rect.y() || rect.height() > h) + ny = rect.y(); + else + ny = rect.y() + rect.height() - h; + if (rect.x() + rect.width() <= w || x > rect.x()) + nx = 0; + else + nx = rect.x() + rect.width() - w; + + kDebug() << nx << ny; + + if (!m_worksheet->animationsEnabled()) { + if (horizontalScrollBar()) + horizontalScrollBar()->setValue(nx); + if (verticalScrollBar()) + verticalScrollBar()->setValue(ny); + return; + } + + if (!m_animation) + m_animation = new QParallelAnimationGroup(this); + + if (horizontalScrollBar()) { + if (!m_hAnimation) { + m_hAnimation = new QPropertyAnimation(horizontalScrollBar(), + "value", this); + m_hAnimation->setStartValue(horizontalScrollBar()->value()); + nx = qBound(0.0, nx, 0.0+horizontalScrollBar()->maximum()); + m_hAnimation->setEndValue(nx); + m_hAnimation->setDuration(100); + m_animation->addAnimation(m_hAnimation); + } else { + qreal progress = static_cast(m_hAnimation->currentTime()) / + m_hAnimation->totalDuration(); + QEasingCurve curve = m_hAnimation->easingCurve(); + qreal value = curve.valueForProgress(progress); + qreal sx = 1/(1-value)*(m_hAnimation->currentValue().toReal() - + value * nx); + m_hAnimation->setStartValue(sx); + m_hAnimation->setEndValue(nx); + } + } else { + m_hAnimation = 0; + } + + if (verticalScrollBar()) { + if (!m_vAnimation) { + m_vAnimation = new QPropertyAnimation(verticalScrollBar(), + "value", this); + m_vAnimation->setStartValue(verticalScrollBar()->value()); + ny = qBound(0.0, ny, 0.0+verticalScrollBar()->maximum()); + m_vAnimation->setEndValue(ny); + m_vAnimation->setDuration(100); + m_animation->addAnimation(m_vAnimation); + } else { + qreal progress = static_cast(m_vAnimation->currentTime()) / + m_vAnimation->totalDuration(); + QEasingCurve curve = m_vAnimation->easingCurve(); + qreal value = curve.valueForProgress(progress); + qreal sy = 1/(1-value)*(m_vAnimation->currentValue().toReal() - + value * ny); + m_vAnimation->setStartValue(sy); + m_vAnimation->setEndValue(ny); + //kDebug() << sy << value << ny; + } + } else { + m_vAnimation = 0; + } + + connect(m_animation, SIGNAL(finished()), this, SLOT(endAnimation())); + m_animation->start(); +} + +bool WorksheetView::isVisible(const QRectF& rect) +{ + const qreal w = viewport()->width() / m_scale; + const qreal h = viewport()->height() / m_scale; + + qreal x,y; + if (m_animation) { + x = m_hAnimation->endValue().toReal(); + y = m_vAnimation->endValue().toReal(); + } else { + if (horizontalScrollBar()) + x = horizontalScrollBar()->value(); + else + x = 0; + if (verticalScrollBar()) + y = verticalScrollBar()->value(); + else + y = 0; + } + + return QRectF(x,y,w,h).contains(rect); +} + +bool WorksheetView::isAtEnd() +{ + bool atEnd = true; + if (verticalScrollBar()) + atEnd &= (verticalScrollBar()->value()==verticalScrollBar()->maximum()); + return atEnd; +} + +void WorksheetView::scrollToEnd() +{ + if (verticalScrollBar()) + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); +} + +void WorksheetView::endAnimation() +{ + if (!m_animation) + return; + + m_animation->deleteLater(); + m_hAnimation = 0; + m_vAnimation = 0; + m_animation = 0; +} + +QPointF WorksheetView::sceneCursorPos() +{ + const QPoint viewPos = viewport()->mapFromGlobal(QCursor::pos()); + return mapToScene(viewPos); +} + +void WorksheetView::resizeEvent(QResizeEvent * event) +{ + Q_UNUSED(event); + updateSceneSize(); +} + +qreal WorksheetView::scaleFactor() +{ + return m_scale; +} + +void WorksheetView::updateSceneSize() +{ + QSize s = viewport()->size(); + m_worksheet->setViewSize(s.width()/m_scale, s.height()/m_scale, m_scale); +} + +void WorksheetView::zoomIn() +{ + m_scale *= 1.1; + scale(1.1, 1.1); + updateSceneSize(); +} + +void WorksheetView::zoomOut() +{ + m_scale /= 1.1; + scale(1/1.1, 1/1.1); + updateSceneSize(); +} + +/* +#include "kdebug.h" + +void WorksheetView::mousePressEvent(QMouseEvent* event) +{ + QPointF pos = mapToScene(event->pos()); + kDebug() << "Click at" << pos; + if (QGraphicsItem* item = scene()->itemAt(pos)) + kDebug() << "item " << item; +} +*/ + +#include "worksheetview.moc" diff --git a/src/worksheetview.h b/src/worksheetview.h new file mode 100644 index 00000000..9bac5370 --- /dev/null +++ b/src/worksheetview.h @@ -0,0 +1,64 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) 2012 Martin Kuettler + */ + +#ifndef WORKSHEETVIEW_H +#define WORKSHEETVIEW_H + +#include + +class QParallelAnimationGroup; +class QPropertyAnimation; + +class Worksheet; + +class WorksheetView : public QGraphicsView +{ + Q_OBJECT + public: + WorksheetView(Worksheet* scene, QWidget* parent); + ~WorksheetView(); + + void makeVisible(const QRectF& rect); + bool isVisible(const QRectF& rect); + bool isAtEnd(); + void scrollToEnd(); + + QPointF sceneCursorPos(); + + void resizeEvent(QResizeEvent* event); + + qreal scaleFactor(); + + void updateSceneSize(); + + public slots: + void zoomIn(); + void zoomOut(); + void endAnimation(); + + private: + qreal m_scale; + QParallelAnimationGroup* m_animation; + QPropertyAnimation* m_hAnimation; + QPropertyAnimation* m_vAnimation; + Worksheet* m_worksheet; +}; + +#endif //WORKSHEETVIEW_H