lib

matrixelement.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
00003                   Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include <qmemarray.h>
00022 #include <qpainter.h>
00023 #include <qptrlist.h>
00024 
00025 #include <kdebug.h>
00026 #include <klocale.h>
00027 
00028 #include "MatrixDialog.h"
00029 #include "elementvisitor.h"
00030 #include "formulaelement.h"
00031 #include "formulacursor.h"
00032 #include "kformulacontainer.h"
00033 #include "kformulacommand.h"
00034 #include "matrixelement.h"
00035 #include "sequenceelement.h"
00036 #include "spaceelement.h"
00037 
00038 
00039 KFORMULA_NAMESPACE_BEGIN
00040 
00041 
00042 class MatrixSequenceElement : public SequenceElement {
00043     typedef SequenceElement inherited;
00044 public:
00045 
00046     MatrixSequenceElement( BasicElement* parent = 0 ) : SequenceElement( parent ) {}
00047     virtual MatrixSequenceElement* clone() {
00048         return new MatrixSequenceElement( *this );
00049     }
00050 
00059     virtual KCommand* buildCommand( Container*, Request* );
00060 };
00061 
00062 
00063 class KFCRemoveRow : public Command {
00064 public:
00065     KFCRemoveRow( const QString& name, Container* document, MatrixElement* m, uint r, uint c );
00066     ~KFCRemoveRow();
00067 
00068     virtual void execute();
00069     virtual void unexecute();
00070 
00071 protected:
00072     MatrixElement* matrix;
00073     uint rowPos;
00074     uint colPos;
00075 
00076     QPtrList<MatrixSequenceElement>* row;
00077 };
00078 
00079 
00080 class KFCInsertRow : public KFCRemoveRow {
00081 public:
00082     KFCInsertRow( const QString& name, Container* document, MatrixElement* m, uint r, uint c );
00083 
00084     virtual void execute()   { KFCRemoveRow::unexecute(); }
00085     virtual void unexecute() { KFCRemoveRow::execute(); }
00086 };
00087 
00088 
00089 class KFCRemoveColumn : public Command {
00090 public:
00091     KFCRemoveColumn( const QString& name, Container* document, MatrixElement* m, uint r, uint c );
00092     ~KFCRemoveColumn();
00093 
00094     virtual void execute();
00095     virtual void unexecute();
00096 
00097 protected:
00098     MatrixElement* matrix;
00099     uint rowPos;
00100     uint colPos;
00101 
00102     QPtrList<MatrixSequenceElement>* column;
00103 };
00104 
00105 
00106 class KFCInsertColumn : public KFCRemoveColumn {
00107 public:
00108     KFCInsertColumn( const QString& name, Container* document, MatrixElement* m, uint r, uint c );
00109 
00110     virtual void execute()   { KFCRemoveColumn::unexecute(); }
00111     virtual void unexecute() { KFCRemoveColumn::execute(); }
00112 };
00113 
00114 
00115 KCommand* MatrixSequenceElement::buildCommand( Container* container, Request* request )
00116 {
00117     FormulaCursor* cursor = container->activeCursor();
00118     if ( cursor->isReadOnly() ) {
00119         return 0;
00120     }
00121 
00122     switch ( *request ) {
00123     case req_appendColumn:
00124     case req_appendRow:
00125     case req_insertColumn:
00126     case req_removeColumn:
00127     case req_insertRow:
00128     case req_removeRow: {
00129         MatrixElement* matrix = static_cast<MatrixElement*>( getParent() );
00130         FormulaCursor* cursor = container->activeCursor();
00131         for ( uint row = 0; row < matrix->getRows(); row++ ) {
00132             for ( uint col = 0; col < matrix->getColumns(); col++ ) {
00133                 if ( matrix->getElement( row, col ) == cursor->getElement() ) {
00134                     switch ( *request ) {
00135                     case req_appendColumn:
00136                         return new KFCInsertColumn( i18n( "Append Column" ), container, matrix, row, matrix->getColumns() );
00137                     case req_appendRow:
00138                         return new KFCInsertRow( i18n( "Append Row" ), container, matrix, matrix->getRows(), col );
00139                     case req_insertColumn:
00140                         return new KFCInsertColumn( i18n( "Insert Column" ), container, matrix, row, col );
00141                     case req_removeColumn:
00142                         if ( matrix->getColumns() > 1 ) {
00143                             return new KFCRemoveColumn( i18n( "Remove Column" ), container, matrix, row, col );
00144                         }
00145                         break;
00146                     case req_insertRow:
00147                         return new KFCInsertRow( i18n( "Insert Row" ), container, matrix, row, col );
00148                     case req_removeRow:
00149                         if ( matrix->getRows() > 1 ) {
00150                             return new KFCRemoveRow( i18n( "Remove Row" ), container, matrix, row, col );
00151                         }
00152                         break;
00153                     default:
00154                         break;
00155                     }
00156                 }
00157             }
00158         }
00159         kdWarning( DEBUGID ) << "MatrixSequenceElement::buildCommand: Sequence not found." << endl;
00160         break;
00161     }
00162     default:
00163         break;
00164     }
00165     return inherited::buildCommand( container, request );
00166 }
00167 
00168 
00169 KFCRemoveRow::KFCRemoveRow( const QString& name, Container* document, MatrixElement* m, uint r, uint c )
00170     : Command( name, document ), matrix( m ), rowPos( r ), colPos( c ), row( 0 )
00171 {
00172 }
00173 
00174 KFCRemoveRow::~KFCRemoveRow()
00175 {
00176     delete row;
00177 }
00178 
00179 void KFCRemoveRow::execute()
00180 {
00181     FormulaCursor* cursor = getExecuteCursor();
00182     row = matrix->content.at( rowPos );
00183     FormulaElement* formula = matrix->formula();
00184     for ( uint i = matrix->getColumns(); i > 0; i-- ) {
00185         formula->elementRemoval( row->at( i-1 ) );
00186     }
00187     matrix->content.take( rowPos );
00188     formula->changed();
00189     if ( rowPos < matrix->getRows() ) {
00190         matrix->getElement( rowPos, colPos )->goInside( cursor );
00191     }
00192     else {
00193         matrix->getElement( rowPos-1, colPos )->goInside( cursor );
00194     }
00195     testDirty();
00196 }
00197 
00198 void KFCRemoveRow::unexecute()
00199 {
00200     matrix->content.insert( rowPos, row );
00201     row = 0;
00202     FormulaCursor* cursor = getExecuteCursor();
00203     matrix->getElement( rowPos, colPos )->goInside( cursor );
00204     matrix->formula()->changed();
00205     testDirty();
00206 }
00207 
00208 
00209 KFCInsertRow::KFCInsertRow( const QString& name, Container* document, MatrixElement* m, uint r, uint c )
00210     : KFCRemoveRow( name, document, m, r, c )
00211 {
00212     row = new QPtrList< MatrixSequenceElement >;
00213     row->setAutoDelete( true );
00214     for ( uint i = 0; i < matrix->getColumns(); i++ ) {
00215         row->append( new MatrixSequenceElement( matrix ) );
00216     }
00217 }
00218 
00219 
00220 KFCRemoveColumn::KFCRemoveColumn( const QString& name, Container* document, MatrixElement* m, uint r, uint c )
00221     : Command( name, document ), matrix( m ), rowPos( r ), colPos( c )
00222 {
00223     column = new QPtrList< MatrixSequenceElement >;
00224     column->setAutoDelete( true );
00225 }
00226 
00227 KFCRemoveColumn::~KFCRemoveColumn()
00228 {
00229     delete column;
00230 }
00231 
00232 void KFCRemoveColumn::execute()
00233 {
00234     FormulaCursor* cursor = getExecuteCursor();
00235     FormulaElement* formula = matrix->formula();
00236     for ( uint i = 0; i < matrix->getRows(); i++ ) {
00237         column->append( matrix->getElement( i, colPos ) );
00238         formula->elementRemoval( column->at( i ) );
00239         matrix->content.at( i )->take( colPos );
00240     }
00241     formula->changed();
00242     if ( colPos < matrix->getColumns() ) {
00243         matrix->getElement( rowPos, colPos )->goInside( cursor );
00244     }
00245     else {
00246         matrix->getElement( rowPos, colPos-1 )->goInside( cursor );
00247     }
00248     testDirty();
00249 }
00250 
00251 void KFCRemoveColumn::unexecute()
00252 {
00253     for ( uint i = 0; i < matrix->getRows(); i++ ) {
00254         matrix->content.at( i )->insert( colPos, column->take( 0 ) );
00255     }
00256     FormulaCursor* cursor = getExecuteCursor();
00257     matrix->getElement( rowPos, colPos )->goInside( cursor );
00258     matrix->formula()->changed();
00259     testDirty();
00260 }
00261 
00262 
00263 KFCInsertColumn::KFCInsertColumn( const QString& name, Container* document, MatrixElement* m, uint r, uint c )
00264     : KFCRemoveColumn( name, document, m, r, c )
00265 {
00266     for ( uint i = 0; i < matrix->getRows(); i++ ) {
00267         column->append( new MatrixSequenceElement( matrix ) );
00268     }
00269 }
00270 
00271 
00272 MatrixElement::MatrixElement(uint rows, uint columns, BasicElement* parent)
00273     : BasicElement(parent),
00274       m_rowNumber( 0 ),
00275       m_align( NoAlign ),
00276       m_widthType( NoSize ),
00277       m_frame( NoLine ),
00278       m_frameHSpacing( NoSize ),
00279       m_frameVSpacing( NoSize ),
00280       m_side( NoSide ),
00281       m_minLabelSpacingType( NoSize ),
00282       m_customEqualRows( false ),
00283       m_customEqualColumns( false ),
00284       m_customDisplayStyle( false )
00285 {
00286     for (uint r = 0; r < rows; r++) {
00287         QPtrList< MatrixSequenceElement >* list = new QPtrList< MatrixSequenceElement >;
00288         list->setAutoDelete(true);
00289         for (uint c = 0; c < columns; c++) {
00290             list->append(new MatrixSequenceElement(this));
00291         }
00292         content.append(list);
00293     }
00294     content.setAutoDelete(true);
00295 }
00296 
00297 MatrixElement::~MatrixElement()
00298 {
00299 }
00300 
00301 
00302 MatrixElement::MatrixElement( const MatrixElement& other )
00303     : BasicElement( other )
00304 {
00305     uint rows = other.getRows();
00306     uint columns = other.getColumns();
00307 
00308     QPtrListIterator< QPtrList< MatrixSequenceElement > > rowIter( other.content );
00309     for (uint r = 0; r < rows; r++) {
00310         ++rowIter;
00311         QPtrListIterator< MatrixSequenceElement > colIter( *rowIter.current() );
00312 
00313         QPtrList< MatrixSequenceElement >* list = new QPtrList< MatrixSequenceElement >;
00314         list->setAutoDelete(true);
00315         for (uint c = 0; c < columns; c++) {
00316             ++colIter;
00317             MatrixSequenceElement *mse =
00318                 //new MatrixSequenceElement( *( other.getElement( r, c ) ) );
00319                 new MatrixSequenceElement( *colIter.current() );
00320             list->append( mse );
00321             mse->setParent( this );
00322         }
00323         content.append(list);
00324     }
00325     content.setAutoDelete(true);
00326 }
00327 
00328 
00329 bool MatrixElement::accept( ElementVisitor* visitor )
00330 {
00331     return visitor->visit( this );
00332 }
00333 
00334 
00335 void MatrixElement::entered( SequenceElement* /*child*/ )
00336 {
00337     formula()->tell( i18n( "Matrix element" ) );
00338 }
00339 
00340 
00341 BasicElement* MatrixElement::goToPos( FormulaCursor* cursor, bool& handled,
00342                                       const LuPixelPoint& point, const LuPixelPoint& parentOrigin )
00343 {
00344     BasicElement* e = BasicElement::goToPos(cursor, handled, point, parentOrigin);
00345     if (e != 0) {
00346         LuPixelPoint myPos(parentOrigin.x() + getX(),
00347                            parentOrigin.y() + getY());
00348 
00349         uint rows = getRows();
00350         uint columns = getColumns();
00351 
00352         for (uint r = 0; r < rows; r++) {
00353             for (uint c = 0; c < columns; c++) {
00354                 BasicElement* element = getElement(r, c);
00355                 e = element->goToPos(cursor, handled, point, myPos);
00356                 if (e != 0) {
00357                     return e;
00358                 }
00359             }
00360         }
00361 
00362         // We are in one of those gaps.
00363         luPixel dx = point.x() - myPos.x();
00364         luPixel dy = point.y() - myPos.y();
00365 
00366         uint row = rows;
00367         for (uint r = 0; r < rows; r++) {
00368             BasicElement* element = getElement(r, 0);
00369             if (element->getY() > dy) {
00370                 row = r;
00371                 break;
00372             }
00373         }
00374         if (row == 0) {
00375             BasicElement* element = getParent();
00376             element->moveLeft(cursor, this);
00377             handled = true;
00378             return element;
00379         }
00380         row--;
00381 
00382         uint column = columns;
00383         for (uint c = 0; c < columns; c++) {
00384             BasicElement* element = getElement(row, c);
00385             if (element->getX() > dx) {
00386                 column = c;
00387                 break;
00388             }
00389         }
00390         if (column == 0) {
00391             BasicElement* element = getParent();
00392             element->moveLeft(cursor, this);
00393             handled = true;
00394             return element;
00395         }
00396         column--;
00397 
00398         // Rescan the rows with the actual colums required.
00399         row = rows;
00400         for (uint r = 0; r < rows; r++) {
00401             BasicElement* element = getElement(r, column);
00402             if (element->getY() > dy) {
00403                 row = r;
00404                 break;
00405             }
00406         }
00407         if (row == 0) {
00408             BasicElement* element = getParent();
00409             element->moveLeft(cursor, this);
00410             handled = true;
00411             return element;
00412         }
00413         row--;
00414 
00415         BasicElement* element = getElement(row, column);
00416         element->moveLeft(cursor, this);
00417         handled = true;
00418         return element;
00419     }
00420     return 0;
00421 }
00422 
00423 
00424 // drawing
00425 //
00426 // Drawing depends on a context which knows the required properties like
00427 // fonts, spaces and such.
00428 // It is essential to calculate elements size with the same context
00429 // before you draw.
00430 
00435 void MatrixElement::calcSizes( const ContextStyle& context,
00436                                ContextStyle::TextStyle tstyle,
00437                                ContextStyle::IndexStyle istyle,
00438                                StyleAttributes& style )
00439 {
00440     QMemArray<luPixel> toMidlines(getRows());
00441     QMemArray<luPixel> fromMidlines(getRows());
00442     QMemArray<luPixel> widths(getColumns());
00443 
00444     toMidlines.fill(0);
00445     fromMidlines.fill(0);
00446     widths.fill(0);
00447 
00448     uint rows = getRows();
00449     uint columns = getColumns();
00450 
00451     ContextStyle::TextStyle i_tstyle = context.convertTextStyleFraction(tstyle);
00452     ContextStyle::IndexStyle i_istyle = context.convertIndexStyleUpper(istyle);
00453     double factor = style.sizeFactor();
00454 
00455     for (uint r = 0; r < rows; r++) {
00456         QPtrList< MatrixSequenceElement >* list = content.at(r);
00457         for (uint c = 0; c < columns; c++) {
00458             SequenceElement* element = list->at(c);
00459             element->calcSizes( context, i_tstyle, i_istyle, style );
00460             toMidlines[r] = QMAX(toMidlines[r], element->axis( context, i_tstyle, factor ));
00461             fromMidlines[r] = QMAX(fromMidlines[r],
00462                                    element->getHeight()-element->axis( context, i_tstyle, factor ));
00463             widths[c] = QMAX(widths[c], element->getWidth());
00464         }
00465     }
00466 
00467     luPixel distX = context.ptToPixelX( context.getThinSpace( tstyle, factor ) );
00468     luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );
00469 
00470     luPixel yPos = 0;
00471     for (uint r = 0; r < rows; r++) {
00472         QPtrList< MatrixSequenceElement >* list = content.at(r);
00473         luPixel xPos = 0;
00474         yPos += toMidlines[r];
00475         for (uint c = 0; c < columns; c++) {
00476             SequenceElement* element = list->at(c);
00477             switch (context.getMatrixAlignment()) {
00478             case ContextStyle::left:
00479                 element->setX(xPos);
00480                 break;
00481             case ContextStyle::center:
00482                 element->setX(xPos + (widths[c] - element->getWidth())/2);
00483                 break;
00484             case ContextStyle::right:
00485                 element->setX(xPos + widths[c] - element->getWidth());
00486                 break;
00487             }
00488             element->setY(yPos - element->axis( context, i_tstyle, factor ));
00489             xPos += widths[c] + distX;
00490         }
00491         yPos += fromMidlines[r] + distY;
00492     }
00493 
00494     luPixel width = distX * (columns - 1);
00495     luPixel height = distY * (rows - 1);
00496 
00497     for (uint r = 0; r < rows; r++) height += toMidlines[r] + fromMidlines[r];
00498     for (uint c = 0; c < columns; c++) width += widths[c];
00499 
00500     setWidth(width);
00501     setHeight(height);
00502     if ((rows == 2) && (columns == 1)) {
00503         setBaseline( getMainChild()->getHeight() + distY / 2 + context.axisHeight( tstyle, factor ) );
00504     }
00505     else {
00506         setBaseline( height/2 + context.axisHeight( tstyle, factor ) );
00507     }
00508 }
00509 
00515 void MatrixElement::draw( QPainter& painter, const LuPixelRect& rect,
00516                           const ContextStyle& context,
00517                           ContextStyle::TextStyle tstyle,
00518                           ContextStyle::IndexStyle istyle,
00519                           StyleAttributes& style,
00520                           const LuPixelPoint& parentOrigin )
00521 {
00522     LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
00523     //if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( rect ) )
00524     //    return;
00525 
00526     uint rows = getRows();
00527     uint columns = getColumns();
00528 
00529     for (uint r = 0; r < rows; r++) {
00530         for (uint c = 0; c < columns; c++) {
00531             getElement(r, c)->draw(painter, rect, context,
00532                                    context.convertTextStyleFraction(tstyle),
00533                                    context.convertIndexStyleUpper(istyle),
00534                                    style,
00535                                    myPos);
00536         }
00537     }
00538 
00539     // Debug
00540     //painter.setPen(Qt::red);
00541     //painter.drawRect(myPos.x(), myPos.y(), getWidth(), getHeight());
00542 }
00543 
00544 
00545 void MatrixElement::dispatchFontCommand( FontCommand* cmd )
00546 {
00547     uint rows = getRows();
00548     uint columns = getColumns();
00549 
00550     for (uint r = 0; r < rows; r++) {
00551         for (uint c = 0; c < columns; c++) {
00552             getElement(r, c)->dispatchFontCommand( cmd );
00553         }
00554     }
00555 }
00556 
00557 
00558 // navigation
00559 //
00560 // The elements are responsible to handle cursor movement themselves.
00561 // To do this they need to know the direction the cursor moves and
00562 // the element it comes from.
00563 //
00564 // The cursor might be in normal or in selection mode.
00565 
00571 void MatrixElement::moveLeft(FormulaCursor* cursor, BasicElement* from)
00572 {
00573     if (cursor->isSelectionMode()) {
00574         getParent()->moveLeft(cursor, this);
00575     }
00576     else {
00577         if (from == getParent()) {
00578             getElement(getRows()-1, getColumns()-1)->moveLeft(cursor, this);
00579         }
00580         else {
00581             bool linear = cursor->getLinearMovement();
00582             uint row = 0;
00583             uint column = 0;
00584             if (searchElement(from, row, column)) {
00585                 if (column > 0) {
00586                     getElement(row, column-1)->moveLeft(cursor, this);
00587                 }
00588                 else if (linear && (row > 0)) {
00589                     getElement(row-1, getColumns()-1)->moveLeft(cursor, this);
00590                 }
00591                 else {
00592                     getParent()->moveLeft(cursor, this);
00593                 }
00594             }
00595             else {
00596                 getParent()->moveLeft(cursor, this);
00597             }
00598         }
00599     }
00600 }
00601 
00607 void MatrixElement::moveRight(FormulaCursor* cursor, BasicElement* from)
00608 {
00609     if (cursor->isSelectionMode()) {
00610         getParent()->moveRight(cursor, this);
00611     }
00612     else {
00613         if (from == getParent()) {
00614             getElement(0, 0)->moveRight(cursor, this);
00615         }
00616         else {
00617             bool linear = cursor->getLinearMovement();
00618             uint row = 0;
00619             uint column = 0;
00620             if (searchElement(from, row, column)) {
00621                 if (column < getColumns()-1) {
00622                     getElement(row, column+1)->moveRight(cursor, this);
00623                 }
00624                 else if (linear && (row < getRows()-1)) {
00625                     getElement(row+1, 0)->moveRight(cursor, this);
00626                 }
00627                 else {
00628                     getParent()->moveRight(cursor, this);
00629                 }
00630             }
00631             else {
00632                 getParent()->moveRight(cursor, this);
00633             }
00634         }
00635     }
00636 }
00637 
00643 void MatrixElement::moveUp(FormulaCursor* cursor, BasicElement* from)
00644 {
00645     if (cursor->isSelectionMode()) {
00646         getParent()->moveUp(cursor, this);
00647     }
00648     else {
00649         if (from == getParent()) {
00650             getElement(0, 0)->moveRight(cursor, this);
00651         }
00652         else {
00653             uint row = 0;
00654             uint column = 0;
00655             if (searchElement(from, row, column)) {
00656                 if (row > 0) {
00657                     getElement(row-1, column)->moveRight(cursor, this);
00658                 }
00659                 else {
00660                     getParent()->moveUp(cursor, this);
00661                 }
00662             }
00663             else {
00664                 getParent()->moveUp(cursor, this);
00665             }
00666         }
00667     }
00668 }
00669 
00675 void MatrixElement::moveDown(FormulaCursor* cursor, BasicElement* from)
00676 {
00677     if (cursor->isSelectionMode()) {
00678         getParent()->moveDown(cursor, this);
00679     }
00680     else {
00681         if (from == getParent()) {
00682             getElement(0, 0)->moveRight(cursor, this);
00683         }
00684         else {
00685             uint row = 0;
00686             uint column = 0;
00687             if (searchElement(from, row, column)) {
00688                 if (row < getRows()-1) {
00689                     getElement(row+1, column)->moveRight(cursor, this);
00690                 }
00691                 else {
00692                     getParent()->moveDown(cursor, this);
00693                 }
00694             }
00695             else {
00696                 getParent()->moveDown(cursor, this);
00697             }
00698         }
00699     }
00700 }
00701 
00706 void MatrixElement::goInside(FormulaCursor* cursor)
00707 {
00708     getElement(0, 0)->goInside(cursor);
00709 }
00710 
00711 
00712 // If there is a main child we must provide the insert/remove semantics.
00713 SequenceElement* MatrixElement::getMainChild()
00714 {
00715     return content.at(0)->at(0);
00716 }
00717 
00718 void MatrixElement::selectChild(FormulaCursor* cursor, BasicElement* child)
00719 {
00720     uint rows = getRows();
00721     uint columns = getColumns();
00722     for (uint r = 0; r < rows; r++) {
00723         for (uint c = 0; c < columns; c++) {
00724             if (child == getElement(r, c)) {
00725                 cursor->setTo(this, r*columns+c);
00726             }
00727         }
00728     }
00729 }
00730 
00731 const MatrixSequenceElement* MatrixElement::getElement( uint row, uint column ) const
00732 {
00733     QPtrListIterator< QPtrList < MatrixSequenceElement > > rows( content );
00734     rows += row;
00735     if ( ! rows.current() )
00736         return 0;
00737 
00738     QPtrListIterator< MatrixSequenceElement > cols ( *rows.current() );
00739     cols += column;
00740     return cols.current();
00741 }
00742 
00743 
00744 bool MatrixElement::searchElement(BasicElement* element, uint& row, uint& column)
00745 {
00746     uint rows = getRows();
00747     uint columns = getColumns();
00748     for (uint r = 0; r < rows; r++) {
00749         for (uint c = 0; c < columns; c++) {
00750             if (element == getElement(r, c)) {
00751                 row = r;
00752                 column = c;
00753                 return true;
00754             }
00755         }
00756     }
00757     return false;
00758 }
00759 
00760 
00764 void MatrixElement::writeDom(QDomElement element)
00765 {
00766     BasicElement::writeDom(element);
00767 
00768     uint rows = getRows();
00769     uint cols = getColumns();
00770 
00771     element.setAttribute("ROWS", rows);
00772     element.setAttribute("COLUMNS", cols);
00773 
00774     QDomDocument doc = element.ownerDocument();
00775 
00776     for (uint r = 0; r < rows; r++) {
00777         for (uint c = 0; c < cols; c++) {
00778             QDomElement tmp = getElement(r,c)->getElementDom(doc);
00779             element.appendChild(tmp);
00780     }
00781         element.appendChild(doc.createComment("end of row"));
00782     }
00783 }
00784 
00789 bool MatrixElement::readAttributesFromDom(QDomElement element)
00790 {
00791     if (!BasicElement::readAttributesFromDom(element)) {
00792         return false;
00793     }
00794     uint rows = 0;
00795     QString rowStr = element.attribute("ROWS");
00796     if(!rowStr.isNull()) {
00797         rows = rowStr.toInt();
00798     }
00799     if (rows == 0) {
00800         kdWarning( DEBUGID ) << "Rows <= 0 in MatrixElement." << endl;
00801         return false;
00802     }
00803 
00804     QString columnStr = element.attribute("COLUMNS");
00805     uint cols = 0;
00806     if(!columnStr.isNull()) {
00807         cols = columnStr.toInt();
00808     }
00809     if (cols == 0) {
00810         kdWarning( DEBUGID ) << "Columns <= 0 in MatrixElement." << endl;
00811         return false;
00812     }
00813 
00814     content.clear();
00815     for (uint r = 0; r < rows; r++) {
00816         QPtrList< MatrixSequenceElement >* list = new QPtrList< MatrixSequenceElement >;
00817         list->setAutoDelete(true);
00818         content.append(list);
00819         for (uint c = 0; c < cols; c++) {
00820             MatrixSequenceElement* element = new MatrixSequenceElement(this);
00821             list->append(element);
00822     }
00823     }
00824     return true;
00825 }
00826 
00832 bool MatrixElement::readContentFromDom(QDomNode& node)
00833 {
00834     if (!BasicElement::readContentFromDom(node)) {
00835         return false;
00836     }
00837 
00838     uint rows = getRows();
00839     uint cols = getColumns();
00840 
00841     uint r = 0;
00842     uint c = 0;
00843     while ( !node.isNull() && r < rows ) {
00844         if ( node.isElement() ) {
00845             SequenceElement* element = getElement( r, c );
00846             QDomElement e = node.toElement();
00847             if ( !element->buildFromDom( e ) ) {
00848                 return false;
00849             }
00850             c++;
00851             if ( c == cols ) {
00852                 c = 0;
00853                 r++;
00854             }
00855         }
00856         node = node.nextSibling();
00857     }
00858     return true;
00859 }
00860 
00861 bool MatrixElement::readAttributesFromMathMLDom( const QDomElement& element )
00862 {
00863     if ( ! BasicElement::readAttributesFromMathMLDom( element ) ) {
00864         return false;
00865     }
00866 
00867     QString alignStr = element.attribute( "align" ).lower();
00868     if ( ! alignStr.isNull() ) {
00869         if ( alignStr.find( "top" ) != -1 ) {
00870             m_align = TopAlign;
00871         }
00872         else if ( alignStr.find( "bottom" ) != -1 ) {
00873             m_align = BottomAlign;
00874         }
00875         else if ( alignStr.find( "center" ) != -1 ) {
00876             m_align = CenterAlign;
00877         }
00878         else if ( alignStr.find( "baseline" ) != -1 ) {
00879             m_align = BaselineAlign;
00880         }
00881         else if ( alignStr.find( "axis" ) != -1 ) {
00882             m_align = AxisAlign;
00883         }
00884         int index = alignStr.findRev( ' ' );
00885         if ( index != -1 ) {
00886             m_rowNumber = alignStr.right( index + 1 ).toInt();
00887         }
00888     }
00889     QString rowalignStr = element.attribute( "rowalign" ).lower();
00890     if ( ! rowalignStr.isNull() ) {
00891         QStringList list = QStringList::split( ' ', rowalignStr );
00892         for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) {
00893             if ( *it == "top" ) {
00894                 m_rowAlign.append( TopAlign );
00895             }
00896             else if ( *it == "bottom" ) {
00897                 m_rowAlign.append( BottomAlign );
00898             }
00899             else if ( *it == "center" ) {
00900                 m_rowAlign.append( CenterAlign );
00901             }
00902             else if ( *it == "baseline" ) {
00903                 m_rowAlign.append( BaselineAlign );
00904             }
00905             else if ( *it == "axis" ) {
00906                 m_rowAlign.append( AxisAlign );
00907             }
00908         }
00909     }
00910     QString columnalignStr = element.attribute( "columnalign" ).lower();
00911     if ( ! columnalignStr.isNull() ) {
00912         QStringList list = QStringList::split( ' ', columnalignStr );
00913         for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) {
00914             if ( *it == "left" ) {
00915                 m_columnAlign.append( LeftHorizontalAlign );
00916             }
00917             else if ( *it == "center" ) {
00918                 m_columnAlign.append( CenterHorizontalAlign );
00919             }
00920             else if ( *it == "right" ) {
00921                 m_columnAlign.append( RightHorizontalAlign );
00922             }
00923         }
00924     }
00925     QString alignmentscopeStr = element.attribute( "alignmentscope" ).lower();
00926     if ( ! alignmentscopeStr.isNull() ) {
00927         QStringList list = QStringList::split( ' ', alignmentscopeStr );
00928         for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) {
00929             if ( *it == "true" ) {
00930                 m_alignmentScope.append( true );
00931             }
00932             else if ( *it == "false" ) {
00933                 m_alignmentScope.append( false );
00934             }
00935         }
00936     }
00937     QString columnwidthStr = element.attribute( "columnwidth" ).lower();
00938     if ( columnwidthStr.isNull() ) {
00939         QStringList list = QStringList::split( ' ', columnwidthStr );
00940         for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) {
00941             SizeType type = NoSize;
00942             double length;
00943             if ( *it == "auto" ) {
00944                 type = AutoSize;
00945             }
00946             else if ( *it == "fit" ) {
00947                 type = FitSize;
00948             }
00949             else {
00950                 length = getSize( columnwidthStr, &type );
00951                 if ( type == NoSize ) {
00952                     type = getSpace( columnwidthStr );
00953                 }
00954             }
00955             if ( type != NoSize ) {
00956                 m_columnWidthType.append( type );
00957                 if ( type == RelativeSize || type == AbsoluteSize || type == PixelSize ) {
00958                     m_columnWidth.append( length );
00959                 }
00960             }
00961         }
00962     }
00963     QString widthStr = element.attribute( "width" ).lower();
00964     if ( ! widthStr.isNull() ) {
00965         if ( widthStr == "auto" ) {
00966             m_widthType = AutoSize;
00967         }
00968         else {
00969             m_width = getSize( widthStr, &m_widthType );
00970         }
00971     }
00972     QString rowspacingStr = element.attribute( "rowspacing" ).lower();
00973     if ( ! rowspacingStr.isNull() ) {
00974         QStringList list = QStringList::split( ' ', rowspacingStr );
00975         for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) {
00976             SizeType type;
00977             double length = getSize( *it, &type );
00978             if ( type != NoSize ) {
00979                 m_rowSpacingType.append( type );
00980                 m_rowSpacing.append( length );
00981             }
00982         }
00983     }
00984     QString columnspacingStr = element.attribute( "columnspacing" ).lower();
00985     if ( ! columnspacingStr.isNull() ) {
00986         QStringList list = QStringList::split( ' ', columnspacingStr );
00987         for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) {
00988             SizeType type;
00989             double length = getSize( *it, &type );
00990             if ( type == NoSize ) {
00991                 type = getSpace( columnspacingStr );
00992             }
00993             if ( type != NoSize ) {
00994                 m_columnSpacingType.append( type );
00995                 if ( type == RelativeSize || type == AbsoluteSize || type == PixelSize ) {
00996                     m_columnSpacing.append( length );
00997                 }
00998             }
00999         }
01000     }
01001     QString rowlinesStr = element.attribute( "rowlines" ).lower();
01002     if ( ! rowlinesStr.isNull() ) {
01003         QStringList list = QStringList::split( ' ', rowlinesStr );
01004         for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) {
01005             if ( *it == "none" ) {
01006                 m_rowLines.append( NoneLine );
01007             }
01008             else if ( *it == "solid" ) {
01009                 m_rowLines.append( SolidLine );
01010             }
01011             else if ( *it == "dashed" ) {
01012                 m_rowLines.append( DashedLine );
01013             }
01014         }
01015     }
01016     QString columnlinesStr = element.attribute( "columnlines" ).lower();
01017     if ( ! columnlinesStr.isNull() ) {
01018         QStringList list = QStringList::split( ' ', columnlinesStr );
01019         for ( QStringList::iterator it = list.begin(); it != list.end(); it++ ) {
01020             if ( *it == "none" ) {
01021                 m_columnLines.append( NoneLine );
01022             }
01023             else if ( *it == "solid" ) {
01024                 m_columnLines.append( SolidLine );
01025             }
01026             else if ( *it == "dashed" ) {
01027                 m_columnLines.append( DashedLine );
01028             }
01029         }
01030     }
01031     QString frameStr = element.attribute( "frame" ).stripWhiteSpace().lower();
01032     if ( ! frameStr.isNull() ) {
01033         if ( frameStr == "none" ) {
01034             m_frame = NoneLine;
01035         }
01036         else if ( frameStr == "solid" ) {
01037             m_frame = SolidLine;
01038         }
01039         else if ( frameStr == "dashed" ) {
01040             m_frame = DashedLine;
01041         }
01042     }
01043     QString framespacingStr = element.attribute( "framespacing" );
01044     if ( ! framespacingStr.isNull() ) {
01045         QStringList list = QStringList::split( ' ', framespacingStr );
01046         m_frameHSpacing = getSize( list[0], &m_frameHSpacingType );
01047         if ( m_frameHSpacingType == NoSize ) {
01048             m_frameHSpacingType = getSpace( list[0] );
01049         }
01050         if ( list.count() > 1 ) {
01051             m_frameVSpacing = getSize( list[1], &m_frameVSpacingType );
01052             if ( m_frameVSpacingType == NoSize ) {
01053                 m_frameVSpacingType = getSpace( list[1] );
01054             }
01055         }
01056     }
01057     QString equalrowsStr = element.attribute( "equalrows" ).stripWhiteSpace().lower();
01058     if ( ! equalrowsStr.isNull() ) {
01059         m_customEqualRows = true;
01060         if ( equalrowsStr == "false" ) {
01061             m_equalRows = false;
01062         }
01063         else {
01064             m_equalRows = true;
01065         }
01066     }
01067     QString equalcolumnsStr = element.attribute( "equalcolumns" ).stripWhiteSpace().lower();
01068     if ( ! equalcolumnsStr.isNull() ) {
01069         m_customEqualColumns = true;
01070         if ( equalcolumnsStr == "false" ) {
01071             m_equalColumns = false;
01072         }
01073         else {
01074             m_equalColumns = true;
01075         }
01076     }
01077     QString displaystyleStr = element.attribute( "displaystyle" ).stripWhiteSpace().lower();
01078     if ( ! displaystyleStr.isNull() ) {
01079         m_customDisplayStyle = true;
01080         if ( displaystyleStr == "false" ) {
01081             m_displayStyle = false;
01082         }
01083         else {
01084             m_displayStyle = true;
01085         }
01086     }
01087     QString sideStr = element.attribute( "side" ).stripWhiteSpace().lower();
01088     if ( ! sideStr.isNull() ) {
01089         if ( sideStr == "left" ) {
01090             m_side = LeftSide;
01091         }
01092         else if ( sideStr == "right" ) {
01093             m_side = RightSide;
01094         }
01095         else if ( sideStr == "leftoverlap" ) {
01096             m_side = LeftOverlapSide;
01097         }
01098         else if ( sideStr == "rightoverlap" ) {
01099             m_side = RightOverlapSide;
01100         }
01101     }
01102     QString minlabelspacingStr = element.attribute( "minlabelspacing" ).stripWhiteSpace().lower();
01103     if ( ! minlabelspacingStr.isNull() ) {
01104         m_minLabelSpacing = getSize( minlabelspacingStr, &m_minLabelSpacingType );
01105         if ( m_minLabelSpacingType == NoSize ) {
01106             m_minLabelSpacingType = getSpace( minlabelspacingStr );
01107         }
01108     }
01109     return true;
01110 }
01111 
01118 int MatrixElement::readContentFromMathMLDom( QDomNode& node )
01119 {
01120     // We have twice, since there may be empty elements and we need to know how
01121     // many of them we have. So, first pass, get number of rows and columns
01122 
01123     if ( BasicElement::readContentFromMathMLDom( node ) == -1 ) {
01124         return -1;
01125     }
01126 
01127     uint rows = 0;
01128     uint cols = 0;
01129     QDomNode n = node;
01130     while ( !n.isNull() ) {
01131         if ( n.isElement() ) {
01132             QDomElement e = n.toElement();
01133             if ( e.tagName().lower() == "mtr" || e.tagName().lower() == "mlabeledtr" )
01134             {
01135                 rows++;
01136 
01137                 /* Determins the number of columns */
01138                 QDomNode cellnode = e.firstChild();
01139                 int cc = 0;
01140 
01141                 while ( !cellnode.isNull() ) {
01142                     if ( cellnode.isElement() )
01143                         cc++;
01144                     cellnode = cellnode.nextSibling();
01145                 }
01146                 if ( cc > 0 && e.tagName().lower() == "mlabeledtr" )
01147                     cc--;
01148                 if ( cc > cols )
01149                     cols = cc;
01150             }
01151         }
01152         n = n.nextSibling();
01153     }
01154 
01155     // Create elements
01156     content.clear();
01157     for (uint r = 0; r < rows; r++) {
01158         QPtrList< MatrixSequenceElement >* list = new QPtrList< MatrixSequenceElement >;
01159         list->setAutoDelete(true);
01160         content.append(list);
01161         for (uint c = 0; c < cols; c++) {
01162             MatrixSequenceElement* element = new MatrixSequenceElement(this);
01163             list->append(element);
01164         }
01165     }
01166 
01167     // Second pass, read elements now
01168     uint r = 0;
01169     uint c = 0;
01170     while ( !node.isNull() ) {
01171         if ( node.isElement() ) {
01172             QDomElement e = node.toElement();
01173             if ( e.tagName().lower() == "mtr" || e.tagName().lower() == "mlabeledtr" ) {
01174                 QDomNode cellnode = e.firstChild();
01175                 if ( e.tagName().lower() == "mlabeledtr" ) {
01176                     while ( ! cellnode.isNull() && ! cellnode.isElement() )
01177                         cellnode = cellnode.nextSibling();
01178                     if ( ! cellnode.isNull() )
01179                         cellnode = cellnode.nextSibling();
01180                 }
01181                 while ( !cellnode.isNull() ) {
01182                     if ( cellnode.isElement() ) {
01183                         QDomElement cellelement = cellnode.toElement();
01184                         if ( cellelement.tagName().lower() != "mtd" ) {
01185                             // TODO: Inferred mtd. Deprecated in MathML 2.0
01186                             kdWarning( DEBUGID ) << "Unsupported tag " 
01187                                                  << cellelement.tagName()
01188                                                  << " inside matrix row\n";
01189                         }
01190                         else {
01191                             SequenceElement* element = getElement(r, c);
01192                             if ( element->buildFromMathMLDom( cellelement ) == -1 )
01193                                 return -1;
01194                             c++;
01195                         }
01196                     }
01197                     cellnode = cellnode.nextSibling();
01198                 }
01199                 c = 0;
01200                 r++;
01201             }
01202         }
01203         node = node.nextSibling();
01204     }
01205     return 1;
01206 }
01207 
01208 QString MatrixElement::toLatex()
01209 {
01210     //All the border handling must be implemented here too
01211 
01212     QString matrix;
01213     uint cols=getColumns();
01214     uint rows=getRows();
01215 
01216     matrix="\\begin{array}{ ";
01217     for(uint i=0;i<cols;i++)
01218     matrix+="c ";
01219 
01220     matrix+="} ";
01221 
01222     for (uint r = 0; r < rows; r++) {
01223         for (uint c = 0; c < cols; c++) {
01224             matrix+=getElement(r, c)->toLatex();
01225         if( c < cols-1)    matrix+=" & ";
01226         }
01227         if(r < rows-1 ) matrix+=" \\\\ ";
01228     }
01229 
01230     matrix+=" \\end{array}";
01231 
01232     return matrix;
01233 }
01234 
01235 QString MatrixElement::formulaString()
01236 {
01237     QString matrix = "[";
01238     uint cols=getColumns();
01239     uint rows=getRows();
01240     for (uint r = 0; r < rows; r++) {
01241         matrix += "[";
01242         for (uint c = 0; c < cols; c++) {
01243             matrix+=getElement(r, c)->formulaString();
01244         if ( c < cols-1 ) matrix+=", ";
01245         }
01246         matrix += "]";
01247         if ( r < rows-1 ) matrix += ", ";
01248     }
01249     matrix += "]";
01250     return matrix;
01251 }
01252 
01253 
01254 SequenceElement* MatrixElement::elementAt(uint row, uint column)
01255 {
01256     return getElement( row, column );
01257 }
01258 
01259 void MatrixElement::writeMathMLAttributes( QDomElement& element ) const
01260 {
01261     QString rownumber;
01262     if ( m_rowNumber ) {
01263         rownumber = QString( " %1" ).arg( m_rowNumber );
01264     }
01265     switch ( m_align ) {
01266     case TopAlign:
01267         element.setAttribute( "align", "top" + rownumber );
01268         break;
01269     case BottomAlign:
01270         element.setAttribute( "align", "bottom" + rownumber );
01271         break;
01272     case CenterAlign:
01273         element.setAttribute( "align", "center" + rownumber );
01274         break;
01275     case BaselineAlign:
01276         element.setAttribute( "align", "baseline" + rownumber );
01277         break;
01278     case AxisAlign:
01279         element.setAttribute( "align", "axis" + rownumber );
01280         break;
01281     default:
01282         break;
01283     }
01284     QString rowalign;
01285     for ( QValueList< VerticalAlign >::const_iterator it = m_rowAlign.begin(); it != m_rowAlign.end(); it++ )
01286     {
01287         switch ( *it ) {
01288         case TopAlign:
01289             rowalign.append( "top " );
01290             break;
01291         case BottomAlign:
01292             rowalign.append( "bottom " );
01293             break;
01294         case CenterAlign:
01295             rowalign.append( "center " );
01296             break;
01297         case BaselineAlign:
01298             rowalign.append( "baseline " );
01299             break;
01300         case AxisAlign:
01301             rowalign.append( "axis " );
01302             break;
01303         default:
01304             break;
01305         }
01306     }
01307     if ( ! rowalign.isNull() ) {
01308         element.setAttribute( "rowalign", rowalign.stripWhiteSpace() );
01309     }
01310     QString columnalign;
01311     for ( QValueList< HorizontalAlign >::const_iterator it = m_columnAlign.begin(); it != m_columnAlign.end(); it++ )
01312     {
01313         switch ( *it ) {
01314         case LeftHorizontalAlign:
01315             rowalign.append( "left " );
01316             break;
01317         case CenterHorizontalAlign:
01318             rowalign.append( "center " );
01319             break;
01320         case RightHorizontalAlign:
01321             rowalign.append( "right " );
01322             break;
01323         default:
01324             break;
01325         }
01326     }
01327     if ( ! columnalign.isNull() ) {
01328         element.setAttribute( "columnalign", columnalign.stripWhiteSpace() );
01329     }
01330     QString alignmentscope;
01331     for ( QValueList< bool >::const_iterator it = m_alignmentScope.begin(); it != m_alignmentScope.end(); it++ )
01332     {
01333         if ( *it ) {
01334             alignmentscope.append( "true " );
01335         }
01336         else {
01337             alignmentscope.append( "false " );
01338         }
01339     }
01340     if ( ! alignmentscope.isNull() ) {
01341         element.setAttribute( "alignmentscope", alignmentscope.stripWhiteSpace() );
01342     }
01343     QString columnwidth;
01344     QValueList< double >::const_iterator lengthIt = m_columnWidth.begin();
01345     for ( QValueList< SizeType >::const_iterator typeIt = m_columnWidthType.begin();
01346           typeIt != m_columnWidthType.end(); typeIt ++ ) {
01347         switch ( *typeIt ) {
01348         case AutoSize:
01349             columnwidth.append( "auto " );
01350             break;
01351         case FitSize:
01352             columnwidth.append( "fit " );
01353             break;
01354         case AbsoluteSize:
01355             columnwidth.append( QString( "%1pt " ).arg( *lengthIt ) );
01356             lengthIt++;
01357             break;
01358         case RelativeSize:
01359             columnwidth.append( QString( "%1% " ).arg( *lengthIt * 100.0 ) );
01360             lengthIt++;
01361             break;
01362         case PixelSize:
01363             columnwidth.append( QString( "%1px " ).arg( *lengthIt ) );
01364             lengthIt++;
01365             break;
01366         case NegativeVeryVeryThinMathSpace:
01367             columnwidth.append( "negativeveryverythinmathspace " );
01368             break;
01369         case NegativeVeryThinMathSpace:
01370             columnwidth.append( "negativeverythinmathspace " );
01371             break;
01372         case NegativeThinMathSpace:
01373             columnwidth.append( "negativethinmathspace " );
01374             break;
01375         case NegativeMediumMathSpace:
01376             columnwidth.append( "negativemediummathspace " );
01377             break;
01378         case NegativeThickMathSpace:
01379             columnwidth.append( "negativethickmathspace " );
01380             break;
01381         case NegativeVeryThickMathSpace:
01382             columnwidth.append( "negativeverythickmathspace " );
01383             break;
01384         case NegativeVeryVeryThickMathSpace:
01385             columnwidth.append( "negativeveryverythickmathspace " );
01386             break;
01387         case VeryVeryThinMathSpace:
01388             columnwidth.append( "veryverythinmathspace " );
01389             break;
01390         case VeryThinMathSpace:
01391             columnwidth.append( "verythinmathspace " );
01392             break;
01393         case ThinMathSpace:
01394             columnwidth.append( "thinmathspace " );
01395             break;
01396         case MediumMathSpace:
01397             columnwidth.append( "mediummathspace " );
01398             break;
01399         case ThickMathSpace:
01400             columnwidth.append( "thickmathspace " );
01401             break;
01402         case VeryThickMathSpace:
01403             columnwidth.append( "verythickmathspace " );
01404             break;
01405         case VeryVeryThickMathSpace:
01406             columnwidth.append( "veryverythickmathspace " );
01407             break;
01408         default:
01409             break;
01410         }
01411     }
01412     if ( ! columnwidth.isNull() ) {
01413         element.setAttribute( "columnwidth", columnwidth.stripWhiteSpace() );
01414     }
01415     switch ( m_widthType ) {
01416     case AutoSize:
01417         element.setAttribute( "width", "auto" );
01418         break;
01419     case AbsoluteSize:
01420         element.setAttribute( "width", QString( "%1pt" ).arg( m_width ) );
01421         break;
01422     case RelativeSize:
01423         element.setAttribute( "width", QString( "%1% " ).arg( m_width * 100.0 ) );
01424         break;
01425     case PixelSize:
01426         element.setAttribute( "width", QString( "%1px " ).arg( m_width ) );
01427         break;
01428     default:
01429         break;
01430     }
01431     QString rowspacing;
01432     lengthIt = m_rowSpacing.begin();
01433     for ( QValueList< SizeType >::const_iterator typeIt = m_rowSpacingType.begin();
01434           typeIt != m_rowSpacingType.end(); typeIt++, lengthIt++ ) {
01435         switch ( *typeIt ) {
01436         case AbsoluteSize:
01437             rowspacing.append( QString( "%1pt " ).arg( *lengthIt ) );
01438             break;
01439         case RelativeSize:
01440             rowspacing.append( QString( "%1% " ).arg( *lengthIt * 100.0 ) );
01441             break;
01442         case PixelSize:
01443             rowspacing.append( QString( "%1px " ).arg( *lengthIt ) );
01444             break;
01445         default:
01446             break;
01447         }
01448     }
01449     if ( ! rowspacing.isNull() ) {
01450         element.setAttribute( "rowspacing", rowspacing.stripWhiteSpace() );
01451     }
01452     QString columnspacing;
01453     lengthIt = m_columnSpacing.begin(); 
01454     for ( QValueList< SizeType >::const_iterator typeIt = m_columnSpacingType.begin();
01455           typeIt != m_columnSpacingType.end(); typeIt++ ) {
01456         switch ( *typeIt ) {
01457         case AbsoluteSize:
01458             columnspacing.append( QString( "%1pt " ).arg( *lengthIt ) );
01459             lengthIt++;
01460             break;
01461         case RelativeSize:
01462             columnspacing.append( QString( "%1% " ).arg( *lengthIt * 100.0 ) );
01463             lengthIt++;
01464             break;
01465         case PixelSize:
01466             columnspacing.append( QString( "%1px " ).arg( *lengthIt ) );
01467             lengthIt++;
01468             break;
01469         case NegativeVeryVeryThinMathSpace:
01470             columnspacing.append( "negativeveryverythinmathspace " );
01471             break;
01472         case NegativeVeryThinMathSpace:
01473             columnspacing.append( "negativeverythinmathspace " );
01474             break;
01475         case NegativeThinMathSpace:
01476             columnspacing.append( "negativethinmathspace " );
01477             break;
01478         case NegativeMediumMathSpace:
01479             columnspacing.append( "negativemediummathspace " );
01480             break;
01481         case NegativeThickMathSpace:
01482             columnspacing.append( "negativethickmathspace " );
01483             break;
01484         case NegativeVeryThickMathSpace:
01485             columnspacing.append( "negativeverythickmathspace " );
01486             break;
01487         case NegativeVeryVeryThickMathSpace:
01488             columnspacing.append( "negativeveryverythickmathspace " );
01489             break;
01490         case VeryVeryThinMathSpace:
01491             columnspacing.append( "veryverythinmathspace " );
01492             break;
01493         case VeryThinMathSpace:
01494             columnspacing.append( "verythinmathspace " );
01495             break;
01496         case ThinMathSpace:
01497             columnspacing.append( "thinmathspace " );
01498             break;
01499         case MediumMathSpace:
01500             columnspacing.append( "mediummathspace " );
01501             break;
01502         case ThickMathSpace:
01503             columnspacing.append( "thickmathspace " );
01504             break;
01505         case VeryThickMathSpace:
01506             columnspacing.append( "verythickmathspace " );
01507             break;
01508         case VeryVeryThickMathSpace:
01509             columnspacing.append( "veryverythickmathspace " );
01510             break;
01511         default:
01512             break;
01513         }
01514     }
01515     if ( ! rowspacing.isNull() ) {
01516         element.setAttribute( "rowspacing", rowspacing.stripWhiteSpace() );
01517     }
01518     QString rowlines;
01519     for ( QValueList< LineType >::const_iterator it = m_rowLines.begin(); it != m_rowLines.end(); it++ )
01520     {
01521         switch ( *it ) {
01522         case NoneLine:
01523             rowlines.append( "none " );
01524             break;
01525         case SolidLine:
01526             rowlines.append( "solid " );
01527             break;
01528         case DashedLine:
01529             rowlines.append( "dashed " );
01530             break;
01531         default:
01532             break;
01533         }
01534     }
01535     if ( ! rowlines.isNull() ) {
01536         element.setAttribute( "rowlines", rowlines.stripWhiteSpace() );
01537     }
01538     QString columnlines;
01539     for ( QValueList< LineType >::const_iterator it = m_columnLines.begin(); it != m_columnLines.end(); it++ )
01540     {
01541         switch ( *it ) {
01542         case NoneLine:
01543             columnlines.append( "none " );
01544             break;
01545         case SolidLine:
01546             columnlines.append( "solid " );
01547             break;
01548         case DashedLine:
01549             columnlines.append( "dashed " );
01550             break;
01551         default:
01552             break;
01553         }
01554     }
01555     if ( ! columnlines.isNull() ) {
01556         element.setAttribute( "columnlines", columnlines.stripWhiteSpace() );
01557     }
01558     switch ( m_frame ) {
01559     case NoneLine:
01560         element.setAttribute( "frame", "none" );
01561         break;
01562     case SolidLine:
01563         element.setAttribute( "frame", "solid" );
01564         break;
01565     case DashedLine:
01566         element.setAttribute( "frame", "dashed" );
01567         break;
01568     default:
01569         break;
01570     }
01571     QString framespacing;
01572     switch ( m_frameHSpacingType ) {
01573     case AbsoluteSize:
01574         framespacing.append( QString( "%1pt " ).arg( m_frameHSpacing ) );
01575         break;
01576     case RelativeSize:
01577         framespacing.append( QString( "%1% " ).arg( m_frameHSpacing * 100.0 ) );
01578         break;
01579     case PixelSize:
01580         framespacing.append( QString( "%1px " ).arg( m_frameHSpacing ) );
01581         break;
01582     case NegativeVeryVeryThinMathSpace:
01583         framespacing.append( "negativeveryverythinmathspace " );
01584         break;
01585     case NegativeVeryThinMathSpace:
01586         framespacing.append( "negativeverythinmathspace " );
01587         break;
01588     case NegativeThinMathSpace:
01589         framespacing.append( "negativethinmathspace " );
01590         break;
01591     case NegativeMediumMathSpace:
01592         framespacing.append( "negativemediummathspace " );
01593         break;
01594     case NegativeThickMathSpace:
01595         framespacing.append( "negativethickmathspace " );
01596         break;
01597     case NegativeVeryThickMathSpace:
01598         framespacing.append( "negativeverythickmathspace " );
01599         break;
01600     case NegativeVeryVeryThickMathSpace:
01601         framespacing.append( "negativeveryverythickmathspace " );
01602         break;
01603     case VeryVeryThinMathSpace:
01604         framespacing.append( "veryverythinmathspace " );
01605         break;
01606     case VeryThinMathSpace:
01607         framespacing.append( "verythinmathspace " );
01608         break;
01609     case ThinMathSpace:
01610         framespacing.append( "thinmathspace " );
01611         break;
01612     case MediumMathSpace:
01613         framespacing.append( "mediummathspace " );
01614         break;
01615     case ThickMathSpace:
01616         framespacing.append( "thickmathspace " );
01617         break;
01618     case VeryThickMathSpace:
01619         framespacing.append( "verythickmathspace " );
01620         break;
01621     case VeryVeryThickMathSpace:
01622         framespacing.append( "veryverythickmathspace " );
01623         break;
01624     default:
01625         break;
01626     }
01627     switch ( m_frameVSpacingType ) {
01628     case AbsoluteSize:
01629         framespacing.append( QString( "%1pt " ).arg( m_frameVSpacing ) );
01630         break;
01631     case RelativeSize:
01632         framespacing.append( QString( "%1% " ).arg( m_frameVSpacing * 100.0 ) );
01633         break;
01634     case PixelSize:
01635         framespacing.append( QString( "%1px " ).arg( m_frameVSpacing ) );
01636         break;
01637     case NegativeVeryVeryThinMathSpace:
01638         framespacing.append( "negativeveryverythinmathspace " );
01639         break;
01640     case NegativeVeryThinMathSpace:
01641         framespacing.append( "negativeverythinmathspace " );
01642         break;
01643     case NegativeThinMathSpace:
01644         framespacing.append( "negativethinmathspace " );
01645         break;
01646     case NegativeMediumMathSpace:
01647         framespacing.append( "negativemediummathspace " );
01648         break;
01649     case NegativeThickMathSpace:
01650         framespacing.append( "negativethickmathspace " );
01651         break;
01652     case NegativeVeryThickMathSpace:
01653         framespacing.append( "negativeverythickmathspace " );
01654         break;
01655     case NegativeVeryVeryThickMathSpace:
01656         framespacing.append( "negativeveryverythickmathspace " );
01657         break;
01658     case VeryVeryThinMathSpace:
01659         framespacing.append( "veryverythinmathspace " );
01660         break;
01661     case VeryThinMathSpace:
01662         framespacing.append( "verythinmathspace " );
01663         break;
01664     case ThinMathSpace:
01665         framespacing.append( "thinmathspace " );
01666         break;
01667     case MediumMathSpace:
01668         framespacing.append( "mediummathspace " );
01669         break;
01670     case ThickMathSpace:
01671         framespacing.append( "thickmathspace " );
01672         break;
01673     case VeryThickMathSpace:
01674         framespacing.append( "verythickmathspace " );
01675         break;
01676     case VeryVeryThickMathSpace:
01677         framespacing.append( "veryverythickmathspace " );
01678         break;
01679     default:
01680         break;
01681     }
01682     if ( ! framespacing.isNull() ) {
01683         element.setAttribute( "framespacing", framespacing.stripWhiteSpace() );
01684     }
01685     if ( m_customEqualRows ) {
01686         element.setAttribute( "equalrows", m_equalRows ? "true" : "false" );
01687     }
01688     if ( m_customEqualColumns ) {
01689         element.setAttribute( "equalcolumns", m_equalColumns ? "true" : "false" );
01690     }
01691     if ( m_customDisplayStyle ) {
01692         element.setAttribute( "displaystyle", m_displayStyle ? "true" : "false" );
01693     }
01694     switch ( m_side ) {
01695     case LeftSide:
01696         element.setAttribute( "side", "left" );
01697         break;
01698     case RightSide:
01699         element.setAttribute( "side", "right" );
01700         break;
01701     case LeftOverlapSide:
01702         element.setAttribute( "side", "leftoverlap" );
01703         break;
01704     case RightOverlapSide:
01705         element.setAttribute( "side", "rightoverlap" );
01706         break;
01707     default:
01708         break;
01709     }
01710     switch ( m_minLabelSpacingType ) {
01711     case AbsoluteSize:
01712         element.setAttribute( "minlabelspacing", QString( "%1pt" ).arg( m_minLabelSpacing ) );
01713         break;
01714     case RelativeSize:
01715         element.setAttribute( "minlabelspacing", QString( "%1%" ).arg( m_minLabelSpacing * 100.0 ) );
01716         break;
01717     case PixelSize:
01718         element.setAttribute( "minlabelspacing", QString( "%1px" ).arg( m_minLabelSpacing ) );
01719         break;
01720     case NegativeVeryVeryThinMathSpace:
01721         element.setAttribute( "minlabelspacing", "negativeveryverythinmathspace" );
01722         break;
01723     case NegativeVeryThinMathSpace:
01724         element.setAttribute( "minlabelspacing", "negativeverythinmathspace" );
01725         break;
01726     case NegativeThinMathSpace:
01727         element.setAttribute( "minlabelspacing", "negativethinmathspace" );
01728         break;
01729     case NegativeMediumMathSpace:
01730         element.setAttribute( "minlabelspacing", "negativemediummathspace" );
01731         break;
01732     case NegativeThickMathSpace:
01733         element.setAttribute( "minlabelspacing", "negativethickmathspace" );
01734         break;
01735     case NegativeVeryThickMathSpace:
01736         element.setAttribute( "minlabelspacing", "negativeverythickmathspace" );
01737         break;
01738     case NegativeVeryVeryThickMathSpace:
01739         element.setAttribute( "minlabelspacing", "negativeveryverythickmathspace" );
01740         break;
01741     case VeryVeryThinMathSpace:
01742         element.setAttribute( "minlabelspacing", "veryverythinmathspace" );
01743         break;
01744     case VeryThinMathSpace:
01745         element.setAttribute( "minlabelspacing", "verythinmathspace" );
01746         break;
01747     case ThinMathSpace:
01748         element.setAttribute( "minlabelspacing", "thinmathspace" );
01749         break;
01750     case MediumMathSpace:
01751         element.setAttribute( "minlabelspacing", "mediummathspace" );
01752         break;
01753     case ThickMathSpace:
01754         element.setAttribute( "minlabelspacing", "thickmathspace" );
01755         break;
01756     case VeryThickMathSpace:
01757         element.setAttribute( "minlabelspacing", "verythickmathspace" );
01758         break;
01759     case VeryVeryThickMathSpace:
01760         element.setAttribute( "minlabelspacing", "veryverythickmathspace" );
01761         break;
01762     default:
01763         break;
01764     }
01765 }
01766 
01767 void MatrixElement::writeMathMLContent( QDomDocument& doc, 
01768                                         QDomElement& element,
01769                                         bool oasisFormat ) const
01770 {
01771     QDomElement row;
01772     QDomElement cell;
01773 
01774     uint rows = getRows();
01775     uint cols = getColumns();
01776 
01777     for ( uint r = 0; r < rows; r++ )
01778     {
01779         row = doc.createElement( oasisFormat ? "math:mtr" : "mtr" );
01780         element.appendChild( row );
01781         for ( uint c = 0; c < cols; c++ )
01782         {
01783             cell = doc.createElement( oasisFormat ? "math:mtd" : "mtd" );
01784             row.appendChild( cell );
01785             getElement(r,c)->writeMathML( doc, cell, oasisFormat );
01786         }
01787     }
01788 }
01789 
01790 
01792 
01793 
01798 class MultilineSequenceElement : public SequenceElement {
01799     typedef SequenceElement inherited;
01800 public:
01801 
01802     MultilineSequenceElement( BasicElement* parent = 0 );
01803 
01804     virtual MultilineSequenceElement* clone() {
01805         return new MultilineSequenceElement( *this );
01806     }
01807 
01808     virtual BasicElement* goToPos( FormulaCursor*, bool& handled,
01809                                    const LuPixelPoint& point, const LuPixelPoint& parentOrigin );
01810 
01815     virtual void calcSizes( const ContextStyle& context,
01816                             ContextStyle::TextStyle tstyle,
01817                             ContextStyle::IndexStyle istyle,
01818                             StyleAttributes& style );
01819 
01820     virtual void registerTab( BasicElement* tab );
01821 
01830     virtual KCommand* buildCommand( Container*, Request* );
01831 
01832     virtual KCommand* input( Container* container, QKeyEvent* event );
01833 
01834     virtual KCommand* input( Container* container, QChar ch );
01835 
01836     uint tabCount() const { return tabs.count(); }
01837 
01838     BasicElement* tab( uint i ) { return tabs.at( i ); }
01839 
01841     void moveTabTo( uint i, luPixel pos );
01842 
01844     int tabBefore( uint pos );
01845 
01847     int tabPos( uint i );
01848 
01849     virtual void writeMathML( QDomDocument& doc, QDomNode& parent, bool oasisFormat = false ) const ;
01850 
01851 private:
01852 
01853     QPtrList<BasicElement> tabs;
01854 };
01855 
01856 
01857 // Split the line at position pos.
01858 class KFCNewLine : public Command {
01859 public:
01860     KFCNewLine( const QString& name, Container* document,
01861                 MultilineSequenceElement* line, uint pos );
01862 
01863     virtual ~KFCNewLine();
01864 
01865     virtual void execute();
01866     virtual void unexecute();
01867 
01868 private:
01869     MultilineSequenceElement* m_line;
01870     MultilineSequenceElement* m_newline;
01871     uint m_pos;
01872 };
01873 
01874 
01875 KFCNewLine::KFCNewLine( const QString& name, Container* document,
01876                         MultilineSequenceElement* line, uint pos )
01877     : Command( name, document ),
01878       m_line( line ), m_pos( pos )
01879 {
01880     m_newline = new MultilineSequenceElement( m_line->getParent() );
01881 }
01882 
01883 
01884 KFCNewLine::~KFCNewLine()
01885 {
01886     delete m_newline;
01887 }
01888 
01889 
01890 void KFCNewLine::execute()
01891 {
01892     FormulaCursor* cursor = getExecuteCursor();
01893     MultilineElement* parent = static_cast<MultilineElement*>( m_line->getParent() );
01894     int linePos = parent->content.find( m_line );
01895     parent->content.insert( linePos+1, m_newline );
01896 
01897     // If there are children to be moved.
01898     if ( m_line->countChildren() > static_cast<int>( m_pos ) ) {
01899 
01900         // Remove anything after position pos from the current line
01901         m_line->selectAllChildren( cursor );
01902         cursor->setMark( m_pos );
01903         QPtrList<BasicElement> elementList;
01904         m_line->remove( cursor, elementList, beforeCursor );
01905 
01906         // Insert the removed stuff into the new line
01907         m_newline->goInside( cursor );
01908         m_newline->insert( cursor, elementList, beforeCursor );
01909         cursor->setPos( cursor->getMark() );
01910     }
01911     else {
01912         m_newline->goInside( cursor );
01913     }
01914 
01915     // The command no longer owns the new line.
01916     m_newline = 0;
01917 
01918     // Tell that something changed
01919     FormulaElement* formula = m_line->formula();
01920     formula->changed();
01921     testDirty();
01922 }
01923 
01924 
01925 void KFCNewLine::unexecute()
01926 {
01927     FormulaCursor* cursor = getExecuteCursor();
01928     MultilineElement* parent = static_cast<MultilineElement*>( m_line->getParent() );
01929     int linePos = parent->content.find( m_line );
01930 
01931     // Now the command owns the new line again.
01932     m_newline = parent->content.at( linePos+1 );
01933 
01934     // Tell all cursors to leave this sequence
01935     FormulaElement* formula = m_line->formula();
01936     formula->elementRemoval( m_newline );
01937 
01938     // If there are children to be moved.
01939     if ( m_newline->countChildren() > 0 ) {
01940 
01941         // Remove anything from the line to be deleted
01942         m_newline->selectAllChildren( cursor );
01943         QPtrList<BasicElement> elementList;
01944         m_newline->remove( cursor, elementList, beforeCursor );
01945 
01946         // Insert the removed stuff into the previous line
01947         m_line->moveEnd( cursor );
01948         m_line->insert( cursor, elementList, beforeCursor );
01949         cursor->setPos( cursor->getMark() );
01950     }
01951     else {
01952         m_line->moveEnd( cursor );
01953     }
01954     parent->content.take( linePos+1 );
01955 
01956     // Tell that something changed
01957     formula->changed();
01958     testDirty();
01959 }
01960 
01961 
01962 MultilineSequenceElement::MultilineSequenceElement( BasicElement* parent )
01963     : SequenceElement( parent )
01964 {
01965     tabs.setAutoDelete( false );
01966 }
01967 
01968 
01969 BasicElement* MultilineSequenceElement::goToPos( FormulaCursor* cursor, bool& handled,
01970                                                  const LuPixelPoint& point, const LuPixelPoint& parentOrigin )
01971 {
01972     //LuPixelPoint myPos(parentOrigin.x() + getX(),
01973     //                   parentOrigin.y() + getY());
01974     BasicElement* e = inherited::goToPos(cursor, handled, point, parentOrigin);
01975 
01976     if (e == 0) {
01977         // If the mouse was behind this line put the cursor to the last position.
01978         if ( ( point.x() > getX()+getWidth() ) &&
01979              ( point.y() >= getY() ) &&
01980              ( point.y() < getY()+getHeight() ) ) {
01981             cursor->setTo(this, countChildren());
01982             handled = true;
01983             return this;
01984         }
01985     }
01986     return e;
01987 }
01988 
01989 
01990 void MultilineSequenceElement::calcSizes( const ContextStyle& context,
01991                                           ContextStyle::TextStyle tstyle,
01992                                           ContextStyle::IndexStyle istyle,
01993                                           StyleAttributes& style )
01994 {
01995     tabs.clear();
01996     inherited::calcSizes( context, tstyle, istyle, style );
01997 }
01998 
01999 
02000 void MultilineSequenceElement::registerTab( BasicElement* tab )
02001 {
02002     tabs.append( tab );
02003 }
02004 
02005 
02006 KCommand* MultilineSequenceElement::buildCommand( Container* container, Request* request )
02007 {
02008     FormulaCursor* cursor = container->activeCursor();
02009     if ( cursor->isReadOnly() ) {
02010         return 0;
02011     }
02012 
02013     switch ( *request ) {
02014     case req_remove: {
02015         // Remove this line if its empty.
02016         // Remove the formula if this line was the only one.
02017         break;
02018     }
02019     case req_addNewline: {
02020         FormulaCursor* cursor = container->activeCursor();
02021         return new KFCNewLine( i18n( "Add Newline" ), container, this, cursor->getPos() );
02022     }
02023     case req_addTabMark: {
02024         KFCReplace* command = new KFCReplace( i18n("Add Tabmark"), container );
02025         SpaceElement* element = new SpaceElement( THIN, true );
02026         command->addElement( element );
02027         return command;
02028     }
02029     default:
02030         break;
02031     }
02032     return inherited::buildCommand( container, request );
02033 }
02034 
02035 
02036 KCommand* MultilineSequenceElement::input( Container* container, QKeyEvent* event )
02037 {
02038     int action = event->key();
02039     //int state = event->state();
02040     //MoveFlag flag = movementFlag(state);
02041 
02042     switch ( action ) {
02043     case Qt::Key_Enter:
02044     case Qt::Key_Return: {
02045         Request newline( req_addNewline );
02046         return buildCommand( container, &newline );
02047     }
02048     case Qt::Key_Tab: {
02049         Request r( req_addTabMark );
02050         return buildCommand( container, &r );
02051     }
02052     }
02053     return inherited::input( container, event );
02054 }
02055 
02056 
02057 KCommand* MultilineSequenceElement::input( Container* container, QChar ch )
02058 {
02059     int latin1 = ch.latin1();
02060     switch (latin1) {
02061     case '&': {
02062         Request r( req_addTabMark );
02063         return buildCommand( container, &r );
02064     }
02065     }
02066     return inherited::input( container, ch );
02067 }
02068 
02069 
02070 void MultilineSequenceElement::moveTabTo( uint i, luPixel pos )
02071 {
02072     BasicElement* marker = tab( i );
02073     luPixel diff = pos - marker->getX();
02074     marker->setWidth( marker->getWidth() + diff );
02075 
02076     for ( int p = childPos( marker )+1; p < countChildren(); ++p ) {
02077         BasicElement* child = getChild( p );
02078         child->setX( child->getX() + diff );
02079     }
02080 
02081     setWidth( getWidth()+diff );
02082 }
02083 
02084 
02085 int MultilineSequenceElement::tabBefore( uint pos )
02086 {
02087     if ( tabs.isEmpty() ) {
02088         return -1;
02089     }
02090     uint tabNum = 0;
02091     for ( uint i=0; i<pos; ++i ) {
02092         BasicElement* child = getChild( i );
02093         if ( tabs.at( tabNum ) == child ) {
02094             if ( tabNum+1 == tabs.count() ) {
02095                 return tabNum;
02096             }
02097             ++tabNum;
02098         }
02099     }
02100     return static_cast<int>( tabNum )-1;
02101 }
02102 
02103 
02104 int MultilineSequenceElement::tabPos( uint i )
02105 {
02106     if ( i < tabs.count() ) {
02107         return childPos( tabs.at( i ) );
02108     }
02109     return -1;
02110 }
02111 
02112 
02113 void MultilineSequenceElement::writeMathML( QDomDocument& doc,
02114                                             QDomNode& parent, bool oasisFormat ) const
02115 {
02116     // parent is required to be a <mtr> tag
02117 
02118     QDomElement tmp = doc.createElement( "TMP" );
02119 
02120     inherited::writeMathML( doc, tmp, oasisFormat );
02121 
02122     /* Now we re-parse the Dom tree, because of the TabMarkers
02123      * that have no direct representation in MathML but mark the
02124      * end of a <mtd> tag.
02125      */
02126 
02127     QDomElement mtd = doc.createElement( oasisFormat ? "math:mtd" : "mtd" );
02128 
02129     // The mrow, if it exists.
02130     QDomNode n = tmp.firstChild().firstChild();
02131     while ( !n.isNull() ) {
02132         // the illegal TabMarkers are children of the mrow, child of tmp.
02133         if ( n.isElement() && n.toElement().tagName() == "TAB" ) {
02134             parent.appendChild( mtd );
02135             mtd = doc.createElement( oasisFormat ? "math:mtd" : "mtd" );
02136         }
02137         else {
02138             mtd.appendChild( n.cloneNode() ); // cloneNode needed?
02139         }
02140         n = n.nextSibling();
02141     }
02142 
02143     parent.appendChild( mtd );
02144 }
02145 
02146 
02147 MultilineElement::MultilineElement( BasicElement* parent )
02148     : BasicElement( parent )
02149 {
02150     content.setAutoDelete( true );
02151     content.append( new MultilineSequenceElement( this ) );
02152 }
02153 
02154 MultilineElement::~MultilineElement()
02155 {
02156 }
02157 
02158 MultilineElement::MultilineElement( const MultilineElement& other )
02159     : BasicElement( other )
02160 {
02161     content.setAutoDelete( true );
02162     uint count = other.content.count();
02163     for (uint i = 0; i < count; i++) {
02164         MultilineSequenceElement* line = content.at(i)->clone();
02165         line->setParent( this );
02166         content.append( line );
02167     }
02168 }
02169 
02170 
02171 bool MultilineElement::accept( ElementVisitor* visitor )
02172 {
02173     return visitor->visit( this );
02174 }
02175 
02176 
02177 void MultilineElement::entered( SequenceElement* /*child*/ )
02178 {
02179     formula()->tell( i18n( "Multi line element" ) );
02180 }
02181 
02182 
02186 BasicElement* MultilineElement::goToPos( FormulaCursor* cursor, bool& handled,
02187                                          const LuPixelPoint& point, const LuPixelPoint& parentOrigin )
02188 {
02189     BasicElement* e = inherited::goToPos(cursor, handled, point, parentOrigin);
02190     if ( e != 0 ) {
02191         LuPixelPoint myPos(parentOrigin.x() + getX(),
02192                            parentOrigin.y() + getY());
02193 
02194         uint count = content.count();
02195         for ( uint i = 0; i < count; ++i ) {
02196             MultilineSequenceElement* line = content.at(i);
02197             e = line->goToPos(cursor, handled, point, myPos);
02198             if (e != 0) {
02199                 return e;
02200             }
02201         }
02202         return this;
02203     }
02204     return 0;
02205 }
02206 
02207 void MultilineElement::goInside( FormulaCursor* cursor )
02208 {
02209     content.at( 0 )->goInside( cursor );
02210 }
02211 
02212 void MultilineElement::moveLeft( FormulaCursor* cursor, BasicElement* from )
02213 {
02214     // If you want to select more than one line you'll have to
02215     // select the whole element.
02216     if (cursor->isSelectionMode()) {
02217         getParent()->moveLeft(cursor, this);
02218     }
02219     else {
02220         // Coming from the parent (sequence) we go to
02221         // the very last position
02222         if (from == getParent()) {
02223             content.at( content.count()-1 )->moveLeft(cursor, this);
02224         }
02225         else {
02226             // Coming from one of the lines we go to the previous line
02227             // or to the parent if there is none.
02228             int pos = content.find( static_cast<MultilineSequenceElement*>( from ) );
02229             if ( pos > -1 ) {
02230                 if ( pos > 0 ) {
02231                     content.at( pos-1 )->moveLeft( cursor, this );
02232                 }
02233                 else {
02234                     getParent()->moveLeft(cursor, this);
02235                 }
02236             }
02237             else {
02238                 kdDebug( DEBUGID ) << k_funcinfo << endl;
02239                 kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl;
02240             }
02241         }
02242     }
02243 }
02244 
02245 void MultilineElement::moveRight( FormulaCursor* cursor, BasicElement* from )
02246 {
02247     if (cursor->isSelectionMode()) {
02248         getParent()->moveRight(cursor, this);
02249     }
02250     else {
02251         if (from == getParent()) {
02252             content.at( 0 )->moveRight(cursor, this);
02253         }
02254         else {
02255             int pos = content.find( static_cast<MultilineSequenceElement*>( from ) );
02256             if ( pos > -1 ) {
02257                 uint upos = pos;
02258                 if ( upos < content.count() ) {
02259                     if ( upos < content.count()-1 ) {
02260                         content.at( upos+1 )->moveRight( cursor, this );
02261                     }
02262                     else {
02263                         getParent()->moveRight(cursor, this);
02264                     }
02265                     return;
02266                 }
02267             }
02268             kdDebug( DEBUGID ) << k_funcinfo << endl;
02269             kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl;
02270         }
02271     }
02272 }
02273 
02274 void MultilineElement::moveUp( FormulaCursor* cursor, BasicElement* from )
02275 {
02276     // If you want to select more than one line you'll have to
02277     // select the whole element.
02278     if (cursor->isSelectionMode()) {
02279         getParent()->moveLeft(cursor, this);
02280     }
02281     else {
02282         // Coming from the parent (sequence) we go to
02283         // the very last position
02284         if (from == getParent()) {
02285             content.at( content.count()-1 )->moveLeft(cursor, this);
02286         }
02287         else {
02288             // Coming from one of the lines we go to the previous line
02289             // or to the parent if there is none.
02290             int pos = content.find( static_cast<MultilineSequenceElement*>( from ) );
02291             if ( pos > -1 ) {
02292                 if ( pos > 0 ) {
02293                     //content.at( pos-1 )->moveLeft( cursor, this );
02294                     // This is rather hackish.
02295                     // But we know what elements we have here.
02296                     int cursorPos = cursor->getPos();
02297                     MultilineSequenceElement* current = content.at( pos );
02298                     MultilineSequenceElement* newLine = content.at( pos-1 );
02299                     int tabNum = current->tabBefore( cursorPos );
02300                     if ( tabNum > -1 ) {
02301                         int oldTabPos = current->tabPos( tabNum );
02302                         int newTabPos = newLine->tabPos( tabNum );
02303                         if ( newTabPos > -1 ) {
02304                             cursorPos += newTabPos-oldTabPos;
02305                             int nextNewTabPos = newLine->tabPos( tabNum+1 );
02306                             if ( nextNewTabPos > -1 ) {
02307                                 cursorPos = QMIN( cursorPos, nextNewTabPos );
02308                             }
02309                         }
02310                         else {
02311                             cursorPos = newLine->countChildren();
02312                         }
02313                     }
02314                     else {
02315                         int nextNewTabPos = newLine->tabPos( 0 );
02316                         if ( nextNewTabPos > -1 ) {
02317                             cursorPos = QMIN( cursorPos, nextNewTabPos );
02318                         }
02319                     }
02320                     cursor->setTo( newLine,
02321                                    QMIN( cursorPos,
02322                                          newLine->countChildren() ) );
02323                 }
02324                 else {
02325                     getParent()->moveLeft(cursor, this);
02326                 }
02327             }
02328             else {
02329                 kdDebug( DEBUGID ) << k_funcinfo << endl;
02330                 kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl;
02331             }
02332         }
02333     }
02334 }
02335 
02336 void MultilineElement::moveDown( FormulaCursor* cursor, BasicElement* from )
02337 {
02338     if (cursor->isSelectionMode()) {
02339         getParent()->moveRight(cursor, this);
02340     }
02341     else {
02342         if (from == getParent()) {
02343             content.at( 0 )->moveRight(cursor, this);
02344         }
02345         else {
02346             int pos = content.find( static_cast<MultilineSequenceElement*>( from ) );
02347             if ( pos > -1 ) {
02348                 uint upos = pos;
02349                 if ( upos < content.count() ) {
02350                     if ( upos < content.count()-1 ) {
02351                         //content.at( upos+1 )->moveRight( cursor, this );
02352                         // This is rather hackish.
02353                         // But we know what elements we have here.
02354                         int cursorPos = cursor->getPos();
02355                         MultilineSequenceElement* current = content.at( upos );
02356                         MultilineSequenceElement* newLine = content.at( upos+1 );
02357                         int tabNum = current->tabBefore( cursorPos );
02358                         if ( tabNum > -1 ) {
02359                             int oldTabPos = current->tabPos( tabNum );
02360                             int newTabPos = newLine->tabPos( tabNum );
02361                             if ( newTabPos > -1 ) {
02362                                 cursorPos += newTabPos-oldTabPos;
02363                                 int nextNewTabPos = newLine->tabPos( tabNum+1 );
02364                                 if ( nextNewTabPos > -1 ) {
02365                                     cursorPos = QMIN( cursorPos, nextNewTabPos );
02366                                 }
02367                             }
02368                             else {
02369                                 cursorPos = newLine->countChildren();
02370                             }
02371                         }
02372                         else {
02373                             int nextNewTabPos = newLine->tabPos( 0 );
02374                             if ( nextNewTabPos > -1 ) {
02375                                 cursorPos = QMIN( cursorPos, nextNewTabPos );
02376                             }
02377                         }
02378                         cursor->setTo( newLine,
02379                                        QMIN( cursorPos,
02380                                              newLine->countChildren() ) );
02381                     }
02382                     else {
02383                         getParent()->moveRight(cursor, this);
02384                     }
02385                     return;
02386                 }
02387             }
02388             kdDebug( DEBUGID ) << k_funcinfo << endl;
02389             kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl;
02390         }
02391     }
02392 }
02393 
02394 
02395 void MultilineElement::calcSizes( const ContextStyle& context,
02396                                   ContextStyle::TextStyle tstyle,
02397                                   ContextStyle::IndexStyle istyle,
02398                                   StyleAttributes& style )
02399 {
02400     double factor = style.sizeFactor();
02401     luPt mySize = context.getAdjustedSize( tstyle, factor );
02402     QFont font = context.getDefaultFont();
02403     font.setPointSizeFloat( context.layoutUnitPtToPt( mySize ) );
02404     QFontMetrics fm( font );
02405     luPixel leading = context.ptToLayoutUnitPt( fm.leading() );
02406     luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );
02407 
02408     uint count = content.count();
02409     luPixel height = -leading;
02410     luPixel width = 0;
02411     uint tabCount = 0;
02412     for ( uint i = 0; i < count; ++i ) {
02413         MultilineSequenceElement* line = content.at(i);
02414         line->calcSizes( context, tstyle, istyle, style );
02415         tabCount = QMAX( tabCount, line->tabCount() );
02416 
02417         height += leading;
02418         line->setX( 0 );
02419         line->setY( height );
02420         height += line->getHeight() + distY;
02421         width = QMAX( line->getWidth(), width );
02422     }
02423 
02424     // calculate the tab positions
02425     for ( uint t = 0; t < tabCount; ++t ) {
02426         luPixel pos = 0;
02427         for ( uint i = 0; i < count; ++i ) {
02428             MultilineSequenceElement* line = content.at(i);
02429             if ( t < line->tabCount() ) {
02430                 pos = QMAX( pos, line->tab( t )->getX() );
02431             }
02432             else {
02433                 pos = QMAX( pos, line->getWidth() );
02434             }
02435         }
02436         for ( uint i = 0; i < count; ++i ) {
02437             MultilineSequenceElement* line = content.at(i);
02438             if ( t < line->tabCount() ) {
02439                 line->moveTabTo( t, pos );
02440                 width = QMAX( width, line->getWidth() );
02441             }
02442         }
02443     }
02444 
02445     setHeight( height );
02446     setWidth( width );
02447     if ( count == 1 ) {
02448         setBaseline( content.at( 0 )->getBaseline() );
02449     }
02450     else {
02451         // There's always a first line. No formulas without lines.
02452         setBaseline( height/2 + context.axisHeight( tstyle, factor ) );
02453     }
02454 }
02455 
02456 void MultilineElement::draw( QPainter& painter, const LuPixelRect& r,
02457                              const ContextStyle& context,
02458                              ContextStyle::TextStyle tstyle,
02459                              ContextStyle::IndexStyle istyle,
02460                              StyleAttributes& style,
02461                              const LuPixelPoint& parentOrigin )
02462 {
02463     LuPixelPoint myPos( parentOrigin.x() + getX(), parentOrigin.y() + getY() );
02464     uint count = content.count();
02465 
02466     if ( context.edit() ) {
02467         uint tabCount = 0;
02468         painter.setPen( context.getHelpColor() );
02469         for ( uint i = 0; i < count; ++i ) {
02470             MultilineSequenceElement* line = content.at(i);
02471             if ( tabCount < line->tabCount() ) {
02472                 for ( uint t = tabCount; t < line->tabCount(); ++t ) {
02473                     BasicElement* marker = line->tab( t );
02474                     painter.drawLine( context.layoutUnitToPixelX( myPos.x()+marker->getX() ),
02475                                       context.layoutUnitToPixelY( myPos.y() ),
02476                                       context.layoutUnitToPixelX( myPos.x()+marker->getX() ),
02477                                       context.layoutUnitToPixelY( myPos.y()+getHeight() ) );
02478                 }
02479                 tabCount = line->tabCount();
02480             }
02481         }
02482     }
02483 
02484     for ( uint i = 0; i < count; ++i ) {
02485         MultilineSequenceElement* line = content.at(i);
02486         line->draw( painter, r, context, tstyle, istyle, style, myPos );
02487     }
02488 }
02489 
02490 
02491 void MultilineElement::dispatchFontCommand( FontCommand* cmd )
02492 {
02493     uint count = content.count();
02494     for ( uint i = 0; i < count; ++i ) {
02495         MultilineSequenceElement* line = content.at(i);
02496         line->dispatchFontCommand( cmd );
02497     }
02498 }
02499 
02500 void MultilineElement::insert( FormulaCursor* cursor,
02501                                QPtrList<BasicElement>& newChildren,
02502                                Direction direction )
02503 {
02504     MultilineSequenceElement* e = static_cast<MultilineSequenceElement*>(newChildren.take(0));
02505     e->setParent(this);
02506     content.insert( cursor->getPos(), e );
02507 
02508     if (direction == beforeCursor) {
02509         e->moveLeft(cursor, this);
02510     }
02511     else {
02512         e->moveRight(cursor, this);
02513     }
02514     cursor->setSelection(false);
02515     formula()->changed();
02516 }
02517 
02518 void MultilineElement::remove( FormulaCursor* cursor,
02519                                QPtrList<BasicElement>& removedChildren,
02520                                Direction direction )
02521 {
02522     if ( content.count() == 1 ) { //&& ( cursor->getPos() == 0 ) ) {
02523         getParent()->selectChild(cursor, this);
02524         getParent()->remove(cursor, removedChildren, direction);
02525     }
02526     else {
02527         MultilineSequenceElement* e = content.take( cursor->getPos() );
02528         removedChildren.append( e );
02529         formula()->elementRemoval( e );
02530         //cursor->setTo( this, denominatorPos );
02531         formula()->changed();
02532     }
02533 }
02534 
02535 void MultilineElement::normalize( FormulaCursor* cursor, Direction direction )
02536 {
02537     int pos = cursor->getPos();
02538     if ( ( cursor->getElement() == this ) &&
02539          ( pos > -1 ) && ( static_cast<unsigned>( pos ) <= content.count() ) ) {
02540         switch ( direction ) {
02541         case beforeCursor:
02542             if ( pos > 0 ) {
02543                 content.at( pos-1 )->moveLeft( cursor, this );
02544                 break;
02545             }
02546             // no break! intended!
02547         case afterCursor:
02548             if ( static_cast<unsigned>( pos ) < content.count() ) {
02549                 content.at( pos )->moveRight( cursor, this );
02550             }
02551             else {
02552                 content.at( pos-1 )->moveLeft( cursor, this );
02553             }
02554             break;
02555         }
02556     }
02557     else {
02558         inherited::normalize( cursor, direction );
02559     }
02560 }
02561 
02562 SequenceElement* MultilineElement::getMainChild()
02563 {
02564     return content.at( 0 );
02565 }
02566 
02567 void MultilineElement::selectChild(FormulaCursor* cursor, BasicElement* child)
02568 {
02569     int pos = content.find( dynamic_cast<MultilineSequenceElement*>( child ) );
02570     if ( pos > -1 ) {
02571         cursor->setTo( this, pos );
02572         //content.at( pos )->moveRight( cursor, this );
02573     }
02574 }
02575 
02576 
02580 void MultilineElement::writeDom(QDomElement element)
02581 {
02582     BasicElement::writeDom(element);
02583 
02584     uint lineCount = content.count();
02585     element.setAttribute( "LINES", lineCount );
02586 
02587     QDomDocument doc = element.ownerDocument();
02588     for ( uint i = 0; i < lineCount; ++i ) {
02589         QDomElement tmp = content.at( i )->getElementDom(doc);
02590         element.appendChild(tmp);
02591     }
02592 }
02593 
02594 void MultilineElement::writeMathML( QDomDocument& doc, QDomNode& parent, bool oasisFormat ) const
02595 {
02596     QDomElement de = doc.createElement( oasisFormat ? "math:mtable" : "mtable" );
02597     QDomElement row; QDomElement cell;
02598 
02599     for ( QPtrListIterator < MultilineSequenceElement > it( content ); it.current(); ++it ) {
02600         row = doc.createElement( oasisFormat ? "math:mtr" : "mtr" );
02601         de.appendChild( row );
02602         //cell = doc.createElement( "mtd" );
02603         //row.appendChild( cell );
02604 
02605         //content.at( i )->writeMathML( doc, cell );
02606         it.current()->writeMathML( doc, row, oasisFormat );
02607     }
02608 
02609     parent.appendChild( de );
02610 }
02611 
02616 bool MultilineElement::readAttributesFromDom(QDomElement element)
02617 {
02618     if (!BasicElement::readAttributesFromDom(element)) {
02619         return false;
02620     }
02621     uint lineCount = 0;
02622     QString lineCountStr = element.attribute("LINES");
02623     if(!lineCountStr.isNull()) {
02624         lineCount = lineCountStr.toInt();
02625     }
02626     if (lineCount == 0) {
02627         kdWarning( DEBUGID ) << "lineCount <= 0 in MultilineElement." << endl;
02628         return false;
02629     }
02630 
02631     content.clear();
02632     for ( uint i = 0; i < lineCount; ++i ) {
02633         MultilineSequenceElement* element = new MultilineSequenceElement(this);
02634         content.append(element);
02635     }
02636     return true;
02637 }
02638 
02644 bool MultilineElement::readContentFromDom(QDomNode& node)
02645 {
02646     if (!BasicElement::readContentFromDom(node)) {
02647         return false;
02648     }
02649 
02650     uint lineCount = content.count();
02651     uint i = 0;
02652     while ( !node.isNull() && i < lineCount ) {
02653         if ( node.isElement() ) {
02654             SequenceElement* element = content.at( i );
02655             QDomElement e = node.toElement();
02656             if ( !element->buildFromDom( e ) ) {
02657                 return false;
02658             }
02659             ++i;
02660         }
02661         node = node.nextSibling();
02662     }
02663     return true;
02664 }
02665 
02666 QString MultilineElement::toLatex()
02667 {
02668     uint lineCount = content.count();
02669     QString muliline = "\\begin{split} ";
02670     for ( uint i = 0; i < lineCount; ++i ) {
02671         muliline += content.at( i )->toLatex();
02672         muliline += " \\\\ ";
02673     }
02674     muliline += "\\end{split}";
02675     return muliline;
02676 }
02677 
02678 // Does this make any sense at all?
02679 QString MultilineElement::formulaString()
02680 {
02681     uint lineCount = content.count();
02682     QString muliline = "";
02683     for ( uint i = 0; i < lineCount; ++i ) {
02684         muliline += content.at( i )->formulaString();
02685         muliline += "\n";
02686     }
02687     //muliline += "";
02688     return muliline;
02689 }
02690 
02691 
02692 KFORMULA_NAMESPACE_END
KDE Home | KDE Accessibility Home | Description of Access Keys