diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /lib/kformula/matrixelement.cc | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'lib/kformula/matrixelement.cc')
-rw-r--r-- | lib/kformula/matrixelement.cc | 2692 |
1 files changed, 2692 insertions, 0 deletions
diff --git a/lib/kformula/matrixelement.cc b/lib/kformula/matrixelement.cc new file mode 100644 index 00000000..88a59081 --- /dev/null +++ b/lib/kformula/matrixelement.cc @@ -0,0 +1,2692 @@ +/* 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 <qmemarray.h> +#include <qpainter.h> +#include <qptrlist.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "MatrixDialog.h" +#include "elementvisitor.h" +#include "formulaelement.h" +#include "formulacursor.h" +#include "kformulacontainer.h" +#include "kformulacommand.h" +#include "matrixelement.h" +#include "sequenceelement.h" +#include "spaceelement.h" + + +KFORMULA_NAMESPACE_BEGIN + + +class MatrixSequenceElement : public SequenceElement { + typedef SequenceElement inherited; +public: + + MatrixSequenceElement( BasicElement* parent = 0 ) : SequenceElement( parent ) {} + virtual MatrixSequenceElement* clone() { + return new MatrixSequenceElement( *this ); + } + + /** + * This is called by the container to get a command depending on + * the current cursor position (this is how the element gets chosen) + * and the request. + * + * @returns the command that performs the requested action with + * the containers active cursor. + */ + virtual KCommand* buildCommand( Container*, Request* ); +}; + + +class KFCRemoveRow : public Command { +public: + KFCRemoveRow( const QString& name, Container* document, MatrixElement* m, uint r, uint c ); + ~KFCRemoveRow(); + + virtual void execute(); + virtual void unexecute(); + +protected: + MatrixElement* matrix; + uint rowPos; + uint colPos; + + QPtrList<MatrixSequenceElement>* row; +}; + + +class KFCInsertRow : public KFCRemoveRow { +public: + KFCInsertRow( const QString& name, Container* document, MatrixElement* m, uint r, uint c ); + + virtual void execute() { KFCRemoveRow::unexecute(); } + virtual void unexecute() { KFCRemoveRow::execute(); } +}; + + +class KFCRemoveColumn : public Command { +public: + KFCRemoveColumn( const QString& name, Container* document, MatrixElement* m, uint r, uint c ); + ~KFCRemoveColumn(); + + virtual void execute(); + virtual void unexecute(); + +protected: + MatrixElement* matrix; + uint rowPos; + uint colPos; + + QPtrList<MatrixSequenceElement>* column; +}; + + +class KFCInsertColumn : public KFCRemoveColumn { +public: + KFCInsertColumn( const QString& name, Container* document, MatrixElement* m, uint r, uint c ); + + virtual void execute() { KFCRemoveColumn::unexecute(); } + virtual void unexecute() { KFCRemoveColumn::execute(); } +}; + + +KCommand* MatrixSequenceElement::buildCommand( Container* container, Request* request ) +{ + FormulaCursor* cursor = container->activeCursor(); + if ( cursor->isReadOnly() ) { + return 0; + } + + switch ( *request ) { + case req_appendColumn: + case req_appendRow: + case req_insertColumn: + case req_removeColumn: + case req_insertRow: + case req_removeRow: { + MatrixElement* matrix = static_cast<MatrixElement*>( getParent() ); + FormulaCursor* cursor = container->activeCursor(); + for ( uint row = 0; row < matrix->getRows(); row++ ) { + for ( uint col = 0; col < matrix->getColumns(); col++ ) { + if ( matrix->getElement( row, col ) == cursor->getElement() ) { + switch ( *request ) { + case req_appendColumn: + return new KFCInsertColumn( i18n( "Append Column" ), container, matrix, row, matrix->getColumns() ); + case req_appendRow: + return new KFCInsertRow( i18n( "Append Row" ), container, matrix, matrix->getRows(), col ); + case req_insertColumn: + return new KFCInsertColumn( i18n( "Insert Column" ), container, matrix, row, col ); + case req_removeColumn: + if ( matrix->getColumns() > 1 ) { + return new KFCRemoveColumn( i18n( "Remove Column" ), container, matrix, row, col ); + } + break; + case req_insertRow: + return new KFCInsertRow( i18n( "Insert Row" ), container, matrix, row, col ); + case req_removeRow: + if ( matrix->getRows() > 1 ) { + return new KFCRemoveRow( i18n( "Remove Row" ), container, matrix, row, col ); + } + break; + default: + break; + } + } + } + } + kdWarning( DEBUGID ) << "MatrixSequenceElement::buildCommand: Sequence not found." << endl; + break; + } + default: + break; + } + return inherited::buildCommand( container, request ); +} + + +KFCRemoveRow::KFCRemoveRow( const QString& name, Container* document, MatrixElement* m, uint r, uint c ) + : Command( name, document ), matrix( m ), rowPos( r ), colPos( c ), row( 0 ) +{ +} + +KFCRemoveRow::~KFCRemoveRow() +{ + delete row; +} + +void KFCRemoveRow::execute() +{ + FormulaCursor* cursor = getExecuteCursor(); + row = matrix->content.at( rowPos ); + FormulaElement* formula = matrix->formula(); + for ( uint i = matrix->getColumns(); i > 0; i-- ) { + formula->elementRemoval( row->at( i-1 ) ); + } + matrix->content.take( rowPos ); + formula->changed(); + if ( rowPos < matrix->getRows() ) { + matrix->getElement( rowPos, colPos )->goInside( cursor ); + } + else { + matrix->getElement( rowPos-1, colPos )->goInside( cursor ); + } + testDirty(); +} + +void KFCRemoveRow::unexecute() +{ + matrix->content.insert( rowPos, row ); + row = 0; + FormulaCursor* cursor = getExecuteCursor(); + matrix->getElement( rowPos, colPos )->goInside( cursor ); + matrix->formula()->changed(); + testDirty(); +} + + +KFCInsertRow::KFCInsertRow( const QString& name, Container* document, MatrixElement* m, uint r, uint c ) + : KFCRemoveRow( name, document, m, r, c ) +{ + row = new QPtrList< MatrixSequenceElement >; + row->setAutoDelete( true ); + for ( uint i = 0; i < matrix->getColumns(); i++ ) { + row->append( new MatrixSequenceElement( matrix ) ); + } +} + + +KFCRemoveColumn::KFCRemoveColumn( const QString& name, Container* document, MatrixElement* m, uint r, uint c ) + : Command( name, document ), matrix( m ), rowPos( r ), colPos( c ) +{ + column = new QPtrList< MatrixSequenceElement >; + column->setAutoDelete( true ); +} + +KFCRemoveColumn::~KFCRemoveColumn() +{ + delete column; +} + +void KFCRemoveColumn::execute() +{ + FormulaCursor* cursor = getExecuteCursor(); + FormulaElement* formula = matrix->formula(); + for ( uint i = 0; i < matrix->getRows(); i++ ) { + column->append( matrix->getElement( i, colPos ) ); + formula->elementRemoval( column->at( i ) ); + matrix->content.at( i )->take( colPos ); + } + formula->changed(); + if ( colPos < matrix->getColumns() ) { + matrix->getElement( rowPos, colPos )->goInside( cursor ); + } + else { + matrix->getElement( rowPos, colPos-1 )->goInside( cursor ); + } + testDirty(); +} + +void KFCRemoveColumn::unexecute() +{ + for ( uint i = 0; i < matrix->getRows(); i++ ) { + matrix->content.at( i )->insert( colPos, column->take( 0 ) ); + } + FormulaCursor* cursor = getExecuteCursor(); + matrix->getElement( rowPos, colPos )->goInside( cursor ); + matrix->formula()->changed(); + testDirty(); +} + + +KFCInsertColumn::KFCInsertColumn( const QString& name, Container* document, MatrixElement* m, uint r, uint c ) + : KFCRemoveColumn( name, document, m, r, c ) +{ + for ( uint i = 0; i < matrix->getRows(); i++ ) { + column->append( new MatrixSequenceElement( matrix ) ); + } +} + + +MatrixElement::MatrixElement(uint rows, uint columns, BasicElement* parent) + : BasicElement(parent), + m_rowNumber( 0 ), + m_align( NoAlign ), + m_widthType( NoSize ), + m_frame( NoLine ), + m_frameHSpacing( NoSize ), + m_frameVSpacing( NoSize ), + m_side( NoSide ), + m_minLabelSpacingType( NoSize ), + m_customEqualRows( false ), + m_customEqualColumns( false ), + m_customDisplayStyle( false ) +{ + for (uint r = 0; r < rows; r++) { + QPtrList< MatrixSequenceElement >* list = new QPtrList< MatrixSequenceElement >; + list->setAutoDelete(true); + for (uint c = 0; c < columns; c++) { + list->append(new MatrixSequenceElement(this)); + } + content.append(list); + } + content.setAutoDelete(true); +} + +MatrixElement::~MatrixElement() +{ +} + + +MatrixElement::MatrixElement( const MatrixElement& other ) + : BasicElement( other ) +{ + uint rows = other.getRows(); + uint columns = other.getColumns(); + + QPtrListIterator< QPtrList< MatrixSequenceElement > > rowIter( other.content ); + for (uint r = 0; r < rows; r++) { + ++rowIter; + QPtrListIterator< MatrixSequenceElement > colIter( *rowIter.current() ); + + QPtrList< MatrixSequenceElement >* list = new QPtrList< MatrixSequenceElement >; + list->setAutoDelete(true); + for (uint c = 0; c < columns; c++) { + ++colIter; + MatrixSequenceElement *mse = + //new MatrixSequenceElement( *( other.getElement( r, c ) ) ); + new MatrixSequenceElement( *colIter.current() ); + list->append( mse ); + mse->setParent( this ); + } + content.append(list); + } + content.setAutoDelete(true); +} + + +bool MatrixElement::accept( ElementVisitor* visitor ) +{ + return visitor->visit( this ); +} + + +void MatrixElement::entered( SequenceElement* /*child*/ ) +{ + formula()->tell( i18n( "Matrix element" ) ); +} + + +BasicElement* MatrixElement::goToPos( FormulaCursor* cursor, bool& handled, + const LuPixelPoint& point, const LuPixelPoint& parentOrigin ) +{ + BasicElement* e = BasicElement::goToPos(cursor, handled, point, parentOrigin); + if (e != 0) { + LuPixelPoint myPos(parentOrigin.x() + getX(), + parentOrigin.y() + getY()); + + uint rows = getRows(); + uint columns = getColumns(); + + for (uint r = 0; r < rows; r++) { + for (uint c = 0; c < columns; c++) { + BasicElement* element = getElement(r, c); + e = element->goToPos(cursor, handled, point, myPos); + if (e != 0) { + return e; + } + } + } + + // We are in one of those gaps. + luPixel dx = point.x() - myPos.x(); + luPixel dy = point.y() - myPos.y(); + + uint row = rows; + for (uint r = 0; r < rows; r++) { + BasicElement* element = getElement(r, 0); + if (element->getY() > dy) { + row = r; + break; + } + } + if (row == 0) { + BasicElement* element = getParent(); + element->moveLeft(cursor, this); + handled = true; + return element; + } + row--; + + uint column = columns; + for (uint c = 0; c < columns; c++) { + BasicElement* element = getElement(row, c); + if (element->getX() > dx) { + column = c; + break; + } + } + if (column == 0) { + BasicElement* element = getParent(); + element->moveLeft(cursor, this); + handled = true; + return element; + } + column--; + + // Rescan the rows with the actual colums required. + row = rows; + for (uint r = 0; r < rows; r++) { + BasicElement* element = getElement(r, column); + if (element->getY() > dy) { + row = r; + break; + } + } + if (row == 0) { + BasicElement* element = getParent(); + element->moveLeft(cursor, this); + handled = true; + return element; + } + row--; + + BasicElement* element = getElement(row, column); + element->moveLeft(cursor, this); + handled = true; + return element; + } + return 0; +} + + +// drawing +// +// Drawing depends on a context which knows the required properties like +// fonts, spaces and such. +// It is essential to calculate elements size with the same context +// before you draw. + +/** + * Calculates our width and height and + * our children's parentPosition. + */ +void MatrixElement::calcSizes( const ContextStyle& context, + ContextStyle::TextStyle tstyle, + ContextStyle::IndexStyle istyle, + StyleAttributes& style ) +{ + QMemArray<luPixel> toMidlines(getRows()); + QMemArray<luPixel> fromMidlines(getRows()); + QMemArray<luPixel> widths(getColumns()); + + toMidlines.fill(0); + fromMidlines.fill(0); + widths.fill(0); + + uint rows = getRows(); + uint columns = getColumns(); + + ContextStyle::TextStyle i_tstyle = context.convertTextStyleFraction(tstyle); + ContextStyle::IndexStyle i_istyle = context.convertIndexStyleUpper(istyle); + double factor = style.sizeFactor(); + + for (uint r = 0; r < rows; r++) { + QPtrList< MatrixSequenceElement >* list = content.at(r); + for (uint c = 0; c < columns; c++) { + SequenceElement* element = list->at(c); + element->calcSizes( context, i_tstyle, i_istyle, style ); + toMidlines[r] = QMAX(toMidlines[r], element->axis( context, i_tstyle, factor )); + fromMidlines[r] = QMAX(fromMidlines[r], + element->getHeight()-element->axis( context, i_tstyle, factor )); + widths[c] = QMAX(widths[c], element->getWidth()); + } + } + + luPixel distX = context.ptToPixelX( context.getThinSpace( tstyle, factor ) ); + luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) ); + + luPixel yPos = 0; + for (uint r = 0; r < rows; r++) { + QPtrList< MatrixSequenceElement >* list = content.at(r); + luPixel xPos = 0; + yPos += toMidlines[r]; + for (uint c = 0; c < columns; c++) { + SequenceElement* element = list->at(c); + switch (context.getMatrixAlignment()) { + case ContextStyle::left: + element->setX(xPos); + break; + case ContextStyle::center: + element->setX(xPos + (widths[c] - element->getWidth())/2); + break; + case ContextStyle::right: + element->setX(xPos + widths[c] - element->getWidth()); + break; + } + element->setY(yPos - element->axis( context, i_tstyle, factor )); + xPos += widths[c] + distX; + } + yPos += fromMidlines[r] + distY; + } + + luPixel width = distX * (columns - 1); + luPixel height = distY * (rows - 1); + + for (uint r = 0; r < rows; r++) height += toMidlines[r] + fromMidlines[r]; + for (uint c = 0; c < columns; c++) width += widths[c]; + + setWidth(width); + setHeight(height); + if ((rows == 2) && (columns == 1)) { + setBaseline( getMainChild()->getHeight() + distY / 2 + context.axisHeight( tstyle, factor ) ); + } + else { + setBaseline( height/2 + context.axisHeight( tstyle, factor ) ); + } +} + +/** + * Draws the whole element including its children. + * The `parentOrigin' is the point this element's parent starts. + * We can use our parentPosition to get our own origin then. + */ +void MatrixElement::draw( QPainter& painter, const LuPixelRect& rect, + const ContextStyle& context, + ContextStyle::TextStyle tstyle, + ContextStyle::IndexStyle istyle, + StyleAttributes& style, + const LuPixelPoint& parentOrigin ) +{ + LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() ); + //if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( rect ) ) + // return; + + uint rows = getRows(); + uint columns = getColumns(); + + for (uint r = 0; r < rows; r++) { + for (uint c = 0; c < columns; c++) { + getElement(r, c)->draw(painter, rect, context, + context.convertTextStyleFraction(tstyle), + context.convertIndexStyleUpper(istyle), + style, + myPos); + } + } + + // Debug + //painter.setPen(Qt::red); + //painter.drawRect(myPos.x(), myPos.y(), getWidth(), getHeight()); +} + + +void MatrixElement::dispatchFontCommand( FontCommand* cmd ) +{ + uint rows = getRows(); + uint columns = getColumns(); + + for (uint r = 0; r < rows; r++) { + for (uint c = 0; c < columns; c++) { + getElement(r, c)->dispatchFontCommand( cmd ); + } + } +} + + +// navigation +// +// The elements are responsible to handle cursor movement themselves. +// To do this they need to know the direction the cursor moves and +// the element it comes from. +// +// The cursor might be in normal or in selection mode. + +/** + * Enters this element while moving to the left starting inside + * the element `from'. Searches for a cursor position inside + * this element or to the left of it. + */ +void MatrixElement::moveLeft(FormulaCursor* cursor, BasicElement* from) +{ + if (cursor->isSelectionMode()) { + getParent()->moveLeft(cursor, this); + } + else { + if (from == getParent()) { + getElement(getRows()-1, getColumns()-1)->moveLeft(cursor, this); + } + else { + bool linear = cursor->getLinearMovement(); + uint row = 0; + uint column = 0; + if (searchElement(from, row, column)) { + if (column > 0) { + getElement(row, column-1)->moveLeft(cursor, this); + } + else if (linear && (row > 0)) { + getElement(row-1, getColumns()-1)->moveLeft(cursor, this); + } + else { + getParent()->moveLeft(cursor, this); + } + } + else { + getParent()->moveLeft(cursor, this); + } + } + } +} + +/** + * Enters this element while moving to the right starting inside + * the element `from'. Searches for a cursor position inside + * this element or to the right of it. + */ +void MatrixElement::moveRight(FormulaCursor* cursor, BasicElement* from) +{ + if (cursor->isSelectionMode()) { + getParent()->moveRight(cursor, this); + } + else { + if (from == getParent()) { + getElement(0, 0)->moveRight(cursor, this); + } + else { + bool linear = cursor->getLinearMovement(); + uint row = 0; + uint column = 0; + if (searchElement(from, row, column)) { + if (column < getColumns()-1) { + getElement(row, column+1)->moveRight(cursor, this); + } + else if (linear && (row < getRows()-1)) { + getElement(row+1, 0)->moveRight(cursor, this); + } + else { + getParent()->moveRight(cursor, this); + } + } + else { + getParent()->moveRight(cursor, this); + } + } + } +} + +/** + * Enters this element while moving up starting inside + * the element `from'. Searches for a cursor position inside + * this element or above it. + */ +void MatrixElement::moveUp(FormulaCursor* cursor, BasicElement* from) +{ + if (cursor->isSelectionMode()) { + getParent()->moveUp(cursor, this); + } + else { + if (from == getParent()) { + getElement(0, 0)->moveRight(cursor, this); + } + else { + uint row = 0; + uint column = 0; + if (searchElement(from, row, column)) { + if (row > 0) { + getElement(row-1, column)->moveRight(cursor, this); + } + else { + getParent()->moveUp(cursor, this); + } + } + else { + getParent()->moveUp(cursor, this); + } + } + } +} + +/** + * Enters this element while moving down starting inside + * the element `from'. Searches for a cursor position inside + * this element or below it. + */ +void MatrixElement::moveDown(FormulaCursor* cursor, BasicElement* from) +{ + if (cursor->isSelectionMode()) { + getParent()->moveDown(cursor, this); + } + else { + if (from == getParent()) { + getElement(0, 0)->moveRight(cursor, this); + } + else { + uint row = 0; + uint column = 0; + if (searchElement(from, row, column)) { + if (row < getRows()-1) { + getElement(row+1, column)->moveRight(cursor, this); + } + else { + getParent()->moveDown(cursor, this); + } + } + else { + getParent()->moveDown(cursor, this); + } + } + } +} + +/** + * Sets the cursor inside this element to its start position. + * For most elements that is the main child. + */ +void MatrixElement::goInside(FormulaCursor* cursor) +{ + getElement(0, 0)->goInside(cursor); +} + + +// If there is a main child we must provide the insert/remove semantics. +SequenceElement* MatrixElement::getMainChild() +{ + return content.at(0)->at(0); +} + +void MatrixElement::selectChild(FormulaCursor* cursor, BasicElement* child) +{ + uint rows = getRows(); + uint columns = getColumns(); + for (uint r = 0; r < rows; r++) { + for (uint c = 0; c < columns; c++) { + if (child == getElement(r, c)) { + cursor->setTo(this, r*columns+c); + } + } + } +} + +const MatrixSequenceElement* MatrixElement::getElement( uint row, uint column ) const +{ + QPtrListIterator< QPtrList < MatrixSequenceElement > > rows( content ); + rows += row; + if ( ! rows.current() ) + return 0; + + QPtrListIterator< MatrixSequenceElement > cols ( *rows.current() ); + cols += column; + return cols.current(); +} + + +bool MatrixElement::searchElement(BasicElement* element, uint& row, uint& column) +{ + uint rows = getRows(); + uint columns = getColumns(); + for (uint r = 0; r < rows; r++) { + for (uint c = 0; c < columns; c++) { + if (element == getElement(r, c)) { + row = r; + column = c; + return true; + } + } + } + return false; +} + + +/** + * Appends our attributes to the dom element. + */ +void MatrixElement::writeDom(QDomElement element) +{ + BasicElement::writeDom(element); + + uint rows = getRows(); + uint cols = getColumns(); + + element.setAttribute("ROWS", rows); + element.setAttribute("COLUMNS", cols); + + QDomDocument doc = element.ownerDocument(); + + for (uint r = 0; r < rows; r++) { + for (uint c = 0; c < cols; c++) { + QDomElement tmp = getElement(r,c)->getElementDom(doc); + element.appendChild(tmp); + } + element.appendChild(doc.createComment("end of row")); + } +} + +/** + * Reads our attributes from the element. + * Returns false if it failed. + */ +bool MatrixElement::readAttributesFromDom(QDomElement element) +{ + if (!BasicElement::readAttributesFromDom(element)) { + return false; + } + uint rows = 0; + QString rowStr = element.attribute("ROWS"); + if(!rowStr.isNull()) { + rows = rowStr.toInt(); + } + if (rows == 0) { + kdWarning( DEBUGID ) << "Rows <= 0 in MatrixElement." << endl; + return false; + } + + QString columnStr = element.attribute("COLUMNS"); + uint cols = 0; + if(!columnStr.isNull()) { + cols = columnStr.toInt(); + } + if (cols == 0) { + kdWarning( DEBUGID ) << "Columns <= 0 in MatrixElement." << endl; + return false; + } + + content.clear(); + for (uint r = 0; r < rows; r++) { + QPtrList< MatrixSequenceElement >* list = new QPtrList< MatrixSequenceElement >; + list->setAutoDelete(true); + content.append(list); + for (uint c = 0; c < cols; c++) { + MatrixSequenceElement* element = new MatrixSequenceElement(this); + list->append(element); + } + } + return true; +} + +/** + * Reads our content from the node. Sets the node to the next node + * that needs to be read. + * Returns false if it failed. + */ +bool MatrixElement::readContentFromDom(QDomNode& node) +{ + if (!BasicElement::readContentFromDom(node)) { + return false; + } + + uint rows = getRows(); + uint cols = getColumns(); + + uint r = 0; + uint c = 0; + while ( !node.isNull() && r < rows ) { + if ( node.isElement() ) { + SequenceElement* element = getElement( r, c ); + QDomElement e = node.toElement(); + if ( !element->buildFromDom( e ) ) { + return false; + } + c++; + if ( c == cols ) { + c = 0; + r++; + } + } + node = node.nextSibling(); + } + return true; +} + +bool MatrixElement::readAttributesFromMathMLDom( const QDomElement& element ) +{ + if ( ! BasicElement::readAttributesFromMathMLDom( element ) ) { + return false; + } + + QString alignStr = element.attribute( "align" ).lower(); + if ( ! alignStr.isNull() ) { + if ( alignStr.find( "top" ) != -1 ) { + m_align = TopAlign; + } + else if ( alignStr.find( "bottom" ) != -1 ) { + m_align = BottomAlign; + } + else if ( alignStr.find( "center" ) != -1 ) { + m_align = CenterAlign; + } + else if ( alignStr.find( "baseline" ) != -1 ) { + m_align = BaselineAlign; + } + else if ( alignStr.find( "axis" ) != -1 ) { + m_align = AxisAlign; + } + int index = alignStr.findRev( ' ' ); + if ( index != -1 ) { + m_rowNumber = alignStr.right( index + 1 ).toInt(); + } + } + QString rowalignStr = element.attribute( "rowalign" ).lower(); + if ( ! rowalignStr.isNull() ) { + QStringList list = QStringList::split( ' ', rowalignStr ); + for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) { + if ( *it == "top" ) { + m_rowAlign.append( TopAlign ); + } + else if ( *it == "bottom" ) { + m_rowAlign.append( BottomAlign ); + } + else if ( *it == "center" ) { + m_rowAlign.append( CenterAlign ); + } + else if ( *it == "baseline" ) { + m_rowAlign.append( BaselineAlign ); + } + else if ( *it == "axis" ) { + m_rowAlign.append( AxisAlign ); + } + } + } + QString columnalignStr = element.attribute( "columnalign" ).lower(); + if ( ! columnalignStr.isNull() ) { + QStringList list = QStringList::split( ' ', columnalignStr ); + for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) { + if ( *it == "left" ) { + m_columnAlign.append( LeftHorizontalAlign ); + } + else if ( *it == "center" ) { + m_columnAlign.append( CenterHorizontalAlign ); + } + else if ( *it == "right" ) { + m_columnAlign.append( RightHorizontalAlign ); + } + } + } + QString alignmentscopeStr = element.attribute( "alignmentscope" ).lower(); + if ( ! alignmentscopeStr.isNull() ) { + QStringList list = QStringList::split( ' ', alignmentscopeStr ); + for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) { + if ( *it == "true" ) { + m_alignmentScope.append( true ); + } + else if ( *it == "false" ) { + m_alignmentScope.append( false ); + } + } + } + QString columnwidthStr = element.attribute( "columnwidth" ).lower(); + if ( columnwidthStr.isNull() ) { + QStringList list = QStringList::split( ' ', columnwidthStr ); + for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) { + SizeType type = NoSize; + double length; + if ( *it == "auto" ) { + type = AutoSize; + } + else if ( *it == "fit" ) { + type = FitSize; + } + else { + length = getSize( columnwidthStr, &type ); + if ( type == NoSize ) { + type = getSpace( columnwidthStr ); + } + } + if ( type != NoSize ) { + m_columnWidthType.append( type ); + if ( type == RelativeSize || type == AbsoluteSize || type == PixelSize ) { + m_columnWidth.append( length ); + } + } + } + } + QString widthStr = element.attribute( "width" ).lower(); + if ( ! widthStr.isNull() ) { + if ( widthStr == "auto" ) { + m_widthType = AutoSize; + } + else { + m_width = getSize( widthStr, &m_widthType ); + } + } + QString rowspacingStr = element.attribute( "rowspacing" ).lower(); + if ( ! rowspacingStr.isNull() ) { + QStringList list = QStringList::split( ' ', rowspacingStr ); + for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) { + SizeType type; + double length = getSize( *it, &type ); + if ( type != NoSize ) { + m_rowSpacingType.append( type ); + m_rowSpacing.append( length ); + } + } + } + QString columnspacingStr = element.attribute( "columnspacing" ).lower(); + if ( ! columnspacingStr.isNull() ) { + QStringList list = QStringList::split( ' ', columnspacingStr ); + for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) { + SizeType type; + double length = getSize( *it, &type ); + if ( type == NoSize ) { + type = getSpace( columnspacingStr ); + } + if ( type != NoSize ) { + m_columnSpacingType.append( type ); + if ( type == RelativeSize || type == AbsoluteSize || type == PixelSize ) { + m_columnSpacing.append( length ); + } + } + } + } + QString rowlinesStr = element.attribute( "rowlines" ).lower(); + if ( ! rowlinesStr.isNull() ) { + QStringList list = QStringList::split( ' ', rowlinesStr ); + for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) { + if ( *it == "none" ) { + m_rowLines.append( NoneLine ); + } + else if ( *it == "solid" ) { + m_rowLines.append( SolidLine ); + } + else if ( *it == "dashed" ) { + m_rowLines.append( DashedLine ); + } + } + } + QString columnlinesStr = element.attribute( "columnlines" ).lower(); + if ( ! columnlinesStr.isNull() ) { + QStringList list = QStringList::split( ' ', columnlinesStr ); + for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) { + if ( *it == "none" ) { + m_columnLines.append( NoneLine ); + } + else if ( *it == "solid" ) { + m_columnLines.append( SolidLine ); + } + else if ( *it == "dashed" ) { + m_columnLines.append( DashedLine ); + } + } + } + QString frameStr = element.attribute( "frame" ).stripWhiteSpace().lower(); + if ( ! frameStr.isNull() ) { + if ( frameStr == "none" ) { + m_frame = NoneLine; + } + else if ( frameStr == "solid" ) { + m_frame = SolidLine; + } + else if ( frameStr == "dashed" ) { + m_frame = DashedLine; + } + } + QString framespacingStr = element.attribute( "framespacing" ); + if ( ! framespacingStr.isNull() ) { + QStringList list = QStringList::split( ' ', framespacingStr ); + m_frameHSpacing = getSize( list[0], &m_frameHSpacingType ); + if ( m_frameHSpacingType == NoSize ) { + m_frameHSpacingType = getSpace( list[0] ); + } + if ( list.count() > 1 ) { + m_frameVSpacing = getSize( list[1], &m_frameVSpacingType ); + if ( m_frameVSpacingType == NoSize ) { + m_frameVSpacingType = getSpace( list[1] ); + } + } + } + QString equalrowsStr = element.attribute( "equalrows" ).stripWhiteSpace().lower(); + if ( ! equalrowsStr.isNull() ) { + m_customEqualRows = true; + if ( equalrowsStr == "false" ) { + m_equalRows = false; + } + else { + m_equalRows = true; + } + } + QString equalcolumnsStr = element.attribute( "equalcolumns" ).stripWhiteSpace().lower(); + if ( ! equalcolumnsStr.isNull() ) { + m_customEqualColumns = true; + if ( equalcolumnsStr == "false" ) { + m_equalColumns = false; + } + else { + m_equalColumns = true; + } + } + QString displaystyleStr = element.attribute( "displaystyle" ).stripWhiteSpace().lower(); + if ( ! displaystyleStr.isNull() ) { + m_customDisplayStyle = true; + if ( displaystyleStr == "false" ) { + m_displayStyle = false; + } + else { + m_displayStyle = true; + } + } + QString sideStr = element.attribute( "side" ).stripWhiteSpace().lower(); + if ( ! sideStr.isNull() ) { + if ( sideStr == "left" ) { + m_side = LeftSide; + } + else if ( sideStr == "right" ) { + m_side = RightSide; + } + else if ( sideStr == "leftoverlap" ) { + m_side = LeftOverlapSide; + } + else if ( sideStr == "rightoverlap" ) { + m_side = RightOverlapSide; + } + } + QString minlabelspacingStr = element.attribute( "minlabelspacing" ).stripWhiteSpace().lower(); + if ( ! minlabelspacingStr.isNull() ) { + m_minLabelSpacing = getSize( minlabelspacingStr, &m_minLabelSpacingType ); + if ( m_minLabelSpacingType == NoSize ) { + m_minLabelSpacingType = getSpace( minlabelspacingStr ); + } + } + return true; +} + +/** + * Reads our content from the MathML node. Sets the node to the next node + * that needs to be read. It is sometimes needed to read more than one node + * (e. g. for fence operators). + * Returns the number of nodes processed or -1 if it failed. + */ +int MatrixElement::readContentFromMathMLDom( QDomNode& node ) +{ + // We have twice, since there may be empty elements and we need to know how + // many of them we have. So, first pass, get number of rows and columns + + if ( BasicElement::readContentFromMathMLDom( node ) == -1 ) { + return -1; + } + + uint rows = 0; + uint cols = 0; + QDomNode n = node; + while ( !n.isNull() ) { + if ( n.isElement() ) { + QDomElement e = n.toElement(); + if ( e.tagName().lower() == "mtr" || e.tagName().lower() == "mlabeledtr" ) + { + rows++; + + /* Determins the number of columns */ + QDomNode cellnode = e.firstChild(); + int cc = 0; + + while ( !cellnode.isNull() ) { + if ( cellnode.isElement() ) + cc++; + cellnode = cellnode.nextSibling(); + } + if ( cc > 0 && e.tagName().lower() == "mlabeledtr" ) + cc--; + if ( cc > cols ) + cols = cc; + } + } + n = n.nextSibling(); + } + + // Create elements + content.clear(); + for (uint r = 0; r < rows; r++) { + QPtrList< MatrixSequenceElement >* list = new QPtrList< MatrixSequenceElement >; + list->setAutoDelete(true); + content.append(list); + for (uint c = 0; c < cols; c++) { + MatrixSequenceElement* element = new MatrixSequenceElement(this); + list->append(element); + } + } + + // Second pass, read elements now + uint r = 0; + uint c = 0; + while ( !node.isNull() ) { + if ( node.isElement() ) { + QDomElement e = node.toElement(); + if ( e.tagName().lower() == "mtr" || e.tagName().lower() == "mlabeledtr" ) { + QDomNode cellnode = e.firstChild(); + if ( e.tagName().lower() == "mlabeledtr" ) { + while ( ! cellnode.isNull() && ! cellnode.isElement() ) + cellnode = cellnode.nextSibling(); + if ( ! cellnode.isNull() ) + cellnode = cellnode.nextSibling(); + } + while ( !cellnode.isNull() ) { + if ( cellnode.isElement() ) { + QDomElement cellelement = cellnode.toElement(); + if ( cellelement.tagName().lower() != "mtd" ) { + // TODO: Inferred mtd. Deprecated in MathML 2.0 + kdWarning( DEBUGID ) << "Unsupported tag " + << cellelement.tagName() + << " inside matrix row\n"; + } + else { + SequenceElement* element = getElement(r, c); + if ( element->buildFromMathMLDom( cellelement ) == -1 ) + return -1; + c++; + } + } + cellnode = cellnode.nextSibling(); + } + c = 0; + r++; + } + } + node = node.nextSibling(); + } + return 1; +} + +QString MatrixElement::toLatex() +{ + //All the border handling must be implemented here too + + QString matrix; + uint cols=getColumns(); + uint rows=getRows(); + + matrix="\\begin{array}{ "; + for(uint i=0;i<cols;i++) + matrix+="c "; + + matrix+="} "; + + for (uint r = 0; r < rows; r++) { + for (uint c = 0; c < cols; c++) { + matrix+=getElement(r, c)->toLatex(); + if( c < cols-1) matrix+=" & "; + } + if(r < rows-1 ) matrix+=" \\\\ "; + } + + matrix+=" \\end{array}"; + + return matrix; +} + +QString MatrixElement::formulaString() +{ + QString matrix = "["; + uint cols=getColumns(); + uint rows=getRows(); + for (uint r = 0; r < rows; r++) { + matrix += "["; + for (uint c = 0; c < cols; c++) { + matrix+=getElement(r, c)->formulaString(); + if ( c < cols-1 ) matrix+=", "; + } + matrix += "]"; + if ( r < rows-1 ) matrix += ", "; + } + matrix += "]"; + return matrix; +} + + +SequenceElement* MatrixElement::elementAt(uint row, uint column) +{ + return getElement( row, column ); +} + +void MatrixElement::writeMathMLAttributes( QDomElement& element ) const +{ + QString rownumber; + if ( m_rowNumber ) { + rownumber = QString( " %1" ).arg( m_rowNumber ); + } + switch ( m_align ) { + case TopAlign: + element.setAttribute( "align", "top" + rownumber ); + break; + case BottomAlign: + element.setAttribute( "align", "bottom" + rownumber ); + break; + case CenterAlign: + element.setAttribute( "align", "center" + rownumber ); + break; + case BaselineAlign: + element.setAttribute( "align", "baseline" + rownumber ); + break; + case AxisAlign: + element.setAttribute( "align", "axis" + rownumber ); + break; + default: + break; + } + QString rowalign; + for ( QValueList< VerticalAlign >::const_iterator it = m_rowAlign.begin(); it != m_rowAlign.end(); it++ ) + { + switch ( *it ) { + case TopAlign: + rowalign.append( "top " ); + break; + case BottomAlign: + rowalign.append( "bottom " ); + break; + case CenterAlign: + rowalign.append( "center " ); + break; + case BaselineAlign: + rowalign.append( "baseline " ); + break; + case AxisAlign: + rowalign.append( "axis " ); + break; + default: + break; + } + } + if ( ! rowalign.isNull() ) { + element.setAttribute( "rowalign", rowalign.stripWhiteSpace() ); + } + QString columnalign; + for ( QValueList< HorizontalAlign >::const_iterator it = m_columnAlign.begin(); it != m_columnAlign.end(); it++ ) + { + switch ( *it ) { + case LeftHorizontalAlign: + rowalign.append( "left " ); + break; + case CenterHorizontalAlign: + rowalign.append( "center " ); + break; + case RightHorizontalAlign: + rowalign.append( "right " ); + break; + default: + break; + } + } + if ( ! columnalign.isNull() ) { + element.setAttribute( "columnalign", columnalign.stripWhiteSpace() ); + } + QString alignmentscope; + for ( QValueList< bool >::const_iterator it = m_alignmentScope.begin(); it != m_alignmentScope.end(); it++ ) + { + if ( *it ) { + alignmentscope.append( "true " ); + } + else { + alignmentscope.append( "false " ); + } + } + if ( ! alignmentscope.isNull() ) { + element.setAttribute( "alignmentscope", alignmentscope.stripWhiteSpace() ); + } + QString columnwidth; + QValueList< double >::const_iterator lengthIt = m_columnWidth.begin(); + for ( QValueList< SizeType >::const_iterator typeIt = m_columnWidthType.begin(); + typeIt != m_columnWidthType.end(); typeIt ++ ) { + switch ( *typeIt ) { + case AutoSize: + columnwidth.append( "auto " ); + break; + case FitSize: + columnwidth.append( "fit " ); + break; + case AbsoluteSize: + columnwidth.append( QString( "%1pt " ).arg( *lengthIt ) ); + lengthIt++; + break; + case RelativeSize: + columnwidth.append( QString( "%1% " ).arg( *lengthIt * 100.0 ) ); + lengthIt++; + break; + case PixelSize: + columnwidth.append( QString( "%1px " ).arg( *lengthIt ) ); + lengthIt++; + break; + case NegativeVeryVeryThinMathSpace: + columnwidth.append( "negativeveryverythinmathspace " ); + break; + case NegativeVeryThinMathSpace: + columnwidth.append( "negativeverythinmathspace " ); + break; + case NegativeThinMathSpace: + columnwidth.append( "negativethinmathspace " ); + break; + case NegativeMediumMathSpace: + columnwidth.append( "negativemediummathspace " ); + break; + case NegativeThickMathSpace: + columnwidth.append( "negativethickmathspace " ); + break; + case NegativeVeryThickMathSpace: + columnwidth.append( "negativeverythickmathspace " ); + break; + case NegativeVeryVeryThickMathSpace: + columnwidth.append( "negativeveryverythickmathspace " ); + break; + case VeryVeryThinMathSpace: + columnwidth.append( "veryverythinmathspace " ); + break; + case VeryThinMathSpace: + columnwidth.append( "verythinmathspace " ); + break; + case ThinMathSpace: + columnwidth.append( "thinmathspace " ); + break; + case MediumMathSpace: + columnwidth.append( "mediummathspace " ); + break; + case ThickMathSpace: + columnwidth.append( "thickmathspace " ); + break; + case VeryThickMathSpace: + columnwidth.append( "verythickmathspace " ); + break; + case VeryVeryThickMathSpace: + columnwidth.append( "veryverythickmathspace " ); + break; + default: + break; + } + } + if ( ! columnwidth.isNull() ) { + element.setAttribute( "columnwidth", columnwidth.stripWhiteSpace() ); + } + switch ( m_widthType ) { + case AutoSize: + element.setAttribute( "width", "auto" ); + break; + case AbsoluteSize: + element.setAttribute( "width", QString( "%1pt" ).arg( m_width ) ); + break; + case RelativeSize: + element.setAttribute( "width", QString( "%1% " ).arg( m_width * 100.0 ) ); + break; + case PixelSize: + element.setAttribute( "width", QString( "%1px " ).arg( m_width ) ); + break; + default: + break; + } + QString rowspacing; + lengthIt = m_rowSpacing.begin(); + for ( QValueList< SizeType >::const_iterator typeIt = m_rowSpacingType.begin(); + typeIt != m_rowSpacingType.end(); typeIt++, lengthIt++ ) { + switch ( *typeIt ) { + case AbsoluteSize: + rowspacing.append( QString( "%1pt " ).arg( *lengthIt ) ); + break; + case RelativeSize: + rowspacing.append( QString( "%1% " ).arg( *lengthIt * 100.0 ) ); + break; + case PixelSize: + rowspacing.append( QString( "%1px " ).arg( *lengthIt ) ); + break; + default: + break; + } + } + if ( ! rowspacing.isNull() ) { + element.setAttribute( "rowspacing", rowspacing.stripWhiteSpace() ); + } + QString columnspacing; + lengthIt = m_columnSpacing.begin(); + for ( QValueList< SizeType >::const_iterator typeIt = m_columnSpacingType.begin(); + typeIt != m_columnSpacingType.end(); typeIt++ ) { + switch ( *typeIt ) { + case AbsoluteSize: + columnspacing.append( QString( "%1pt " ).arg( *lengthIt ) ); + lengthIt++; + break; + case RelativeSize: + columnspacing.append( QString( "%1% " ).arg( *lengthIt * 100.0 ) ); + lengthIt++; + break; + case PixelSize: + columnspacing.append( QString( "%1px " ).arg( *lengthIt ) ); + lengthIt++; + break; + case NegativeVeryVeryThinMathSpace: + columnspacing.append( "negativeveryverythinmathspace " ); + break; + case NegativeVeryThinMathSpace: + columnspacing.append( "negativeverythinmathspace " ); + break; + case NegativeThinMathSpace: + columnspacing.append( "negativethinmathspace " ); + break; + case NegativeMediumMathSpace: + columnspacing.append( "negativemediummathspace " ); + break; + case NegativeThickMathSpace: + columnspacing.append( "negativethickmathspace " ); + break; + case NegativeVeryThickMathSpace: + columnspacing.append( "negativeverythickmathspace " ); + break; + case NegativeVeryVeryThickMathSpace: + columnspacing.append( "negativeveryverythickmathspace " ); + break; + case VeryVeryThinMathSpace: + columnspacing.append( "veryverythinmathspace " ); + break; + case VeryThinMathSpace: + columnspacing.append( "verythinmathspace " ); + break; + case ThinMathSpace: + columnspacing.append( "thinmathspace " ); + break; + case MediumMathSpace: + columnspacing.append( "mediummathspace " ); + break; + case ThickMathSpace: + columnspacing.append( "thickmathspace " ); + break; + case VeryThickMathSpace: + columnspacing.append( "verythickmathspace " ); + break; + case VeryVeryThickMathSpace: + columnspacing.append( "veryverythickmathspace " ); + break; + default: + break; + } + } + if ( ! rowspacing.isNull() ) { + element.setAttribute( "rowspacing", rowspacing.stripWhiteSpace() ); + } + QString rowlines; + for ( QValueList< LineType >::const_iterator it = m_rowLines.begin(); it != m_rowLines.end(); it++ ) + { + switch ( *it ) { + case NoneLine: + rowlines.append( "none " ); + break; + case SolidLine: + rowlines.append( "solid " ); + break; + case DashedLine: + rowlines.append( "dashed " ); + break; + default: + break; + } + } + if ( ! rowlines.isNull() ) { + element.setAttribute( "rowlines", rowlines.stripWhiteSpace() ); + } + QString columnlines; + for ( QValueList< LineType >::const_iterator it = m_columnLines.begin(); it != m_columnLines.end(); it++ ) + { + switch ( *it ) { + case NoneLine: + columnlines.append( "none " ); + break; + case SolidLine: + columnlines.append( "solid " ); + break; + case DashedLine: + columnlines.append( "dashed " ); + break; + default: + break; + } + } + if ( ! columnlines.isNull() ) { + element.setAttribute( "columnlines", columnlines.stripWhiteSpace() ); + } + switch ( m_frame ) { + case NoneLine: + element.setAttribute( "frame", "none" ); + break; + case SolidLine: + element.setAttribute( "frame", "solid" ); + break; + case DashedLine: + element.setAttribute( "frame", "dashed" ); + break; + default: + break; + } + QString framespacing; + switch ( m_frameHSpacingType ) { + case AbsoluteSize: + framespacing.append( QString( "%1pt " ).arg( m_frameHSpacing ) ); + break; + case RelativeSize: + framespacing.append( QString( "%1% " ).arg( m_frameHSpacing * 100.0 ) ); + break; + case PixelSize: + framespacing.append( QString( "%1px " ).arg( m_frameHSpacing ) ); + break; + case NegativeVeryVeryThinMathSpace: + framespacing.append( "negativeveryverythinmathspace " ); + break; + case NegativeVeryThinMathSpace: + framespacing.append( "negativeverythinmathspace " ); + break; + case NegativeThinMathSpace: + framespacing.append( "negativethinmathspace " ); + break; + case NegativeMediumMathSpace: + framespacing.append( "negativemediummathspace " ); + break; + case NegativeThickMathSpace: + framespacing.append( "negativethickmathspace " ); + break; + case NegativeVeryThickMathSpace: + framespacing.append( "negativeverythickmathspace " ); + break; + case NegativeVeryVeryThickMathSpace: + framespacing.append( "negativeveryverythickmathspace " ); + break; + case VeryVeryThinMathSpace: + framespacing.append( "veryverythinmathspace " ); + break; + case VeryThinMathSpace: + framespacing.append( "verythinmathspace " ); + break; + case ThinMathSpace: + framespacing.append( "thinmathspace " ); + break; + case MediumMathSpace: + framespacing.append( "mediummathspace " ); + break; + case ThickMathSpace: + framespacing.append( "thickmathspace " ); + break; + case VeryThickMathSpace: + framespacing.append( "verythickmathspace " ); + break; + case VeryVeryThickMathSpace: + framespacing.append( "veryverythickmathspace " ); + break; + default: + break; + } + switch ( m_frameVSpacingType ) { + case AbsoluteSize: + framespacing.append( QString( "%1pt " ).arg( m_frameVSpacing ) ); + break; + case RelativeSize: + framespacing.append( QString( "%1% " ).arg( m_frameVSpacing * 100.0 ) ); + break; + case PixelSize: + framespacing.append( QString( "%1px " ).arg( m_frameVSpacing ) ); + break; + case NegativeVeryVeryThinMathSpace: + framespacing.append( "negativeveryverythinmathspace " ); + break; + case NegativeVeryThinMathSpace: + framespacing.append( "negativeverythinmathspace " ); + break; + case NegativeThinMathSpace: + framespacing.append( "negativethinmathspace " ); + break; + case NegativeMediumMathSpace: + framespacing.append( "negativemediummathspace " ); + break; + case NegativeThickMathSpace: + framespacing.append( "negativethickmathspace " ); + break; + case NegativeVeryThickMathSpace: + framespacing.append( "negativeverythickmathspace " ); + break; + case NegativeVeryVeryThickMathSpace: + framespacing.append( "negativeveryverythickmathspace " ); + break; + case VeryVeryThinMathSpace: + framespacing.append( "veryverythinmathspace " ); + break; + case VeryThinMathSpace: + framespacing.append( "verythinmathspace " ); + break; + case ThinMathSpace: + framespacing.append( "thinmathspace " ); + break; + case MediumMathSpace: + framespacing.append( "mediummathspace " ); + break; + case ThickMathSpace: + framespacing.append( "thickmathspace " ); + break; + case VeryThickMathSpace: + framespacing.append( "verythickmathspace " ); + break; + case VeryVeryThickMathSpace: + framespacing.append( "veryverythickmathspace " ); + break; + default: + break; + } + if ( ! framespacing.isNull() ) { + element.setAttribute( "framespacing", framespacing.stripWhiteSpace() ); + } + if ( m_customEqualRows ) { + element.setAttribute( "equalrows", m_equalRows ? "true" : "false" ); + } + if ( m_customEqualColumns ) { + element.setAttribute( "equalcolumns", m_equalColumns ? "true" : "false" ); + } + if ( m_customDisplayStyle ) { + element.setAttribute( "displaystyle", m_displayStyle ? "true" : "false" ); + } + switch ( m_side ) { + case LeftSide: + element.setAttribute( "side", "left" ); + break; + case RightSide: + element.setAttribute( "side", "right" ); + break; + case LeftOverlapSide: + element.setAttribute( "side", "leftoverlap" ); + break; + case RightOverlapSide: + element.setAttribute( "side", "rightoverlap" ); + break; + default: + break; + } + switch ( m_minLabelSpacingType ) { + case AbsoluteSize: + element.setAttribute( "minlabelspacing", QString( "%1pt" ).arg( m_minLabelSpacing ) ); + break; + case RelativeSize: + element.setAttribute( "minlabelspacing", QString( "%1%" ).arg( m_minLabelSpacing * 100.0 ) ); + break; + case PixelSize: + element.setAttribute( "minlabelspacing", QString( "%1px" ).arg( m_minLabelSpacing ) ); + break; + case NegativeVeryVeryThinMathSpace: + element.setAttribute( "minlabelspacing", "negativeveryverythinmathspace" ); + break; + case NegativeVeryThinMathSpace: + element.setAttribute( "minlabelspacing", "negativeverythinmathspace" ); + break; + case NegativeThinMathSpace: + element.setAttribute( "minlabelspacing", "negativethinmathspace" ); + break; + case NegativeMediumMathSpace: + element.setAttribute( "minlabelspacing", "negativemediummathspace" ); + break; + case NegativeThickMathSpace: + element.setAttribute( "minlabelspacing", "negativethickmathspace" ); + break; + case NegativeVeryThickMathSpace: + element.setAttribute( "minlabelspacing", "negativeverythickmathspace" ); + break; + case NegativeVeryVeryThickMathSpace: + element.setAttribute( "minlabelspacing", "negativeveryverythickmathspace" ); + break; + case VeryVeryThinMathSpace: + element.setAttribute( "minlabelspacing", "veryverythinmathspace" ); + break; + case VeryThinMathSpace: + element.setAttribute( "minlabelspacing", "verythinmathspace" ); + break; + case ThinMathSpace: + element.setAttribute( "minlabelspacing", "thinmathspace" ); + break; + case MediumMathSpace: + element.setAttribute( "minlabelspacing", "mediummathspace" ); + break; + case ThickMathSpace: + element.setAttribute( "minlabelspacing", "thickmathspace" ); + break; + case VeryThickMathSpace: + element.setAttribute( "minlabelspacing", "verythickmathspace" ); + break; + case VeryVeryThickMathSpace: + element.setAttribute( "minlabelspacing", "veryverythickmathspace" ); + break; + default: + break; + } +} + +void MatrixElement::writeMathMLContent( QDomDocument& doc, + QDomElement& element, + bool oasisFormat ) const +{ + QDomElement row; + QDomElement cell; + + uint rows = getRows(); + uint cols = getColumns(); + + for ( uint r = 0; r < rows; r++ ) + { + row = doc.createElement( oasisFormat ? "math:mtr" : "mtr" ); + element.appendChild( row ); + for ( uint c = 0; c < cols; c++ ) + { + cell = doc.createElement( oasisFormat ? "math:mtd" : "mtd" ); + row.appendChild( cell ); + getElement(r,c)->writeMathML( doc, cell, oasisFormat ); + } + } +} + + +////////////////////////////////////////////////////////////////////////////// + + +/** + * The lines behaviour is (a little) different from that + * of ordinary sequences. + */ +class MultilineSequenceElement : public SequenceElement { + typedef SequenceElement inherited; +public: + + MultilineSequenceElement( BasicElement* parent = 0 ); + + virtual MultilineSequenceElement* clone() { + return new MultilineSequenceElement( *this ); + } + + virtual BasicElement* goToPos( FormulaCursor*, bool& handled, + const LuPixelPoint& point, const LuPixelPoint& parentOrigin ); + + /** + * Calculates our width and height and + * our children's parentPosition. + */ + virtual void calcSizes( const ContextStyle& context, + ContextStyle::TextStyle tstyle, + ContextStyle::IndexStyle istyle, + StyleAttributes& style ); + + virtual void registerTab( BasicElement* tab ); + + /** + * This is called by the container to get a command depending on + * the current cursor position (this is how the element gets chosen) + * and the request. + * + * @returns the command that performs the requested action with + * the containers active cursor. + */ + virtual KCommand* buildCommand( Container*, Request* ); + + virtual KCommand* input( Container* container, QKeyEvent* event ); + + virtual KCommand* input( Container* container, QChar ch ); + + uint tabCount() const { return tabs.count(); } + + BasicElement* tab( uint i ) { return tabs.at( i ); } + + /// Change the width of tab i and move all elements after it. + void moveTabTo( uint i, luPixel pos ); + + /// Return the greatest tab number less than pos. + int tabBefore( uint pos ); + + /// Return the position of tab i. + int tabPos( uint i ); + + virtual void writeMathML( QDomDocument& doc, QDomNode& parent, bool oasisFormat = false ) const ; + +private: + + QPtrList<BasicElement> tabs; +}; + + +// Split the line at position pos. +class KFCNewLine : public Command { +public: + KFCNewLine( const QString& name, Container* document, + MultilineSequenceElement* line, uint pos ); + + virtual ~KFCNewLine(); + + virtual void execute(); + virtual void unexecute(); + +private: + MultilineSequenceElement* m_line; + MultilineSequenceElement* m_newline; + uint m_pos; +}; + + +KFCNewLine::KFCNewLine( const QString& name, Container* document, + MultilineSequenceElement* line, uint pos ) + : Command( name, document ), + m_line( line ), m_pos( pos ) +{ + m_newline = new MultilineSequenceElement( m_line->getParent() ); +} + + +KFCNewLine::~KFCNewLine() +{ + delete m_newline; +} + + +void KFCNewLine::execute() +{ + FormulaCursor* cursor = getExecuteCursor(); + MultilineElement* parent = static_cast<MultilineElement*>( m_line->getParent() ); + int linePos = parent->content.find( m_line ); + parent->content.insert( linePos+1, m_newline ); + + // If there are children to be moved. + if ( m_line->countChildren() > static_cast<int>( m_pos ) ) { + + // Remove anything after position pos from the current line + m_line->selectAllChildren( cursor ); + cursor->setMark( m_pos ); + QPtrList<BasicElement> elementList; + m_line->remove( cursor, elementList, beforeCursor ); + + // Insert the removed stuff into the new line + m_newline->goInside( cursor ); + m_newline->insert( cursor, elementList, beforeCursor ); + cursor->setPos( cursor->getMark() ); + } + else { + m_newline->goInside( cursor ); + } + + // The command no longer owns the new line. + m_newline = 0; + + // Tell that something changed + FormulaElement* formula = m_line->formula(); + formula->changed(); + testDirty(); +} + + +void KFCNewLine::unexecute() +{ + FormulaCursor* cursor = getExecuteCursor(); + MultilineElement* parent = static_cast<MultilineElement*>( m_line->getParent() ); + int linePos = parent->content.find( m_line ); + + // Now the command owns the new line again. + m_newline = parent->content.at( linePos+1 ); + + // Tell all cursors to leave this sequence + FormulaElement* formula = m_line->formula(); + formula->elementRemoval( m_newline ); + + // If there are children to be moved. + if ( m_newline->countChildren() > 0 ) { + + // Remove anything from the line to be deleted + m_newline->selectAllChildren( cursor ); + QPtrList<BasicElement> elementList; + m_newline->remove( cursor, elementList, beforeCursor ); + + // Insert the removed stuff into the previous line + m_line->moveEnd( cursor ); + m_line->insert( cursor, elementList, beforeCursor ); + cursor->setPos( cursor->getMark() ); + } + else { + m_line->moveEnd( cursor ); + } + parent->content.take( linePos+1 ); + + // Tell that something changed + formula->changed(); + testDirty(); +} + + +MultilineSequenceElement::MultilineSequenceElement( BasicElement* parent ) + : SequenceElement( parent ) +{ + tabs.setAutoDelete( false ); +} + + +BasicElement* MultilineSequenceElement::goToPos( FormulaCursor* cursor, bool& handled, + const LuPixelPoint& point, const LuPixelPoint& parentOrigin ) +{ + //LuPixelPoint myPos(parentOrigin.x() + getX(), + // parentOrigin.y() + getY()); + BasicElement* e = inherited::goToPos(cursor, handled, point, parentOrigin); + + if (e == 0) { + // If the mouse was behind this line put the cursor to the last position. + if ( ( point.x() > getX()+getWidth() ) && + ( point.y() >= getY() ) && + ( point.y() < getY()+getHeight() ) ) { + cursor->setTo(this, countChildren()); + handled = true; + return this; + } + } + return e; +} + + +void MultilineSequenceElement::calcSizes( const ContextStyle& context, + ContextStyle::TextStyle tstyle, + ContextStyle::IndexStyle istyle, + StyleAttributes& style ) +{ + tabs.clear(); + inherited::calcSizes( context, tstyle, istyle, style ); +} + + +void MultilineSequenceElement::registerTab( BasicElement* tab ) +{ + tabs.append( tab ); +} + + +KCommand* MultilineSequenceElement::buildCommand( Container* container, Request* request ) +{ + FormulaCursor* cursor = container->activeCursor(); + if ( cursor->isReadOnly() ) { + return 0; + } + + switch ( *request ) { + case req_remove: { + // Remove this line if its empty. + // Remove the formula if this line was the only one. + break; + } + case req_addNewline: { + FormulaCursor* cursor = container->activeCursor(); + return new KFCNewLine( i18n( "Add Newline" ), container, this, cursor->getPos() ); + } + case req_addTabMark: { + KFCReplace* command = new KFCReplace( i18n("Add Tabmark"), container ); + SpaceElement* element = new SpaceElement( THIN, true ); + command->addElement( element ); + return command; + } + default: + break; + } + return inherited::buildCommand( container, request ); +} + + +KCommand* MultilineSequenceElement::input( Container* container, QKeyEvent* event ) +{ + int action = event->key(); + //int state = event->state(); + //MoveFlag flag = movementFlag(state); + + switch ( action ) { + case Qt::Key_Enter: + case Qt::Key_Return: { + Request newline( req_addNewline ); + return buildCommand( container, &newline ); + } + case Qt::Key_Tab: { + Request r( req_addTabMark ); + return buildCommand( container, &r ); + } + } + return inherited::input( container, event ); +} + + +KCommand* MultilineSequenceElement::input( Container* container, QChar ch ) +{ + int latin1 = ch.latin1(); + switch (latin1) { + case '&': { + Request r( req_addTabMark ); + return buildCommand( container, &r ); + } + } + return inherited::input( container, ch ); +} + + +void MultilineSequenceElement::moveTabTo( uint i, luPixel pos ) +{ + BasicElement* marker = tab( i ); + luPixel diff = pos - marker->getX(); + marker->setWidth( marker->getWidth() + diff ); + + for ( int p = childPos( marker )+1; p < countChildren(); ++p ) { + BasicElement* child = getChild( p ); + child->setX( child->getX() + diff ); + } + + setWidth( getWidth()+diff ); +} + + +int MultilineSequenceElement::tabBefore( uint pos ) +{ + if ( tabs.isEmpty() ) { + return -1; + } + uint tabNum = 0; + for ( uint i=0; i<pos; ++i ) { + BasicElement* child = getChild( i ); + if ( tabs.at( tabNum ) == child ) { + if ( tabNum+1 == tabs.count() ) { + return tabNum; + } + ++tabNum; + } + } + return static_cast<int>( tabNum )-1; +} + + +int MultilineSequenceElement::tabPos( uint i ) +{ + if ( i < tabs.count() ) { + return childPos( tabs.at( i ) ); + } + return -1; +} + + +void MultilineSequenceElement::writeMathML( QDomDocument& doc, + QDomNode& parent, bool oasisFormat ) const +{ + // parent is required to be a <mtr> tag + + QDomElement tmp = doc.createElement( "TMP" ); + + inherited::writeMathML( doc, tmp, oasisFormat ); + + /* Now we re-parse the Dom tree, because of the TabMarkers + * that have no direct representation in MathML but mark the + * end of a <mtd> tag. + */ + + QDomElement mtd = doc.createElement( oasisFormat ? "math:mtd" : "mtd" ); + + // The mrow, if it exists. + QDomNode n = tmp.firstChild().firstChild(); + while ( !n.isNull() ) { + // the illegal TabMarkers are children of the mrow, child of tmp. + if ( n.isElement() && n.toElement().tagName() == "TAB" ) { + parent.appendChild( mtd ); + mtd = doc.createElement( oasisFormat ? "math:mtd" : "mtd" ); + } + else { + mtd.appendChild( n.cloneNode() ); // cloneNode needed? + } + n = n.nextSibling(); + } + + parent.appendChild( mtd ); +} + + +MultilineElement::MultilineElement( BasicElement* parent ) + : BasicElement( parent ) +{ + content.setAutoDelete( true ); + content.append( new MultilineSequenceElement( this ) ); +} + +MultilineElement::~MultilineElement() +{ +} + +MultilineElement::MultilineElement( const MultilineElement& other ) + : BasicElement( other ) +{ + content.setAutoDelete( true ); + uint count = other.content.count(); + for (uint i = 0; i < count; i++) { + MultilineSequenceElement* line = content.at(i)->clone(); + line->setParent( this ); + content.append( line ); + } +} + + +bool MultilineElement::accept( ElementVisitor* visitor ) +{ + return visitor->visit( this ); +} + + +void MultilineElement::entered( SequenceElement* /*child*/ ) +{ + formula()->tell( i18n( "Multi line element" ) ); +} + + +/** + * Returns the element the point is in. + */ +BasicElement* MultilineElement::goToPos( FormulaCursor* cursor, bool& handled, + const LuPixelPoint& point, const LuPixelPoint& parentOrigin ) +{ + BasicElement* e = inherited::goToPos(cursor, handled, point, parentOrigin); + if ( e != 0 ) { + LuPixelPoint myPos(parentOrigin.x() + getX(), + parentOrigin.y() + getY()); + + uint count = content.count(); + for ( uint i = 0; i < count; ++i ) { + MultilineSequenceElement* line = content.at(i); + e = line->goToPos(cursor, handled, point, myPos); + if (e != 0) { + return e; + } + } + return this; + } + return 0; +} + +void MultilineElement::goInside( FormulaCursor* cursor ) +{ + content.at( 0 )->goInside( cursor ); +} + +void MultilineElement::moveLeft( FormulaCursor* cursor, BasicElement* from ) +{ + // If you want to select more than one line you'll have to + // select the whole element. + if (cursor->isSelectionMode()) { + getParent()->moveLeft(cursor, this); + } + else { + // Coming from the parent (sequence) we go to + // the very last position + if (from == getParent()) { + content.at( content.count()-1 )->moveLeft(cursor, this); + } + else { + // Coming from one of the lines we go to the previous line + // or to the parent if there is none. + int pos = content.find( static_cast<MultilineSequenceElement*>( from ) ); + if ( pos > -1 ) { + if ( pos > 0 ) { + content.at( pos-1 )->moveLeft( cursor, this ); + } + else { + getParent()->moveLeft(cursor, this); + } + } + else { + kdDebug( DEBUGID ) << k_funcinfo << endl; + kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl; + } + } + } +} + +void MultilineElement::moveRight( FormulaCursor* cursor, BasicElement* from ) +{ + if (cursor->isSelectionMode()) { + getParent()->moveRight(cursor, this); + } + else { + if (from == getParent()) { + content.at( 0 )->moveRight(cursor, this); + } + else { + int pos = content.find( static_cast<MultilineSequenceElement*>( from ) ); + if ( pos > -1 ) { + uint upos = pos; + if ( upos < content.count() ) { + if ( upos < content.count()-1 ) { + content.at( upos+1 )->moveRight( cursor, this ); + } + else { + getParent()->moveRight(cursor, this); + } + return; + } + } + kdDebug( DEBUGID ) << k_funcinfo << endl; + kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl; + } + } +} + +void MultilineElement::moveUp( FormulaCursor* cursor, BasicElement* from ) +{ + // If you want to select more than one line you'll have to + // select the whole element. + if (cursor->isSelectionMode()) { + getParent()->moveLeft(cursor, this); + } + else { + // Coming from the parent (sequence) we go to + // the very last position + if (from == getParent()) { + content.at( content.count()-1 )->moveLeft(cursor, this); + } + else { + // Coming from one of the lines we go to the previous line + // or to the parent if there is none. + int pos = content.find( static_cast<MultilineSequenceElement*>( from ) ); + if ( pos > -1 ) { + if ( pos > 0 ) { + //content.at( pos-1 )->moveLeft( cursor, this ); + // This is rather hackish. + // But we know what elements we have here. + int cursorPos = cursor->getPos(); + MultilineSequenceElement* current = content.at( pos ); + MultilineSequenceElement* newLine = content.at( pos-1 ); + int tabNum = current->tabBefore( cursorPos ); + if ( tabNum > -1 ) { + int oldTabPos = current->tabPos( tabNum ); + int newTabPos = newLine->tabPos( tabNum ); + if ( newTabPos > -1 ) { + cursorPos += newTabPos-oldTabPos; + int nextNewTabPos = newLine->tabPos( tabNum+1 ); + if ( nextNewTabPos > -1 ) { + cursorPos = QMIN( cursorPos, nextNewTabPos ); + } + } + else { + cursorPos = newLine->countChildren(); + } + } + else { + int nextNewTabPos = newLine->tabPos( 0 ); + if ( nextNewTabPos > -1 ) { + cursorPos = QMIN( cursorPos, nextNewTabPos ); + } + } + cursor->setTo( newLine, + QMIN( cursorPos, + newLine->countChildren() ) ); + } + else { + getParent()->moveLeft(cursor, this); + } + } + else { + kdDebug( DEBUGID ) << k_funcinfo << endl; + kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl; + } + } + } +} + +void MultilineElement::moveDown( FormulaCursor* cursor, BasicElement* from ) +{ + if (cursor->isSelectionMode()) { + getParent()->moveRight(cursor, this); + } + else { + if (from == getParent()) { + content.at( 0 )->moveRight(cursor, this); + } + else { + int pos = content.find( static_cast<MultilineSequenceElement*>( from ) ); + if ( pos > -1 ) { + uint upos = pos; + if ( upos < content.count() ) { + if ( upos < content.count()-1 ) { + //content.at( upos+1 )->moveRight( cursor, this ); + // This is rather hackish. + // But we know what elements we have here. + int cursorPos = cursor->getPos(); + MultilineSequenceElement* current = content.at( upos ); + MultilineSequenceElement* newLine = content.at( upos+1 ); + int tabNum = current->tabBefore( cursorPos ); + if ( tabNum > -1 ) { + int oldTabPos = current->tabPos( tabNum ); + int newTabPos = newLine->tabPos( tabNum ); + if ( newTabPos > -1 ) { + cursorPos += newTabPos-oldTabPos; + int nextNewTabPos = newLine->tabPos( tabNum+1 ); + if ( nextNewTabPos > -1 ) { + cursorPos = QMIN( cursorPos, nextNewTabPos ); + } + } + else { + cursorPos = newLine->countChildren(); + } + } + else { + int nextNewTabPos = newLine->tabPos( 0 ); + if ( nextNewTabPos > -1 ) { + cursorPos = QMIN( cursorPos, nextNewTabPos ); + } + } + cursor->setTo( newLine, + QMIN( cursorPos, + newLine->countChildren() ) ); + } + else { + getParent()->moveRight(cursor, this); + } + return; + } + } + kdDebug( DEBUGID ) << k_funcinfo << endl; + kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl; + } + } +} + + +void MultilineElement::calcSizes( const ContextStyle& context, + ContextStyle::TextStyle tstyle, + ContextStyle::IndexStyle istyle, + StyleAttributes& style ) +{ + double factor = style.sizeFactor(); + luPt mySize = context.getAdjustedSize( tstyle, factor ); + QFont font = context.getDefaultFont(); + font.setPointSizeFloat( context.layoutUnitPtToPt( mySize ) ); + QFontMetrics fm( font ); + luPixel leading = context.ptToLayoutUnitPt( fm.leading() ); + luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) ); + + uint count = content.count(); + luPixel height = -leading; + luPixel width = 0; + uint tabCount = 0; + for ( uint i = 0; i < count; ++i ) { + MultilineSequenceElement* line = content.at(i); + line->calcSizes( context, tstyle, istyle, style ); + tabCount = QMAX( tabCount, line->tabCount() ); + + height += leading; + line->setX( 0 ); + line->setY( height ); + height += line->getHeight() + distY; + width = QMAX( line->getWidth(), width ); + } + + // calculate the tab positions + for ( uint t = 0; t < tabCount; ++t ) { + luPixel pos = 0; + for ( uint i = 0; i < count; ++i ) { + MultilineSequenceElement* line = content.at(i); + if ( t < line->tabCount() ) { + pos = QMAX( pos, line->tab( t )->getX() ); + } + else { + pos = QMAX( pos, line->getWidth() ); + } + } + for ( uint i = 0; i < count; ++i ) { + MultilineSequenceElement* line = content.at(i); + if ( t < line->tabCount() ) { + line->moveTabTo( t, pos ); + width = QMAX( width, line->getWidth() ); + } + } + } + + setHeight( height ); + setWidth( width ); + if ( count == 1 ) { + setBaseline( content.at( 0 )->getBaseline() ); + } + else { + // There's always a first line. No formulas without lines. + setBaseline( height/2 + context.axisHeight( tstyle, factor ) ); + } +} + +void MultilineElement::draw( QPainter& painter, const LuPixelRect& r, + const ContextStyle& context, + ContextStyle::TextStyle tstyle, + ContextStyle::IndexStyle istyle, + StyleAttributes& style, + const LuPixelPoint& parentOrigin ) +{ + LuPixelPoint myPos( parentOrigin.x() + getX(), parentOrigin.y() + getY() ); + uint count = content.count(); + + if ( context.edit() ) { + uint tabCount = 0; + painter.setPen( context.getHelpColor() ); + for ( uint i = 0; i < count; ++i ) { + MultilineSequenceElement* line = content.at(i); + if ( tabCount < line->tabCount() ) { + for ( uint t = tabCount; t < line->tabCount(); ++t ) { + BasicElement* marker = line->tab( t ); + painter.drawLine( context.layoutUnitToPixelX( myPos.x()+marker->getX() ), + context.layoutUnitToPixelY( myPos.y() ), + context.layoutUnitToPixelX( myPos.x()+marker->getX() ), + context.layoutUnitToPixelY( myPos.y()+getHeight() ) ); + } + tabCount = line->tabCount(); + } + } + } + + for ( uint i = 0; i < count; ++i ) { + MultilineSequenceElement* line = content.at(i); + line->draw( painter, r, context, tstyle, istyle, style, myPos ); + } +} + + +void MultilineElement::dispatchFontCommand( FontCommand* cmd ) +{ + uint count = content.count(); + for ( uint i = 0; i < count; ++i ) { + MultilineSequenceElement* line = content.at(i); + line->dispatchFontCommand( cmd ); + } +} + +void MultilineElement::insert( FormulaCursor* cursor, + QPtrList<BasicElement>& newChildren, + Direction direction ) +{ + MultilineSequenceElement* e = static_cast<MultilineSequenceElement*>(newChildren.take(0)); + e->setParent(this); + content.insert( cursor->getPos(), e ); + + if (direction == beforeCursor) { + e->moveLeft(cursor, this); + } + else { + e->moveRight(cursor, this); + } + cursor->setSelection(false); + formula()->changed(); +} + +void MultilineElement::remove( FormulaCursor* cursor, + QPtrList<BasicElement>& removedChildren, + Direction direction ) +{ + if ( content.count() == 1 ) { //&& ( cursor->getPos() == 0 ) ) { + getParent()->selectChild(cursor, this); + getParent()->remove(cursor, removedChildren, direction); + } + else { + MultilineSequenceElement* e = content.take( cursor->getPos() ); + removedChildren.append( e ); + formula()->elementRemoval( e ); + //cursor->setTo( this, denominatorPos ); + formula()->changed(); + } +} + +void MultilineElement::normalize( FormulaCursor* cursor, Direction direction ) +{ + int pos = cursor->getPos(); + if ( ( cursor->getElement() == this ) && + ( pos > -1 ) && ( static_cast<unsigned>( pos ) <= content.count() ) ) { + switch ( direction ) { + case beforeCursor: + if ( pos > 0 ) { + content.at( pos-1 )->moveLeft( cursor, this ); + break; + } + // no break! intended! + case afterCursor: + if ( static_cast<unsigned>( pos ) < content.count() ) { + content.at( pos )->moveRight( cursor, this ); + } + else { + content.at( pos-1 )->moveLeft( cursor, this ); + } + break; + } + } + else { + inherited::normalize( cursor, direction ); + } +} + +SequenceElement* MultilineElement::getMainChild() +{ + return content.at( 0 ); +} + +void MultilineElement::selectChild(FormulaCursor* cursor, BasicElement* child) +{ + int pos = content.find( dynamic_cast<MultilineSequenceElement*>( child ) ); + if ( pos > -1 ) { + cursor->setTo( this, pos ); + //content.at( pos )->moveRight( cursor, this ); + } +} + + +/** + * Appends our attributes to the dom element. + */ +void MultilineElement::writeDom(QDomElement element) +{ + BasicElement::writeDom(element); + + uint lineCount = content.count(); + element.setAttribute( "LINES", lineCount ); + + QDomDocument doc = element.ownerDocument(); + for ( uint i = 0; i < lineCount; ++i ) { + QDomElement tmp = content.at( i )->getElementDom(doc); + element.appendChild(tmp); + } +} + +void MultilineElement::writeMathML( QDomDocument& doc, QDomNode& parent, bool oasisFormat ) const +{ + QDomElement de = doc.createElement( oasisFormat ? "math:mtable" : "mtable" ); + QDomElement row; QDomElement cell; + + for ( QPtrListIterator < MultilineSequenceElement > it( content ); it.current(); ++it ) { + row = doc.createElement( oasisFormat ? "math:mtr" : "mtr" ); + de.appendChild( row ); + //cell = doc.createElement( "mtd" ); + //row.appendChild( cell ); + + //content.at( i )->writeMathML( doc, cell ); + it.current()->writeMathML( doc, row, oasisFormat ); + } + + parent.appendChild( de ); +} + +/** + * Reads our attributes from the element. + * Returns false if it failed. + */ +bool MultilineElement::readAttributesFromDom(QDomElement element) +{ + if (!BasicElement::readAttributesFromDom(element)) { + return false; + } + uint lineCount = 0; + QString lineCountStr = element.attribute("LINES"); + if(!lineCountStr.isNull()) { + lineCount = lineCountStr.toInt(); + } + if (lineCount == 0) { + kdWarning( DEBUGID ) << "lineCount <= 0 in MultilineElement." << endl; + return false; + } + + content.clear(); + for ( uint i = 0; i < lineCount; ++i ) { + MultilineSequenceElement* element = new MultilineSequenceElement(this); + content.append(element); + } + return true; +} + +/** + * Reads our content from the node. Sets the node to the next node + * that needs to be read. + * Returns false if it failed. + */ +bool MultilineElement::readContentFromDom(QDomNode& node) +{ + if (!BasicElement::readContentFromDom(node)) { + return false; + } + + uint lineCount = content.count(); + uint i = 0; + while ( !node.isNull() && i < lineCount ) { + if ( node.isElement() ) { + SequenceElement* element = content.at( i ); + QDomElement e = node.toElement(); + if ( !element->buildFromDom( e ) ) { + return false; + } + ++i; + } + node = node.nextSibling(); + } + return true; +} + +QString MultilineElement::toLatex() +{ + uint lineCount = content.count(); + QString muliline = "\\begin{split} "; + for ( uint i = 0; i < lineCount; ++i ) { + muliline += content.at( i )->toLatex(); + muliline += " \\\\ "; + } + muliline += "\\end{split}"; + return muliline; +} + +// Does this make any sense at all? +QString MultilineElement::formulaString() +{ + uint lineCount = content.count(); + QString muliline = ""; + for ( uint i = 0; i < lineCount; ++i ) { + muliline += content.at( i )->formulaString(); + muliline += "\n"; + } + //muliline += ""; + return muliline; +} + + +KFORMULA_NAMESPACE_END |