/* This file is part of the KDE project Copyright (C) 2001 Andrea Rizzi Ulrich Kuettler 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 #include #include #include #include #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 TQString& name, Container* document, MatrixElement* m, uint r, uint c ); ~KFCRemoveRow(); virtual void execute(); virtual void unexecute(); protected: MatrixElement* matrix; uint rowPos; uint colPos; TQPtrList* row; }; class KFCInsertRow : public KFCRemoveRow { public: KFCInsertRow( const TQString& 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 TQString& name, Container* document, MatrixElement* m, uint r, uint c ); ~KFCRemoveColumn(); virtual void execute(); virtual void unexecute(); protected: MatrixElement* matrix; uint rowPos; uint colPos; TQPtrList* column; }; class KFCInsertColumn : public KFCRemoveColumn { public: KFCInsertColumn( const TQString& 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( 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 TQString& 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 TQString& name, Container* document, MatrixElement* m, uint r, uint c ) : KFCRemoveRow( name, document, m, r, c ) { row = new TQPtrList< MatrixSequenceElement >; row->setAutoDelete( true ); for ( uint i = 0; i < matrix->getColumns(); i++ ) { row->append( new MatrixSequenceElement( matrix ) ); } } KFCRemoveColumn::KFCRemoveColumn( const TQString& name, Container* document, MatrixElement* m, uint r, uint c ) : Command( name, document ), matrix( m ), rowPos( r ), colPos( c ) { column = new TQPtrList< 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 TQString& 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++) { TQPtrList< MatrixSequenceElement >* list = new TQPtrList< 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(); TQPtrListIterator< TQPtrList< MatrixSequenceElement > > rowIter( other.content ); for (uint r = 0; r < rows; r++) { ++rowIter; TQPtrListIterator< MatrixSequenceElement > colIter( *rowIter.current() ); TQPtrList< MatrixSequenceElement >* list = new TQPtrList< 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 ) { TQMemArray toMidlines(getRows()); TQMemArray fromMidlines(getRows()); TQMemArray 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++) { TQPtrList< 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] = TQMAX(toMidlines[r], element->axis( context, i_tstyle, factor )); fromMidlines[r] = TQMAX(fromMidlines[r], element->getHeight()-element->axis( context, i_tstyle, factor )); widths[c] = TQMAX(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++) { TQPtrList< 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( TQPainter& 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(TQt::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 { TQPtrListIterator< TQPtrList < MatrixSequenceElement > > rows( content ); rows += row; if ( ! rows.current() ) return 0; TQPtrListIterator< 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(TQDomElement element) { BasicElement::writeDom(element); uint rows = getRows(); uint cols = getColumns(); element.setAttribute("ROWS", rows); element.setAttribute("COLUMNS", cols); TQDomDocument doc = element.ownerDocument(); for (uint r = 0; r < rows; r++) { for (uint c = 0; c < cols; c++) { TQDomElement 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(TQDomElement element) { if (!BasicElement::readAttributesFromDom(element)) { return false; } uint rows = 0; TQString rowStr = element.attribute("ROWS"); if(!rowStr.isNull()) { rows = rowStr.toInt(); } if (rows == 0) { kdWarning( DEBUGID ) << "Rows <= 0 in MatrixElement." << endl; return false; } TQString 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++) { TQPtrList< MatrixSequenceElement >* list = new TQPtrList< 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(TQDomNode& 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 ); TQDomElement 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 TQDomElement& element ) { if ( ! BasicElement::readAttributesFromMathMLDom( element ) ) { return false; } TQString 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(); } } TQString rowalignStr = element.attribute( "rowalign" ).lower(); if ( ! rowalignStr.isNull() ) { TQStringList list = TQStringList::split( ' ', rowalignStr ); for ( TQStringList::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 ); } } } TQString columnalignStr = element.attribute( "columnalign" ).lower(); if ( ! columnalignStr.isNull() ) { TQStringList list = TQStringList::split( ' ', columnalignStr ); for ( TQStringList::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 ); } } } TQString alignmentscopeStr = element.attribute( "alignmentscope" ).lower(); if ( ! alignmentscopeStr.isNull() ) { TQStringList list = TQStringList::split( ' ', alignmentscopeStr ); for ( TQStringList::iterator it = list.begin(); it != list.end(); it++ ) { if ( *it == "true" ) { m_alignmentScope.append( true ); } else if ( *it == "false" ) { m_alignmentScope.append( false ); } } } TQString columnwidthStr = element.attribute( "columnwidth" ).lower(); if ( columnwidthStr.isNull() ) { TQStringList list = TQStringList::split( ' ', columnwidthStr ); for ( TQStringList::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 ); } } } } TQString widthStr = element.attribute( "width" ).lower(); if ( ! widthStr.isNull() ) { if ( widthStr == "auto" ) { m_widthType = AutoSize; } else { m_width = getSize( widthStr, &m_widthType ); } } TQString rowspacingStr = element.attribute( "rowspacing" ).lower(); if ( ! rowspacingStr.isNull() ) { TQStringList list = TQStringList::split( ' ', rowspacingStr ); for ( TQStringList::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 ); } } } TQString columnspacingStr = element.attribute( "columnspacing" ).lower(); if ( ! columnspacingStr.isNull() ) { TQStringList list = TQStringList::split( ' ', columnspacingStr ); for ( TQStringList::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 ); } } } } TQString rowlinesStr = element.attribute( "rowlines" ).lower(); if ( ! rowlinesStr.isNull() ) { TQStringList list = TQStringList::split( ' ', rowlinesStr ); for ( TQStringList::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 ); } } } TQString columnlinesStr = element.attribute( "columnlines" ).lower(); if ( ! columnlinesStr.isNull() ) { TQStringList list = TQStringList::split( ' ', columnlinesStr ); for ( TQStringList::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 ); } } } TQString 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; } } TQString framespacingStr = element.attribute( "framespacing" ); if ( ! framespacingStr.isNull() ) { TQStringList list = TQStringList::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] ); } } } TQString equalrowsStr = element.attribute( "equalrows" ).stripWhiteSpace().lower(); if ( ! equalrowsStr.isNull() ) { m_customEqualRows = true; if ( equalrowsStr == "false" ) { m_equalRows = false; } else { m_equalRows = true; } } TQString equalcolumnsStr = element.attribute( "equalcolumns" ).stripWhiteSpace().lower(); if ( ! equalcolumnsStr.isNull() ) { m_customEqualColumns = true; if ( equalcolumnsStr == "false" ) { m_equalColumns = false; } else { m_equalColumns = true; } } TQString displaystyleStr = element.attribute( "displaystyle" ).stripWhiteSpace().lower(); if ( ! displaystyleStr.isNull() ) { m_customDisplayStyle = true; if ( displaystyleStr == "false" ) { m_displayStyle = false; } else { m_displayStyle = true; } } TQString 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; } } TQString 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( TQDomNode& 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; TQDomNode n = node; while ( !n.isNull() ) { if ( n.isElement() ) { TQDomElement e = n.toElement(); if ( e.tagName().lower() == "mtr" || e.tagName().lower() == "mlabeledtr" ) { rows++; /* Determins the number of columns */ TQDomNode 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++) { TQPtrList< MatrixSequenceElement >* list = new TQPtrList< 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() ) { TQDomElement e = node.toElement(); if ( e.tagName().lower() == "mtr" || e.tagName().lower() == "mlabeledtr" ) { TQDomNode 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() ) { TQDomElement 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; } TQString MatrixElement::toLatex() { //All the border handling must be implemented here too TQString matrix; uint cols=getColumns(); uint rows=getRows(); matrix="\\begin{array}{ "; for(uint i=0;itoLatex(); if( c < cols-1) matrix+=" & "; } if(r < rows-1 ) matrix+=" \\\\ "; } matrix+=" \\end{array}"; return matrix; } TQString MatrixElement::formulaString() { TQString 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( TQDomElement& element ) const { TQString rownumber; if ( m_rowNumber ) { rownumber = TQString( " %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; } TQString rowalign; for ( TQValueList::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() ); } TQString columnalign; for ( TQValueList::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() ); } TQString alignmentscope; for ( TQValueList< 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() ); } TQString columnwidth; TQValueList< double >::const_iterator lengthIt = m_columnWidth.begin(); for ( TQValueList< 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( TQString( "%1pt " ).arg( *lengthIt ) ); lengthIt++; break; case RelativeSize: columnwidth.append( TQString( "%1% " ).arg( *lengthIt * 100.0 ) ); lengthIt++; break; case PixelSize: columnwidth.append( TQString( "%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", TQString( "%1pt" ).arg( m_width ) ); break; case RelativeSize: element.setAttribute( "width", TQString( "%1% " ).arg( m_width * 100.0 ) ); break; case PixelSize: element.setAttribute( "width", TQString( "%1px " ).arg( m_width ) ); break; default: break; } TQString rowspacing; lengthIt = m_rowSpacing.begin(); for ( TQValueList< SizeType >::const_iterator typeIt = m_rowSpacingType.begin(); typeIt != m_rowSpacingType.end(); typeIt++, lengthIt++ ) { switch ( *typeIt ) { case AbsoluteSize: rowspacing.append( TQString( "%1pt " ).arg( *lengthIt ) ); break; case RelativeSize: rowspacing.append( TQString( "%1% " ).arg( *lengthIt * 100.0 ) ); break; case PixelSize: rowspacing.append( TQString( "%1px " ).arg( *lengthIt ) ); break; default: break; } } if ( ! rowspacing.isNull() ) { element.setAttribute( "rowspacing", rowspacing.stripWhiteSpace() ); } TQString columnspacing; lengthIt = m_columnSpacing.begin(); for ( TQValueList< SizeType >::const_iterator typeIt = m_columnSpacingType.begin(); typeIt != m_columnSpacingType.end(); typeIt++ ) { switch ( *typeIt ) { case AbsoluteSize: columnspacing.append( TQString( "%1pt " ).arg( *lengthIt ) ); lengthIt++; break; case RelativeSize: columnspacing.append( TQString( "%1% " ).arg( *lengthIt * 100.0 ) ); lengthIt++; break; case PixelSize: columnspacing.append( TQString( "%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() ); } TQString rowlines; for ( TQValueList< 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() ); } TQString columnlines; for ( TQValueList< 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; } TQString framespacing; switch ( m_frameHSpacingType ) { case AbsoluteSize: framespacing.append( TQString( "%1pt " ).arg( m_frameHSpacing ) ); break; case RelativeSize: framespacing.append( TQString( "%1% " ).arg( m_frameHSpacing * 100.0 ) ); break; case PixelSize: framespacing.append( TQString( "%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( TQString( "%1pt " ).arg( m_frameVSpacing ) ); break; case RelativeSize: framespacing.append( TQString( "%1% " ).arg( m_frameVSpacing * 100.0 ) ); break; case PixelSize: framespacing.append( TQString( "%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", TQString( "%1pt" ).arg( m_minLabelSpacing ) ); break; case RelativeSize: element.setAttribute( "minlabelspacing", TQString( "%1%" ).arg( m_minLabelSpacing * 100.0 ) ); break; case PixelSize: element.setAttribute( "minlabelspacing", TQString( "%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( TQDomDocument& doc, TQDomElement& element, bool oasisFormat ) const { TQDomElement row; TQDomElement 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, TQKeyEvent* event ); virtual KCommand* input( Container* container, TQChar 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( TQDomDocument& doc, TQDomNode& parent, bool oasisFormat = false ) const ; private: TQPtrList tabs; }; // Split the line at position pos. class KFCNewLine : public Command { public: KFCNewLine( const TQString& 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 TQString& 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( 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( m_pos ) ) { // Remove anything after position pos from the current line m_line->selectAllChildren( cursor ); cursor->setMark( m_pos ); TQPtrList 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( 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 ); TQPtrList 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, TQKeyEvent* event ) { int action = event->key(); //int state = event->state(); //MoveFlag flag = movementFlag(state); switch ( action ) { case TQt::Key_Enter: case TQt::Key_Return: { Request newline( req_addNewline ); return buildCommand( container, &newline ); } case TQt::Key_Tab: { Request r( req_addTabMark ); return buildCommand( container, &r ); } } return inherited::input( container, event ); } KCommand* MultilineSequenceElement::input( Container* container, TQChar 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( tabNum )-1; } int MultilineSequenceElement::tabPos( uint i ) { if ( i < tabs.count() ) { return childPos( tabs.at( i ) ); } return -1; } void MultilineSequenceElement::writeMathML( TQDomDocument& doc, TQDomNode& parent, bool oasisFormat ) const { // parent is required to be a tag TQDomElement 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 tag. */ TQDomElement mtd = doc.createElement( oasisFormat ? "math:mtd" : "mtd" ); // The mrow, if it exists. TQDomNode 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( 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( 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( 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 = TQMIN( cursorPos, nextNewTabPos ); } } else { cursorPos = newLine->countChildren(); } } else { int nextNewTabPos = newLine->tabPos( 0 ); if ( nextNewTabPos > -1 ) { cursorPos = TQMIN( cursorPos, nextNewTabPos ); } } cursor->setTo( newLine, TQMIN( 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( 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 = TQMIN( cursorPos, nextNewTabPos ); } } else { cursorPos = newLine->countChildren(); } } else { int nextNewTabPos = newLine->tabPos( 0 ); if ( nextNewTabPos > -1 ) { cursorPos = TQMIN( cursorPos, nextNewTabPos ); } } cursor->setTo( newLine, TQMIN( 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 ); TQFont font = context.getDefaultFont(); font.setPointSizeFloat( context.layoutUnitPtToPt( mySize ) ); TQFontMetrics 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 = TQMAX( tabCount, line->tabCount() ); height += leading; line->setX( 0 ); line->setY( height ); height += line->getHeight() + distY; width = TQMAX( 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 = TQMAX( pos, line->tab( t )->getX() ); } else { pos = TQMAX( pos, line->getWidth() ); } } for ( uint i = 0; i < count; ++i ) { MultilineSequenceElement* line = content.at(i); if ( t < line->tabCount() ) { line->moveTabTo( t, pos ); width = TQMAX( 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( TQPainter& 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, TQPtrList& newChildren, Direction direction ) { MultilineSequenceElement* e = static_cast(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, TQPtrList& 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( 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( 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( child ) ); if ( pos > -1 ) { cursor->setTo( this, pos ); //content.at( pos )->moveRight( cursor, this ); } } /** * Appends our attributes to the dom element. */ void MultilineElement::writeDom(TQDomElement element) { BasicElement::writeDom(element); uint lineCount = content.count(); element.setAttribute( "LINES", lineCount ); TQDomDocument doc = element.ownerDocument(); for ( uint i = 0; i < lineCount; ++i ) { TQDomElement tmp = content.at( i )->getElementDom(doc); element.appendChild(tmp); } } void MultilineElement::writeMathML( TQDomDocument& doc, TQDomNode& parent, bool oasisFormat ) const { TQDomElement de = doc.createElement( oasisFormat ? "math:mtable" : "mtable" ); TQDomElement row; TQDomElement cell; for ( TQPtrListIterator < 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(TQDomElement element) { if (!BasicElement::readAttributesFromDom(element)) { return false; } uint lineCount = 0; TQString 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(TQDomNode& 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 ); TQDomElement e = node.toElement(); if ( !element->buildFromDom( e ) ) { return false; } ++i; } node = node.nextSibling(); } return true; } TQString MultilineElement::toLatex() { uint lineCount = content.count(); TQString 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? TQString MultilineElement::formulaString() { uint lineCount = content.count(); TQString muliline = ""; for ( uint i = 0; i < lineCount; ++i ) { muliline += content.at( i )->formulaString(); muliline += "\n"; } //muliline += ""; return muliline; } KFORMULA_NAMESPACE_END