diff options
Diffstat (limited to 'lib/kformula/formulacursor.cpp')
-rw-r--r-- | lib/kformula/formulacursor.cpp | 746 |
1 files changed, 746 insertions, 0 deletions
diff --git a/lib/kformula/formulacursor.cpp b/lib/kformula/formulacursor.cpp new file mode 100644 index 000000000..53d02b1f8 --- /dev/null +++ b/lib/kformula/formulacursor.cpp @@ -0,0 +1,746 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org> + Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <tqpainter.h> + +#include <kdebug.h> +#include <assert.h> + +#include "formulacursor.h" +#include "formulaelement.h" +#include "indexelement.h" +#include "matrixelement.h" +#include "rootelement.h" +#include "sequenceelement.h" +#include "symbolelement.h" +#include "textelement.h" + +KFORMULA_NAMESPACE_BEGIN + +FormulaCursor::FormulaCursor(FormulaElement* element) + : selectionFlag(false), linearMovement(false), + hasChangedFlag(true), readOnly(false) +{ + //setTo(element, 0); + element->goInside( this ); +} + +void FormulaCursor::setTo(BasicElement* element, uint cursor, int mark) +{ + hasChangedFlag = true; + current = element; + cursorPos = cursor; + if ((mark == -1) && selectionFlag) { + return; + } + if (mark != -1) { + setSelection(true); + } + markPos = mark; +} + + +void FormulaCursor::setPos(uint pos) +{ + hasChangedFlag = true; + cursorPos = pos; +} + +void FormulaCursor::setMark(int mark) +{ + hasChangedFlag = true; + markPos = mark; +} + +void FormulaCursor::calcCursorSize( const ContextStyle& context, bool smallCursor ) +{ + // We only draw the cursor if its normalized. + SequenceElement* sequence = dynamic_cast<SequenceElement*>(current); + + if (sequence != 0) { + sequence->calcCursorSize( context, this, smallCursor ); + } +} + +void FormulaCursor::draw( TQPainter& painter, const ContextStyle& context, + StyleAttributes& style, bool smallCursor, bool activeCursor ) +{ + //if (readOnly && !isSelection()) + //return; + + // We only draw the cursor if its normalized. + SequenceElement* sequence = dynamic_cast<SequenceElement*>(current); + + if (sequence != 0) { + sequence->drawCursor( painter, context, style, this, smallCursor, activeCursor ); + } +} + + +void FormulaCursor::handleSelectState(int flag) +{ + if (flag & SelectMovement) { + if (!isSelection()) { + setMark(getPos()); + setSelection(true); + } + } + else { + setSelection(false); + } +} + +void FormulaCursor::moveLeft(int flag) +{ + BasicElement* element = getElement(); + handleSelectState(flag); + if (flag & WordMovement) { + SequenceElement* sequence = dynamic_cast<SequenceElement*>(current); + if (sequence != 0) { + sequence->moveWordLeft(this); + } + else { + element->moveHome(this); + } + } + else { + element->moveLeft(this, element); + } +} + +void FormulaCursor::moveRight(int flag) +{ + BasicElement* element = getElement(); + handleSelectState(flag); + if (flag & WordMovement) { + SequenceElement* sequence = dynamic_cast<SequenceElement*>(current); + if (sequence != 0) { + sequence->moveWordRight(this); + } + else { + element->moveEnd(this); + } + } + else { + element->moveRight(this, element); + } +} + +void FormulaCursor::moveUp(int flag) +{ + BasicElement* element = getElement(); + handleSelectState(flag); + element->moveUp(this, element); +} + +void FormulaCursor::moveDown(int flag) +{ + BasicElement* element = getElement(); + handleSelectState(flag); + element->moveDown(this, element); +} + +void FormulaCursor::moveHome(int flag) +{ + BasicElement* element = getElement(); + handleSelectState(flag); + if (flag & WordMovement) { + element->formula()->moveHome(this); + } + else { + element->moveHome(this); + } +} + +void FormulaCursor::moveEnd(int flag) +{ + BasicElement* element = getElement(); + handleSelectState(flag); + if (flag & WordMovement) { + element->formula()->moveEnd(this); + } + else { + element->moveEnd(this); + } +} + +bool FormulaCursor::isHome() const +{ + return ( getElement() == getElement()->formula() ) && ( getPos() == 0 ); +} + +bool FormulaCursor::isEnd() const +{ + return ( getElement() == getElement()->formula() ) && + ( getPos() == normal()->countChildren() ); +} + +void FormulaCursor::mousePress( const LuPixelPoint& pos, int flag ) +{ + FormulaElement* formula = getElement()->formula(); + formula->goToPos( this, pos ); + if (flag & SelectMovement) { + setSelection(true); + if (getMark() == -1) { + setMark(getPos()); + } + } + else { + setSelection(false); + setMark(getPos()); + } +} + +void FormulaCursor::mouseMove( const LuPixelPoint& point, int ) +{ + setSelection(true); + BasicElement* element = getElement(); + int mark = getMark(); + + FormulaElement* formula = getElement()->formula(); + formula->goToPos( this, point ); + BasicElement* newElement = getElement(); + int pos = getPos(); + + BasicElement* posChild = 0; + BasicElement* markChild = 0; + while (element != newElement) { + posChild = newElement; + newElement = newElement->getParent(); + if (newElement == 0) { + posChild = 0; + newElement = getElement(); + markChild = element; + element = element->getParent(); + } + } + + if (dynamic_cast<SequenceElement*>(element) == 0) { + element = element->getParent(); + element->selectChild(this, newElement); + } + else { + if (posChild != 0) { + element->selectChild(this, posChild); + pos = getPos(); + } + if (markChild != 0) { + element->selectChild(this, markChild); + mark = getMark(); + } + if (pos == mark) { + if ((posChild == 0) && (markChild != 0)) { + mark++; + } + else if ((posChild != 0) && (markChild == 0)) { + mark--; + } + } + else if (pos < mark) { + if (posChild != 0) { + pos--; + } + } + setTo(element, pos, mark); + } +} + +void FormulaCursor::mouseRelease( const LuPixelPoint&, int ) +{ + //mouseSelectionFlag = false; +} + + +/** + * Moves the cursor inside the element. Selection is turned off. + */ +void FormulaCursor::goInsideElement(BasicElement* element) +{ + element->goInside(this); +} + + +/** + * Moves the cursor to a normal position. That is somewhere + * inside a SequenceElement. + * You need to call this after each removal because the cursor + * might point to some non existing place. + */ +void FormulaCursor::normalize(Direction direction) +{ + BasicElement* element = getElement(); + element->normalize(this, direction); +} + + +/** + * Inserts the child at the current position. + * Ignores the selection. + */ +void FormulaCursor::insert(BasicElement* child, Direction direction) +{ + TQPtrList<BasicElement> list; + list.append(child); + insert(list, direction); +} + +void FormulaCursor::insert(TQPtrList<BasicElement>& children, + Direction direction) +{ + assert( !isReadOnly() ); + BasicElement* element = getElement(); + element->insert(this, children, direction); +} + + +/** + * Removes the current selected children and returns them. + * The cursor needs to be normal (that is be inside a SequenceElement) + * for this to have any effect. + */ +void FormulaCursor::remove(TQPtrList<BasicElement>& children, + Direction direction) +{ + assert( !isReadOnly() ); + SequenceElement* sequence = normal(); + if (sequence != 0) { + + // If there is no child to remove in the sequence + // remove the sequence instead. + if (sequence->countChildren() == 0) { + BasicElement* parent = sequence->getParent(); + if (parent != 0) { + parent->selectChild(this, sequence); + parent->remove(this, children, direction); + return; + } + } + else { + sequence->remove(this, children, direction); + } + } +} + + +/** + * Replaces the current selection with the supplied element. + * The replaced elements become the new element's main child's content. + */ +void FormulaCursor::replaceSelectionWith(BasicElement* element, + Direction direction) +{ + assert( !isReadOnly() ); + TQPtrList<BasicElement> list; + // we suppres deletion here to get an error if something + // was left in the list. + //list.setAutoDelete(true); + + //remove(list, direction); + if (isSelection()) { + getElement()->remove(this, list, direction); + } + + insert(element, direction); + SequenceElement* mainChild = element->getMainChild(); + if (mainChild != 0) { + mainChild->goInside(this); + insert(list); + /* + BasicElement* parent = element->getParent(); + if (direction == beforeCursor) { + parent->moveRight(this, element); + } + else { + parent->moveLeft(this, element); + } + */ + element->selectChild(this, mainChild); + } +} + + +/** + * Replaces the element the cursor points to with its main child's + * content. + */ +BasicElement* FormulaCursor::replaceByMainChildContent(Direction direction) +{ + assert( !isReadOnly() ); + TQPtrList<BasicElement> childrenList; + TQPtrList<BasicElement> list; + BasicElement* element = getElement(); + SequenceElement* mainChild = element->getMainChild(); + if ((mainChild != 0) && (mainChild->countChildren() > 0)) { + mainChild->selectAllChildren(this); + remove(childrenList); + } + element->getParent()->selectChild(this, element); + setSelection(false); + remove(list); + insert(childrenList, direction); + if (list.count() > 0) { + return list.take(0); + } + return 0; +} + + +/** + * Trys to find the element we are the main child of and replace + * it with our content. + * + * This is simply another form of replaceByMainChildContent. You + * use this one if the cursor is normalized and inside the main child. + */ +BasicElement* FormulaCursor::removeEnclosingElement(Direction direction) +{ + assert( !isReadOnly() ); + BasicElement* parent = getElement()->getParent(); + if (parent != 0) { + if (getElement() == parent->getMainChild()) { + parent->selectChild(this, getElement()); + return replaceByMainChildContent(direction); + } + } + return 0; +} + + +/** + * Returns wether the element the cursor points to should be replaced. + * Elements are senseless as soon as they only contain a main child. + */ +bool FormulaCursor::elementIsSenseless() +{ + BasicElement* element = getElement(); + return element->isSenseless(); +} + + +/** + * Returns the child the cursor points to. Depending on the + * direction this might be the child before or after the + * cursor. + * + * Might be 0 is there is no such child. + */ +BasicElement* FormulaCursor::getActiveChild(Direction direction) +{ + return getElement()->getChild(this, direction); +} + +BasicElement* FormulaCursor::getSelectedChild() +{ + if (isSelection()) { + if ((getSelectionEnd() - getSelectionStart()) > 1) { + return 0; + } + return getActiveChild((getPos() > getMark()) ? + beforeCursor : + afterCursor); + } + else { + return getActiveChild(beforeCursor); + } +} + + +void FormulaCursor::selectActiveElement() +{ + if ( !isSelection() && getPos() > 0 ) { + setSelection( true ); + setMark( getPos() - 1 ); + } +} + + +/** + * Tells whether we currently point to the given elements + * main child and to the place behind its last child. + */ +bool FormulaCursor::pointsAfterMainChild(BasicElement* element) +{ + if (element != 0) { + SequenceElement* mainChild = element->getMainChild(); + return (getElement() == mainChild) && + ((mainChild->countChildren() == getPos()) || (0 == getPos())); + } + return false; +} + + +/** + * Returns the IndexElement the cursor is on or 0 + * if there is non. + */ +IndexElement* FormulaCursor::getActiveIndexElement() +{ + IndexElement* element = dynamic_cast<IndexElement*>(getSelectedChild()); + + if ((element == 0) && !isSelection()) { + element = dynamic_cast<IndexElement*>(getElement()->getParent()); + if (!pointsAfterMainChild(element)) { + return 0; + } + } + return element; +} + + +/** + * Returns the RootElement the cursor is on or 0 + * if there is non. + */ +RootElement* FormulaCursor::getActiveRootElement() +{ + RootElement* element = dynamic_cast<RootElement*>(getSelectedChild()); + + if ((element == 0) && !isSelection()) { + element = dynamic_cast<RootElement*>(getElement()->getParent()); + if (!pointsAfterMainChild(element)) { + return 0; + } + } + return element; +} + + +/** + * @returns the SymbolElement the cursor is on or 0 + * if there is non. + */ +SymbolElement* FormulaCursor::getActiveSymbolElement() +{ + SymbolElement* element = dynamic_cast<SymbolElement*>(getSelectedChild()); + + if ((element == 0) && !isSelection()) { + element = dynamic_cast<SymbolElement*>(getElement()->getParent()); + if (!pointsAfterMainChild(element)) { + return 0; + } + } + return element; +} + +/** + * @returns the NameSequence the cursor is on or 0 + * if there is non. + */ +NameSequence* FormulaCursor::getActiveNameSequence() +{ + NameSequence* element = dynamic_cast<NameSequence*>( getSelectedChild() ); + + if ( ( element == 0 ) && !isSelection() ) { + element = dynamic_cast<NameSequence*>( getElement() ); + if ( !pointsAfterMainChild( element ) ) { + return 0; + } + } + return element; +} + +/** + * @returns the TextElement the cursor is on or 0. + */ +TextElement* FormulaCursor::getActiveTextElement() +{ + return dynamic_cast<TextElement*>(getSelectedChild()); +} + + +MatrixElement* FormulaCursor::getActiveMatrixElement() +{ + MatrixElement* element = dynamic_cast<MatrixElement*>(getSelectedChild()); + + if ( ( element != 0 ) && !isSelection() ) { + normal()->selectChild( this, element ); + } +// if ((element == 0) && !isSelection()) { +// element = dynamic_cast<MatrixElement*>(getElement()->getParent()); +// if (!pointsAfterMainChild(element)) { +// return 0; +// } +// } + return element; +} + +/** + * The element is going to leave the formula with and all its children. + */ +void FormulaCursor::elementWillVanish(BasicElement* element) +{ + BasicElement* child = getElement(); + if (element && child == element->getParent()) { + child->childWillVanish(this, element); + return; + } + while (child != 0) { + if (child == element) { + // This is meant to catch all cursors that did not + // cause the deletion. + child->getParent()->moveLeft(this, child); + setSelection(false); + hasChangedFlag = true; + return; + } + child = child->getParent(); + } +} + + +/** + * A new formula has been loaded. Our current element has to change. + */ +void FormulaCursor::formulaLoaded(FormulaElement* rootElement) +{ + //current = rootElement; + //setPos(0); + rootElement->goInside( this ); + setMark(-1); + setSelection(false); +} + + +bool FormulaCursor::isReadOnly() const +{ + if ( readOnly ) { + return true; + } + const SequenceElement* sequence = normal(); + if ( sequence != 0 ) { + bool ro = sequence->readOnly( this ); + //kdDebug() << k_funcinfo << "readOnly=" << ro << endl; + return ro; + } + return false; +} + + +/** + * Stores the currently selected elements inside a dom. + */ +void FormulaCursor::copy( TQDomDocument& doc ) +{ + if (isSelection()) { + SequenceElement* sequence = normal(); + if (sequence != 0) { + TQDomElement root = doc.createElementNS( "http://www.w3.org/1998/Math/MathML", + "math" ); + doc.appendChild( root ); + TQDomElement de = doc.createElement( "mrow" ); + root.appendChild( de ); + sequence->getChildrenMathMLDom(doc, de, getSelectionStart(), getSelectionEnd()); + } + else { + // This must never happen. + tqFatal("A not normalized cursor is selecting."); + } + } +} + +/** + * Inserts the elements that could be read from the dom into + * the list. Returns true on success. + */ +bool FormulaCursor::buildElementsFromDom( TQDomElement root, TQPtrList<BasicElement>& list ) +{ + assert( !isReadOnly() ); + SequenceElement* sequence = normal(); + if (sequence != 0) { + TQDomElement e = root.firstChild().toElement(); + if (sequence->buildChildrenFromDom(list, e.firstChild())) { + return true; + } + } + return false; +} + +/** + * Inserts the elements that could be read from the MathML dom into + * the list. Returns true on success. + */ +bool FormulaCursor::buildElementsFromMathMLDom( TQDomElement root, TQPtrList<BasicElement>& list ) +{ + assert( !isReadOnly() ); + SequenceElement* sequence = normal(); + if (sequence != 0) { + TQDomElement e = root.firstChild().toElement(); + if (sequence->buildChildrenFromMathMLDom(list, e.firstChild())) { + return true; + } + } + return false; +} + +/** + * Creates a new CursorData object that describes the cursor. + * It's up to the caller to delete this object. + */ +FormulaCursor::CursorData* FormulaCursor::getCursorData() +{ + return new CursorData(current, cursorPos, markPos, + selectionFlag, linearMovement, readOnly); +} + + +// Keep in sync with 'setCursorData' +FormulaCursor& FormulaCursor::operator= (const FormulaCursor& other) +{ + current = other.current; + cursorPos = other.cursorPos; + markPos = other.markPos; + selectionFlag = other.selectionFlag; + linearMovement = other.linearMovement; + readOnly = other.readOnly; + hasChangedFlag = true; + return *this; +} + + +/** + * Sets the cursor to where the CursorData points to. No checking is done + * so you better make sure the point exists. + */ +void FormulaCursor::setCursorData(FormulaCursor::CursorData* data) +{ + current = data->current; + cursorPos = data->cursorPos; + markPos = data->markPos; + selectionFlag = data->selectionFlag; + linearMovement = data->linearMovement; + readOnly = data->readOnly; + hasChangedFlag = true; +} + + +/** + * Returns the sequence the cursor is in if we are normal. If not returns 0. + */ +SequenceElement* FormulaCursor::normal() +{ + return dynamic_cast<SequenceElement*>(current); +} + +const SequenceElement* FormulaCursor::normal() const +{ + return dynamic_cast<SequenceElement*>(current); +} + +KFORMULA_NAMESPACE_END |