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")
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 > cols )
01147                     cols = cc;
01148             }
01149         }
01150         n = n.nextSibling();
01151     }
01152 
01153     // Create elements
01154     content.clear();
01155     for (uint r = 0; r < rows; r++) {
01156         QPtrList< MatrixSequenceElement >* list = new QPtrList< MatrixSequenceElement >;
01157         list->setAutoDelete(true);
01158         content.append(list);
01159         for (uint c = 0; c < cols; c++) {
01160             MatrixSequenceElement* element = new MatrixSequenceElement(this);
01161             list->append(element);
01162         }
01163     }
01164 
01165     // Second pass, read elements now
01166     uint r = 0;
01167     uint c = 0;
01168     while ( !node.isNull() ) {
01169         if ( node.isElement() ) {
01170             QDomElement e = node.toElement();
01171             if ( e.tagName().lower() == "mtr" ) {
01172                 QDomNode cellnode = e.firstChild();
01173                 while ( !cellnode.isNull() ) {
01174                     if ( cellnode.isElement() ) {
01175                         QDomElement cellelement = cellnode.toElement();
01176                         if ( cellelement.tagName().lower() != "mtd" ) {
01177                             // TODO: Inferred mtd. Deprecated in MathML 2.0
01178                             kdWarning( DEBUGID ) << "Unsupported tag " 
01179                                                  << cellelement.tagName()
01180                                                  << " inside matrix row\n";
01181                         }
01182                         else {
01183                             SequenceElement* element = getElement(r, c);
01184                             if ( element->buildFromMathMLDom( cellelement ) == -1 )
01185                                 return -1;
01186                             c++;
01187                         }
01188                     }
01189                     cellnode = cellnode.nextSibling();
01190                 }
01191                 c = 0;
01192                 r++;
01193             }
01194         }
01195         node = node.nextSibling();
01196     }
01197     return 1;
01198 }
01199 
01200 QString MatrixElement::toLatex()
01201 {
01202     //All the border handling must be implemented here too
01203 
01204     QString matrix;
01205     uint cols=getColumns();
01206     uint rows=getRows();
01207 
01208     matrix="\\begin{array}{ ";
01209     for(uint i=0;i<cols;i++)
01210     matrix+="c ";
01211 
01212     matrix+="} ";
01213 
01214     for (uint r = 0; r < rows; r++) {
01215         for (uint c = 0; c < cols; c++) {
01216             matrix+=getElement(r, c)->toLatex();
01217         if( c < cols-1)    matrix+=" & ";
01218         }
01219         if(r < rows-1 ) matrix+=" \\\\ ";
01220     }
01221 
01222     matrix+=" \\end{array}";
01223 
01224     return matrix;
01225 }
01226 
01227 QString MatrixElement::formulaString()
01228 {
01229     QString matrix = "[";
01230     uint cols=getColumns();
01231     uint rows=getRows();
01232     for (uint r = 0; r < rows; r++) {
01233         matrix += "[";
01234         for (uint c = 0; c < cols; c++) {
01235             matrix+=getElement(r, c)->formulaString();
01236         if ( c < cols-1 ) matrix+=", ";
01237         }
01238         matrix += "]";
01239         if ( r < rows-1 ) matrix += ", ";
01240     }
01241     matrix += "]";
01242     return matrix;
01243 }
01244 
01245 
01246 SequenceElement* MatrixElement::elementAt(uint row, uint column)
01247 {
01248     return getElement( row, column );
01249 }
01250 
01251 void MatrixElement::writeMathMLAttributes( QDomElement& element ) const
01252 {
01253     QString rownumber;
01254     if ( m_rowNumber ) {
01255         rownumber = QString( " %1" ).arg( m_rowNumber );
01256     }
01257     switch ( m_align ) {
01258     case TopAlign:
01259         element.setAttribute( "align", "top" + rownumber );
01260         break;
01261     case BottomAlign:
01262         element.setAttribute( "align", "bottom" + rownumber );
01263         break;
01264     case CenterAlign:
01265         element.setAttribute( "align", "center" + rownumber );
01266         break;
01267     case BaselineAlign:
01268         element.setAttribute( "align", "baseline" + rownumber );
01269         break;
01270     case AxisAlign:
01271         element.setAttribute( "align", "axis" + rownumber );
01272         break;
01273     default:
01274         break;
01275     }
01276     QString rowalign;
01277     for ( QValueList< VerticalAlign >::const_iterator it = m_rowAlign.begin(); it != m_rowAlign.end(); it++ )
01278     {
01279         switch ( *it ) {
01280         case TopAlign:
01281             rowalign.append( "top " );
01282             break;
01283         case BottomAlign:
01284             rowalign.append( "bottom " );
01285             break;
01286         case CenterAlign:
01287             rowalign.append( "center " );
01288             break;
01289         case BaselineAlign:
01290             rowalign.append( "baseline " );
01291             break;
01292         case AxisAlign:
01293             rowalign.append( "axis " );
01294             break;
01295         default:
01296             break;
01297         }
01298     }
01299     if ( ! rowalign.isNull() ) {
01300         element.setAttribute( "rowalign", rowalign.stripWhiteSpace() );
01301     }
01302     QString columnalign;
01303     for ( QValueList< HorizontalAlign >::const_iterator it = m_columnAlign.begin(); it != m_columnAlign.end(); it++ )
01304     {
01305         switch ( *it ) {
01306         case LeftHorizontalAlign:
01307             rowalign.append( "left " );
01308             break;
01309         case CenterHorizontalAlign:
01310             rowalign.append( "center " );
01311             break;
01312         case RightHorizontalAlign:
01313             rowalign.append( "right " );
01314             break;
01315         default:
01316             break;
01317         }
01318     }
01319     if ( ! columnalign.isNull() ) {
01320         element.setAttribute( "columnalign", columnalign.stripWhiteSpace() );
01321     }
01322     QString alignmentscope;
01323     for ( QValueList< bool >::const_iterator it = m_alignmentScope.begin(); it != m_alignmentScope.end(); it++ )
01324     {
01325         if ( *it ) {
01326             alignmentscope.append( "true " );
01327         }
01328         else {
01329             alignmentscope.append( "false " );
01330         }
01331     }
01332     if ( ! alignmentscope.isNull() ) {
01333         element.setAttribute( "alignmentscope", alignmentscope.stripWhiteSpace() );
01334     }
01335     QString columnwidth;
01336     QValueList< double >::const_iterator lengthIt = m_columnWidth.begin();
01337     for ( QValueList< SizeType >::const_iterator typeIt = m_columnWidthType.begin();
01338           typeIt != m_columnWidthType.end(); typeIt ++ ) {
01339         switch ( *typeIt ) {
01340         case AutoSize:
01341             columnwidth.append( "auto " );
01342             break;
01343         case FitSize:
01344             columnwidth.append( "fit " );
01345             break;
01346         case AbsoluteSize:
01347             columnwidth.append( QString( "%1pt " ).arg( *lengthIt ) );
01348             lengthIt++;
01349             break;
01350         case RelativeSize:
01351             columnwidth.append( QString( "%1% " ).arg( *lengthIt * 100.0 ) );
01352             lengthIt++;
01353             break;
01354         case PixelSize:
01355             columnwidth.append( QString( "%1px " ).arg( *lengthIt ) );
01356             lengthIt++;
01357             break;
01358         case NegativeVeryVeryThinMathSpace:
01359             columnwidth.append( "negativeveryverythinmathspace " );
01360             break;
01361         case NegativeVeryThinMathSpace:
01362             columnwidth.append( "negativeverythinmathspace " );
01363             break;
01364         case NegativeThinMathSpace:
01365             columnwidth.append( "negativethinmathspace " );
01366             break;
01367         case NegativeMediumMathSpace:
01368             columnwidth.append( "negativemediummathspace " );
01369             break;
01370         case NegativeThickMathSpace:
01371             columnwidth.append( "negativethickmathspace " );
01372             break;
01373         case NegativeVeryThickMathSpace:
01374             columnwidth.append( "negativeverythickmathspace " );
01375             break;
01376         case NegativeVeryVeryThickMathSpace:
01377             columnwidth.append( "negativeveryverythickmathspace " );
01378             break;
01379         case VeryVeryThinMathSpace:
01380             columnwidth.append( "veryverythinmathspace " );
01381             break;
01382         case VeryThinMathSpace:
01383             columnwidth.append( "verythinmathspace " );
01384             break;
01385         case ThinMathSpace:
01386             columnwidth.append( "thinmathspace " );
01387             break;
01388         case MediumMathSpace:
01389             columnwidth.append( "mediummathspace " );
01390             break;
01391         case ThickMathSpace:
01392             columnwidth.append( "thickmathspace " );
01393             break;
01394         case VeryThickMathSpace:
01395             columnwidth.append( "verythickmathspace " );
01396             break;
01397         case VeryVeryThickMathSpace:
01398             columnwidth.append( "veryverythickmathspace " );
01399             break;
01400         default:
01401             break;
01402         }
01403     }
01404     if ( ! columnwidth.isNull() ) {
01405         element.setAttribute( "columnwidth", columnwidth.stripWhiteSpace() );
01406     }
01407     switch ( m_widthType ) {
01408     case AutoSize:
01409         element.setAttribute( "width", "auto" );
01410         break;
01411     case AbsoluteSize:
01412         element.setAttribute( "width", QString( "%1pt" ).arg( m_width ) );
01413         break;
01414     case RelativeSize:
01415         element.setAttribute( "width", QString( "%1% " ).arg( m_width * 100.0 ) );
01416         break;
01417     case PixelSize:
01418         element.setAttribute( "width", QString( "%1px " ).arg( m_width ) );
01419         break;
01420     default:
01421         break;
01422     }
01423     QString rowspacing;
01424     lengthIt = m_rowSpacing.begin();
01425     for ( QValueList< SizeType >::const_iterator typeIt = m_rowSpacingType.begin();
01426           typeIt != m_rowSpacingType.end(); typeIt++, lengthIt++ ) {
01427         switch ( *typeIt ) {
01428         case AbsoluteSize:
01429             rowspacing.append( QString( "%1pt " ).arg( *lengthIt ) );
01430             break;
01431         case RelativeSize:
01432             rowspacing.append( QString( "%1% " ).arg( *lengthIt * 100.0 ) );
01433             break;
01434         case PixelSize:
01435             rowspacing.append( QString( "%1px " ).arg( *lengthIt ) );
01436             break;
01437         default:
01438             break;
01439         }
01440     }
01441     if ( ! rowspacing.isNull() ) {
01442         element.setAttribute( "rowspacing", rowspacing.stripWhiteSpace() );
01443     }
01444     QString columnspacing;
01445     lengthIt = m_columnSpacing.begin(); 
01446     for ( QValueList< SizeType >::const_iterator typeIt = m_columnSpacingType.begin();
01447           typeIt != m_columnSpacingType.end(); typeIt++ ) {
01448         switch ( *typeIt ) {
01449         case AbsoluteSize:
01450             columnspacing.append( QString( "%1pt " ).arg( *lengthIt ) );
01451             lengthIt++;
01452             break;
01453         case RelativeSize:
01454             columnspacing.append( QString( "%1% " ).arg( *lengthIt * 100.0 ) );
01455             lengthIt++;
01456             break;
01457         case PixelSize:
01458             columnspacing.append( QString( "%1px " ).arg( *lengthIt ) );
01459             lengthIt++;
01460             break;
01461         case NegativeVeryVeryThinMathSpace:
01462             columnspacing.append( "negativeveryverythinmathspace " );
01463             break;
01464         case NegativeVeryThinMathSpace:
01465             columnspacing.append( "negativeverythinmathspace " );
01466             break;
01467         case NegativeThinMathSpace:
01468             columnspacing.append( "negativethinmathspace " );
01469             break;
01470         case NegativeMediumMathSpace:
01471             columnspacing.append( "negativemediummathspace " );
01472             break;
01473         case NegativeThickMathSpace:
01474             columnspacing.append( "negativethickmathspace " );
01475             break;
01476         case NegativeVeryThickMathSpace:
01477             columnspacing.append( "negativeverythickmathspace " );
01478             break;
01479         case NegativeVeryVeryThickMathSpace:
01480             columnspacing.append( "negativeveryverythickmathspace " );
01481             break;
01482         case VeryVeryThinMathSpace:
01483             columnspacing.append( "veryverythinmathspace " );
01484             break;
01485         case VeryThinMathSpace:
01486             columnspacing.append( "verythinmathspace " );
01487             break;
01488         case ThinMathSpace:
01489             columnspacing.append( "thinmathspace " );
01490             break;
01491         case MediumMathSpace:
01492             columnspacing.append( "mediummathspace " );
01493             break;
01494         case ThickMathSpace:
01495             columnspacing.append( "thickmathspace " );
01496             break;
01497         case VeryThickMathSpace:
01498             columnspacing.append( "verythickmathspace " );
01499             break;
01500         case VeryVeryThickMathSpace:
01501             columnspacing.append( "veryverythickmathspace " );
01502             break;
01503         default:
01504             break;
01505         }
01506     }
01507     if ( ! rowspacing.isNull() ) {
01508         element.setAttribute( "rowspacing", rowspacing.stripWhiteSpace() );
01509     }
01510     QString rowlines;
01511     for ( QValueList< LineType >::const_iterator it = m_rowLines.begin(); it != m_rowLines.end(); it++ )
01512     {
01513         switch ( *it ) {
01514         case NoneLine:
01515             rowlines.append( "none " );
01516             break;
01517         case SolidLine:
01518             rowlines.append( "solid " );
01519             break;
01520         case DashedLine:
01521             rowlines.append( "dashed " );
01522             break;
01523         default:
01524             break;
01525         }
01526     }
01527     if ( ! rowlines.isNull() ) {
01528         element.setAttribute( "rowlines", rowlines.stripWhiteSpace() );
01529     }
01530     QString columnlines;
01531     for ( QValueList< LineType >::const_iterator it = m_columnLines.begin(); it != m_columnLines.end(); it++ )
01532     {
01533         switch ( *it ) {
01534         case NoneLine:
01535             columnlines.append( "none " );
01536             break;
01537         case SolidLine:
01538             columnlines.append( "solid " );
01539             break;
01540         case DashedLine:
01541             columnlines.append( "dashed " );
01542             break;
01543         default:
01544             break;
01545         }
01546     }
01547     if ( ! columnlines.isNull() ) {
01548         element.setAttribute( "columnlines", columnlines.stripWhiteSpace() );
01549     }
01550     switch ( m_frame ) {
01551     case NoneLine:
01552         element.setAttribute( "frame", "none" );
01553         break;
01554     case SolidLine:
01555         element.setAttribute( "frame", "solid" );
01556         break;
01557     case DashedLine:
01558         element.setAttribute( "frame", "dashed" );
01559         break;
01560     default:
01561         break;
01562     }
01563     QString framespacing;
01564     switch ( m_frameHSpacingType ) {
01565     case AbsoluteSize:
01566         framespacing.append( QString( "%1pt " ).arg( m_frameHSpacing ) );
01567         break;
01568     case RelativeSize:
01569         framespacing.append( QString( "%1% " ).arg( m_frameHSpacing * 100.0 ) );
01570         break;
01571     case PixelSize:
01572         framespacing.append( QString( "%1px " ).arg( m_frameHSpacing ) );
01573         break;
01574     case NegativeVeryVeryThinMathSpace:
01575         framespacing.append( "negativeveryverythinmathspace " );
01576         break;
01577     case NegativeVeryThinMathSpace:
01578         framespacing.append( "negativeverythinmathspace " );
01579         break;
01580     case NegativeThinMathSpace:
01581         framespacing.append( "negativethinmathspace " );
01582         break;
01583     case NegativeMediumMathSpace:
01584         framespacing.append( "negativemediummathspace " );
01585         break;
01586     case NegativeThickMathSpace:
01587         framespacing.append( "negativethickmathspace " );
01588         break;
01589     case NegativeVeryThickMathSpace:
01590         framespacing.append( "negativeverythickmathspace " );
01591         break;
01592     case NegativeVeryVeryThickMathSpace:
01593         framespacing.append( "negativeveryverythickmathspace " );
01594         break;
01595     case VeryVeryThinMathSpace:
01596         framespacing.append( "veryverythinmathspace " );
01597         break;
01598     case VeryThinMathSpace:
01599         framespacing.append( "verythinmathspace " );
01600         break;
01601     case ThinMathSpace:
01602         framespacing.append( "thinmathspace " );
01603         break;
01604     case MediumMathSpace:
01605         framespacing.append( "mediummathspace " );
01606         break;
01607     case ThickMathSpace:
01608         framespacing.append( "thickmathspace " );
01609         break;
01610     case VeryThickMathSpace:
01611         framespacing.append( "verythickmathspace " );
01612         break;
01613     case VeryVeryThickMathSpace:
01614         framespacing.append( "veryverythickmathspace " );
01615         break;
01616     default:
01617         break;
01618     }
01619     switch ( m_frameVSpacingType ) {
01620     case AbsoluteSize:
01621         framespacing.append( QString( "%1pt " ).arg( m_frameVSpacing ) );
01622         break;
01623     case RelativeSize:
01624         framespacing.append( QString( "%1% " ).arg( m_frameVSpacing * 100.0 ) );
01625         break;
01626     case PixelSize:
01627         framespacing.append( QString( "%1px " ).arg( m_frameVSpacing ) );
01628         break;
01629     case NegativeVeryVeryThinMathSpace:
01630         framespacing.append( "negativeveryverythinmathspace " );
01631         break;
01632     case NegativeVeryThinMathSpace:
01633         framespacing.append( "negativeverythinmathspace " );
01634         break;
01635     case NegativeThinMathSpace:
01636         framespacing.append( "negativethinmathspace " );
01637         break;
01638     case NegativeMediumMathSpace:
01639         framespacing.append( "negativemediummathspace " );
01640         break;
01641     case NegativeThickMathSpace:
01642         framespacing.append( "negativethickmathspace " );
01643         break;
01644     case NegativeVeryThickMathSpace:
01645         framespacing.append( "negativeverythickmathspace " );
01646         break;
01647     case NegativeVeryVeryThickMathSpace:
01648         framespacing.append( "negativeveryverythickmathspace " );
01649         break;
01650     case VeryVeryThinMathSpace:
01651         framespacing.append( "veryverythinmathspace " );
01652         break;
01653     case VeryThinMathSpace:
01654         framespacing.append( "verythinmathspace " );
01655         break;
01656     case ThinMathSpace:
01657         framespacing.append( "thinmathspace " );
01658         break;
01659     case MediumMathSpace:
01660         framespacing.append( "mediummathspace " );
01661         break;
01662     case ThickMathSpace:
01663         framespacing.append( "thickmathspace " );
01664         break;
01665     case VeryThickMathSpace:
01666         framespacing.append( "verythickmathspace " );
01667         break;
01668     case VeryVeryThickMathSpace:
01669         framespacing.append( "veryverythickmathspace " );
01670         break;
01671     default:
01672         break;
01673     }
01674     if ( ! framespacing.isNull() ) {
01675         element.setAttribute( "framespacing", framespacing.stripWhiteSpace() );
01676     }
01677     if ( m_customEqualRows ) {
01678         element.setAttribute( "equalrows", m_equalRows ? "true" : "false" );
01679     }
01680     if ( m_customEqualColumns ) {
01681         element.setAttribute( "equalcolumns", m_equalColumns ? "true" : "false" );
01682     }
01683     if ( m_customDisplayStyle ) {
01684         element.setAttribute( "displaystyle", m_displayStyle ? "true" : "false" );
01685     }
01686     switch ( m_side ) {
01687     case LeftSide:
01688         element.setAttribute( "side", "left" );
01689         break;
01690     case RightSide:
01691         element.setAttribute( "side", "right" );
01692         break;
01693     case LeftOverlapSide:
01694         element.setAttribute( "side", "leftoverlap" );
01695         break;
01696     case RightOverlapSide:
01697         element.setAttribute( "side", "rightoverlap" );
01698         break;
01699     default:
01700         break;
01701     }
01702     switch ( m_minLabelSpacingType ) {
01703     case AbsoluteSize:
01704         element.setAttribute( "minlabelspacing", QString( "%1pt" ).arg( m_minLabelSpacing ) );
01705         break;
01706     case RelativeSize:
01707         element.setAttribute( "minlabelspacing", QString( "%1%" ).arg( m_minLabelSpacing * 100.0 ) );
01708         break;
01709     case PixelSize:
01710         element.setAttribute( "minlabelspacing", QString( "%1px" ).arg( m_minLabelSpacing ) );
01711         break;
01712     case NegativeVeryVeryThinMathSpace:
01713         element.setAttribute( "minlabelspacing", "negativeveryverythinmathspace" );
01714         break;
01715     case NegativeVeryThinMathSpace:
01716         element.setAttribute( "minlabelspacing", "negativeverythinmathspace" );
01717         break;
01718     case NegativeThinMathSpace:
01719         element.setAttribute( "minlabelspacing", "negativethinmathspace" );
01720         break;
01721     case NegativeMediumMathSpace:
01722         element.setAttribute( "minlabelspacing", "negativemediummathspace" );
01723         break;
01724     case NegativeThickMathSpace:
01725         element.setAttribute( "minlabelspacing", "negativethickmathspace" );
01726         break;
01727     case NegativeVeryThickMathSpace:
01728         element.setAttribute( "minlabelspacing", "negativeverythickmathspace" );
01729         break;
01730     case NegativeVeryVeryThickMathSpace:
01731         element.setAttribute( "minlabelspacing", "negativeveryverythickmathspace" );
01732         break;
01733     case VeryVeryThinMathSpace:
01734         element.setAttribute( "minlabelspacing", "veryverythinmathspace" );
01735         break;
01736     case VeryThinMathSpace:
01737         element.setAttribute( "minlabelspacing", "verythinmathspace" );
01738         break;
01739     case ThinMathSpace:
01740         element.setAttribute( "minlabelspacing", "thinmathspace" );
01741         break;
01742     case MediumMathSpace:
01743         element.setAttribute( "minlabelspacing", "mediummathspace" );
01744         break;
01745     case ThickMathSpace:
01746         element.setAttribute( "minlabelspacing", "thickmathspace" );
01747         break;
01748     case VeryThickMathSpace:
01749         element.setAttribute( "minlabelspacing", "verythickmathspace" );
01750         break;
01751     case VeryVeryThickMathSpace:
01752         element.setAttribute( "minlabelspacing", "veryverythickmathspace" );
01753         break;
01754     default:
01755         break;
01756     }
01757 }
01758 
01759 void MatrixElement::writeMathMLContent( QDomDocument& doc, 
01760                                         QDomElement& element,
01761                                         bool oasisFormat ) const
01762 {
01763     QDomElement row;
01764     QDomElement cell;
01765 
01766     uint rows = getRows();
01767     uint cols = getColumns();
01768 
01769     for ( uint r = 0; r < rows; r++ )
01770     {
01771         row = doc.createElement( oasisFormat ? "math:mtr" : "mtr" );
01772         element.appendChild( row );
01773         for ( uint c = 0; c < cols; c++ )
01774         {
01775             cell = doc.createElement( oasisFormat ? "math:mtd" : "mtd" );
01776             row.appendChild( cell );
01777             getElement(r,c)->writeMathML( doc, cell, oasisFormat );
01778         }
01779     }
01780 }
01781 
01782 
01784 
01785 
01790 class MultilineSequenceElement : public SequenceElement {
01791     typedef SequenceElement inherited;
01792 public:
01793 
01794     MultilineSequenceElement( BasicElement* parent = 0 );
01795 
01796     virtual MultilineSequenceElement* clone() {
01797         return new MultilineSequenceElement( *this );
01798     }
01799 
01800     virtual BasicElement* goToPos( FormulaCursor*, bool& handled,
01801                                    const LuPixelPoint& point, const LuPixelPoint& parentOrigin );
01802 
01807     virtual void calcSizes( const ContextStyle& context,
01808                             ContextStyle::TextStyle tstyle,
01809                             ContextStyle::IndexStyle istyle,
01810                             StyleAttributes& style );
01811 
01812     virtual void registerTab( BasicElement* tab );
01813 
01822     virtual KCommand* buildCommand( Container*, Request* );
01823 
01824     virtual KCommand* input( Container* container, QKeyEvent* event );
01825 
01826     virtual KCommand* input( Container* container, QChar ch );
01827 
01828     uint tabCount() const { return tabs.count(); }
01829 
01830     BasicElement* tab( uint i ) { return tabs.at( i ); }
01831 
01833     void moveTabTo( uint i, luPixel pos );
01834 
01836     int tabBefore( uint pos );
01837 
01839     int tabPos( uint i );
01840 
01841     virtual void writeMathML( QDomDocument& doc, QDomNode& parent, bool oasisFormat = false ) const ;
01842 
01843 private:
01844 
01845     QPtrList<BasicElement> tabs;
01846 };
01847 
01848 
01849 // Split the line at position pos.
01850 class KFCNewLine : public Command {
01851 public:
01852     KFCNewLine( const QString& name, Container* document,
01853                 MultilineSequenceElement* line, uint pos );
01854 
01855     virtual ~KFCNewLine();
01856 
01857     virtual void execute();
01858     virtual void unexecute();
01859 
01860 private:
01861     MultilineSequenceElement* m_line;
01862     MultilineSequenceElement* m_newline;
01863     uint m_pos;
01864 };
01865 
01866 
01867 KFCNewLine::KFCNewLine( const QString& name, Container* document,
01868                         MultilineSequenceElement* line, uint pos )
01869     : Command( name, document ),
01870       m_line( line ), m_pos( pos )
01871 {
01872     m_newline = new MultilineSequenceElement( m_line->getParent() );
01873 }
01874 
01875 
01876 KFCNewLine::~KFCNewLine()
01877 {
01878     delete m_newline;
01879 }
01880 
01881 
01882 void KFCNewLine::execute()
01883 {
01884     FormulaCursor* cursor = getExecuteCursor();
01885     MultilineElement* parent = static_cast<MultilineElement*>( m_line->getParent() );
01886     int linePos = parent->content.find( m_line );
01887     parent->content.insert( linePos+1, m_newline );
01888 
01889     // If there are children to be moved.
01890     if ( m_line->countChildren() > static_cast<int>( m_pos ) ) {
01891 
01892         // Remove anything after position pos from the current line
01893         m_line->selectAllChildren( cursor );
01894         cursor->setMark( m_pos );
01895         QPtrList<BasicElement> elementList;
01896         m_line->remove( cursor, elementList, beforeCursor );
01897 
01898         // Insert the removed stuff into the new line
01899         m_newline->goInside( cursor );
01900         m_newline->insert( cursor, elementList, beforeCursor );
01901         cursor->setPos( cursor->getMark() );
01902     }
01903     else {
01904         m_newline->goInside( cursor );
01905     }
01906 
01907     // The command no longer owns the new line.
01908     m_newline = 0;
01909 
01910     // Tell that something changed
01911     FormulaElement* formula = m_line->formula();
01912     formula->changed();
01913     testDirty();
01914 }
01915 
01916 
01917 void KFCNewLine::unexecute()
01918 {
01919     FormulaCursor* cursor = getExecuteCursor();
01920     MultilineElement* parent = static_cast<MultilineElement*>( m_line->getParent() );
01921     int linePos = parent->content.find( m_line );
01922 
01923     // Now the command owns the new line again.
01924     m_newline = parent->content.at( linePos+1 );
01925 
01926     // Tell all cursors to leave this sequence
01927     FormulaElement* formula = m_line->formula();
01928     formula->elementRemoval( m_newline );
01929 
01930     // If there are children to be moved.
01931     if ( m_newline->countChildren() > 0 ) {
01932 
01933         // Remove anything from the line to be deleted
01934         m_newline->selectAllChildren( cursor );
01935         QPtrList<BasicElement> elementList;
01936         m_newline->remove( cursor, elementList, beforeCursor );
01937 
01938         // Insert the removed stuff into the previous line
01939         m_line->moveEnd( cursor );
01940         m_line->insert( cursor, elementList, beforeCursor );
01941         cursor->setPos( cursor->getMark() );
01942     }
01943     else {
01944         m_line->moveEnd( cursor );
01945     }
01946     parent->content.take( linePos+1 );
01947 
01948     // Tell that something changed
01949     formula->changed();
01950     testDirty();
01951 }
01952 
01953 
01954 MultilineSequenceElement::MultilineSequenceElement( BasicElement* parent )
01955     : SequenceElement( parent )
01956 {
01957     tabs.setAutoDelete( false );
01958 }
01959 
01960 
01961 BasicElement* MultilineSequenceElement::goToPos( FormulaCursor* cursor, bool& handled,
01962                                                  const LuPixelPoint& point, const LuPixelPoint& parentOrigin )
01963 {
01964     //LuPixelPoint myPos(parentOrigin.x() + getX(),
01965     //                   parentOrigin.y() + getY());
01966     BasicElement* e = inherited::goToPos(cursor, handled, point, parentOrigin);
01967 
01968     if (e == 0) {
01969         // If the mouse was behind this line put the cursor to the last position.
01970         if ( ( point.x() > getX()+getWidth() ) &&
01971              ( point.y() >= getY() ) &&
01972              ( point.y() < getY()+getHeight() ) ) {
01973             cursor->setTo(this, countChildren());
01974             handled = true;
01975             return this;
01976         }
01977     }
01978     return e;
01979 }
01980 
01981 
01982 void MultilineSequenceElement::calcSizes( const ContextStyle& context,
01983                                           ContextStyle::TextStyle tstyle,
01984                                           ContextStyle::IndexStyle istyle,
01985                                           StyleAttributes& style )
01986 {
01987     tabs.clear();
01988     inherited::calcSizes( context, tstyle, istyle, style );
01989 }
01990 
01991 
01992 void MultilineSequenceElement::registerTab( BasicElement* tab )
01993 {
01994     tabs.append( tab );
01995 }
01996 
01997 
01998 KCommand* MultilineSequenceElement::buildCommand( Container* container, Request* request )
01999 {
02000     FormulaCursor* cursor = container->activeCursor();
02001     if ( cursor->isReadOnly() ) {
02002         return 0;
02003     }
02004 
02005     switch ( *request ) {
02006     case req_remove: {
02007         // Remove this line if its empty.
02008         // Remove the formula if this line was the only one.
02009         break;
02010     }
02011     case req_addNewline: {
02012         FormulaCursor* cursor = container->activeCursor();
02013         return new KFCNewLine( i18n( "Add Newline" ), container, this, cursor->getPos() );
02014     }
02015     case req_addTabMark: {
02016         KFCReplace* command = new KFCReplace( i18n("Add Tabmark"), container );
02017         SpaceElement* element = new SpaceElement( THIN, true );
02018         command->addElement( element );
02019         return command;
02020     }
02021     default:
02022         break;
02023     }
02024     return inherited::buildCommand( container, request );
02025 }
02026 
02027 
02028 KCommand* MultilineSequenceElement::input( Container* container, QKeyEvent* event )
02029 {
02030     int action = event->key();
02031     //int state = event->state();
02032     //MoveFlag flag = movementFlag(state);
02033 
02034     switch ( action ) {
02035     case Qt::Key_Enter:
02036     case Qt::Key_Return: {
02037         Request newline( req_addNewline );
02038         return buildCommand( container, &newline );
02039     }
02040     case Qt::Key_Tab: {
02041         Request r( req_addTabMark );
02042         return buildCommand( container, &r );
02043     }
02044     }
02045     return inherited::input( container, event );
02046 }
02047 
02048 
02049 KCommand* MultilineSequenceElement::input( Container* container, QChar ch )
02050 {
02051     int latin1 = ch.latin1();
02052     switch (latin1) {
02053     case '&': {
02054         Request r( req_addTabMark );
02055         return buildCommand( container, &r );
02056     }
02057     }
02058     return inherited::input( container, ch );
02059 }
02060 
02061 
02062 void MultilineSequenceElement::moveTabTo( uint i, luPixel pos )
02063 {
02064     BasicElement* marker = tab( i );
02065     luPixel diff = pos - marker->getX();
02066     marker->setWidth( marker->getWidth() + diff );
02067 
02068     for ( int p = childPos( marker )+1; p < countChildren(); ++p ) {
02069         BasicElement* child = getChild( p );
02070         child->setX( child->getX() + diff );
02071     }
02072 
02073     setWidth( getWidth()+diff );
02074 }
02075 
02076 
02077 int MultilineSequenceElement::tabBefore( uint pos )
02078 {
02079     if ( tabs.isEmpty() ) {
02080         return -1;
02081     }
02082     uint tabNum = 0;
02083     for ( uint i=0; i<pos; ++i ) {
02084         BasicElement* child = getChild( i );
02085         if ( tabs.at( tabNum ) == child ) {
02086             if ( tabNum+1 == tabs.count() ) {
02087                 return tabNum;
02088             }
02089             ++tabNum;
02090         }
02091     }
02092     return static_cast<int>( tabNum )-1;
02093 }
02094 
02095 
02096 int MultilineSequenceElement::tabPos( uint i )
02097 {
02098     if ( i < tabs.count() ) {
02099         return childPos( tabs.at( i ) );
02100     }
02101     return -1;
02102 }
02103 
02104 
02105 void MultilineSequenceElement::writeMathML( QDomDocument& doc,
02106                                             QDomNode& parent, bool oasisFormat ) const
02107 {
02108     // parent is required to be a <mtr> tag
02109 
02110     QDomElement tmp = doc.createElement( "TMP" );
02111 
02112     inherited::writeMathML( doc, tmp, oasisFormat );
02113 
02114     /* Now we re-parse the Dom tree, because of the TabMarkers
02115      * that have no direct representation in MathML but mark the
02116      * end of a <mtd> tag.
02117      */
02118 
02119     QDomElement mtd = doc.createElement( oasisFormat ? "math:mtd" : "mtd" );
02120 
02121     // The mrow, if it exists.
02122     QDomNode n = tmp.firstChild().firstChild();
02123     while ( !n.isNull() ) {
02124         // the illegal TabMarkers are children of the mrow, child of tmp.
02125         if ( n.isElement() && n.toElement().tagName() == "TAB" ) {
02126             parent.appendChild( mtd );
02127             mtd = doc.createElement( oasisFormat ? "math:mtd" : "mtd" );
02128         }
02129         else {
02130             mtd.appendChild( n.cloneNode() ); // cloneNode needed?
02131         }
02132         n = n.nextSibling();
02133     }
02134 
02135     parent.appendChild( mtd );
02136 }
02137 
02138 
02139 MultilineElement::MultilineElement( BasicElement* parent )
02140     : BasicElement( parent )
02141 {
02142     content.setAutoDelete( true );
02143     content.append( new MultilineSequenceElement( this ) );
02144 }
02145 
02146 MultilineElement::~MultilineElement()
02147 {
02148 }
02149 
02150 MultilineElement::MultilineElement( const MultilineElement& other )
02151     : BasicElement( other )
02152 {
02153     content.setAutoDelete( true );
02154     uint count = other.content.count();
02155     for (uint i = 0; i < count; i++) {
02156         MultilineSequenceElement* line = content.at(i)->clone();
02157         line->setParent( this );
02158         content.append( line );
02159     }
02160 }
02161 
02162 
02163 bool MultilineElement::accept( ElementVisitor* visitor )
02164 {
02165     return visitor->visit( this );
02166 }
02167 
02168 
02169 void MultilineElement::entered( SequenceElement* /*child*/ )
02170 {
02171     formula()->tell( i18n( "Multi line element" ) );
02172 }
02173 
02174 
02178 BasicElement* MultilineElement::goToPos( FormulaCursor* cursor, bool& handled,
02179                                          const LuPixelPoint& point, const LuPixelPoint& parentOrigin )
02180 {
02181     BasicElement* e = inherited::goToPos(cursor, handled, point, parentOrigin);
02182     if ( e != 0 ) {
02183         LuPixelPoint myPos(parentOrigin.x() + getX(),
02184                            parentOrigin.y() + getY());
02185 
02186         uint count = content.count();
02187         for ( uint i = 0; i < count; ++i ) {
02188             MultilineSequenceElement* line = content.at(i);
02189             e = line->goToPos(cursor, handled, point, myPos);
02190             if (e != 0) {
02191                 return e;
02192             }
02193         }
02194         return this;
02195     }
02196     return 0;
02197 }
02198 
02199 void MultilineElement::goInside( FormulaCursor* cursor )
02200 {
02201     content.at( 0 )->goInside( cursor );
02202 }
02203 
02204 void MultilineElement::moveLeft( FormulaCursor* cursor, BasicElement* from )
02205 {
02206     // If you want to select more than one line you'll have to
02207     // select the whole element.
02208     if (cursor->isSelectionMode()) {
02209         getParent()->moveLeft(cursor, this);
02210     }
02211     else {
02212         // Coming from the parent (sequence) we go to
02213         // the very last position
02214         if (from == getParent()) {
02215             content.at( content.count()-1 )->moveLeft(cursor, this);
02216         }
02217         else {
02218             // Coming from one of the lines we go to the previous line
02219             // or to the parent if there is none.
02220             int pos = content.find( static_cast<MultilineSequenceElement*>( from ) );
02221             if ( pos > -1 ) {
02222                 if ( pos > 0 ) {
02223                     content.at( pos-1 )->moveLeft( cursor, this );
02224                 }
02225                 else {
02226                     getParent()->moveLeft(cursor, this);
02227                 }
02228             }
02229             else {
02230                 kdDebug( DEBUGID ) << k_funcinfo << endl;
02231                 kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl;
02232             }
02233         }
02234     }
02235 }
02236 
02237 void MultilineElement::moveRight( FormulaCursor* cursor, BasicElement* from )
02238 {
02239     if (cursor->isSelectionMode()) {
02240         getParent()->moveRight(cursor, this);
02241     }
02242     else {
02243         if (from == getParent()) {
02244             content.at( 0 )->moveRight(cursor, this);
02245         }
02246         else {
02247             int pos = content.find( static_cast<MultilineSequenceElement*>( from ) );
02248             if ( pos > -1 ) {
02249                 uint upos = pos;
02250                 if ( upos < content.count() ) {
02251                     if ( upos < content.count()-1 ) {
02252                         content.at( upos+1 )->moveRight( cursor, this );
02253                     }
02254                     else {
02255                         getParent()->moveRight(cursor, this);
02256                     }
02257                     return;
02258                 }
02259             }
02260             kdDebug( DEBUGID ) << k_funcinfo << endl;
02261             kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl;
02262         }
02263     }
02264 }
02265 
02266 void MultilineElement::moveUp( FormulaCursor* cursor, BasicElement* from )
02267 {
02268     // If you want to select more than one line you'll have to
02269     // select the whole element.
02270     if (cursor->isSelectionMode()) {
02271         getParent()->moveLeft(cursor, this);
02272     }
02273     else {
02274         // Coming from the parent (sequence) we go to
02275         // the very last position
02276         if (from == getParent()) {
02277             content.at( content.count()-1 )->moveLeft(cursor, this);
02278         }
02279         else {
02280             // Coming from one of the lines we go to the previous line
02281             // or to the parent if there is none.
02282             int pos = content.find( static_cast<MultilineSequenceElement*>( from ) );
02283             if ( pos > -1 ) {
02284                 if ( pos > 0 ) {
02285                     //content.at( pos-1 )->moveLeft( cursor, this );
02286                     // This is rather hackish.
02287                     // But we know what elements we have here.
02288                     int cursorPos = cursor->getPos();
02289                     MultilineSequenceElement* current = content.at( pos );
02290                     MultilineSequenceElement* newLine = content.at( pos-1 );
02291                     int tabNum = current->tabBefore( cursorPos );
02292                     if ( tabNum > -1 ) {
02293                         int oldTabPos = current->tabPos( tabNum );
02294                         int newTabPos = newLine->tabPos( tabNum );
02295                         if ( newTabPos > -1 ) {
02296                             cursorPos += newTabPos-oldTabPos;
02297                             int nextNewTabPos = newLine->tabPos( tabNum+1 );
02298                             if ( nextNewTabPos > -1 ) {
02299                                 cursorPos = QMIN( cursorPos, nextNewTabPos );
02300                             }
02301                         }
02302                         else {
02303                             cursorPos = newLine->countChildren();
02304                         }
02305                     }
02306                     else {
02307                         int nextNewTabPos = newLine->tabPos( 0 );
02308                         if ( nextNewTabPos > -1 ) {
02309                             cursorPos = QMIN( cursorPos, nextNewTabPos );
02310                         }
02311                     }
02312                     cursor->setTo( newLine,
02313                                    QMIN( cursorPos,
02314                                          newLine->countChildren() ) );
02315                 }
02316                 else {
02317                     getParent()->moveLeft(cursor, this);
02318                 }
02319             }
02320             else {
02321                 kdDebug( DEBUGID ) << k_funcinfo << endl;
02322                 kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl;
02323             }
02324         }
02325     }
02326 }
02327 
02328 void MultilineElement::moveDown( FormulaCursor* cursor, BasicElement* from )
02329 {
02330     if (cursor->isSelectionMode()) {
02331         getParent()->moveRight(cursor, this);
02332     }
02333     else {
02334         if (from == getParent()) {
02335             content.at( 0 )->moveRight(cursor, this);
02336         }
02337         else {
02338             int pos = content.find( static_cast<MultilineSequenceElement*>( from ) );
02339             if ( pos > -1 ) {
02340                 uint upos = pos;
02341                 if ( upos < content.count() ) {
02342                     if ( upos < content.count()-1 ) {
02343                         //content.at( upos+1 )->moveRight( cursor, this );
02344                         // This is rather hackish.
02345                         // But we know what elements we have here.
02346                         int cursorPos = cursor->getPos();
02347                         MultilineSequenceElement* current = content.at( upos );
02348                         MultilineSequenceElement* newLine = content.at( upos+1 );
02349                         int tabNum = current->tabBefore( cursorPos );
02350                         if ( tabNum > -1 ) {
02351                             int oldTabPos = current->tabPos( tabNum );
02352                             int newTabPos = newLine->tabPos( tabNum );
02353                             if ( newTabPos > -1 ) {
02354                                 cursorPos += newTabPos-oldTabPos;
02355                                 int nextNewTabPos = newLine->tabPos( tabNum+1 );
02356                                 if ( nextNewTabPos > -1 ) {
02357                                     cursorPos = QMIN( cursorPos, nextNewTabPos );
02358                                 }
02359                             }
02360                             else {
02361                                 cursorPos = newLine->countChildren();
02362                             }
02363                         }
02364                         else {
02365                             int nextNewTabPos = newLine->tabPos( 0 );
02366                             if ( nextNewTabPos > -1 ) {
02367                                 cursorPos = QMIN( cursorPos, nextNewTabPos );
02368                             }
02369                         }
02370                         cursor->setTo( newLine,
02371                                        QMIN( cursorPos,
02372                                              newLine->countChildren() ) );
02373                     }
02374                     else {
02375                         getParent()->moveRight(cursor, this);
02376                     }
02377                     return;
02378                 }
02379             }
02380             kdDebug( DEBUGID ) << k_funcinfo << endl;
02381             kdDebug( DEBUGID ) << "Serious confusion. Must never happen." << endl;
02382         }
02383     }
02384 }
02385 
02386 
02387 void MultilineElement::calcSizes( const ContextStyle& context,
02388                                   ContextStyle::TextStyle tstyle,
02389                                   ContextStyle::IndexStyle istyle,
02390                                   StyleAttributes& style )
02391 {
02392     double factor = style.sizeFactor();
02393     luPt mySize = context.getAdjustedSize( tstyle, factor );
02394     QFont font = context.getDefaultFont();
02395     font.setPointSizeFloat( context.layoutUnitPtToPt( mySize ) );
02396     QFontMetrics fm( font );
02397     luPixel leading = context.ptToLayoutUnitPt( fm.leading() );
02398     luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );
02399 
02400     uint count = content.count();
02401     luPixel height = -leading;
02402     luPixel width = 0;
02403     uint tabCount = 0;
02404     for ( uint i = 0; i < count; ++i ) {
02405         MultilineSequenceElement* line = content.at(i);
02406         line->calcSizes( context, tstyle, istyle, style );
02407         tabCount = QMAX( tabCount, line->tabCount() );
02408 
02409         height += leading;
02410         line->setX( 0 );
02411         line->setY( height );
02412         height += line->getHeight() + distY;
02413         width = QMAX( line->getWidth(), width );
02414     }
02415 
02416     // calculate the tab positions
02417     for ( uint t = 0; t < tabCount; ++t ) {
02418         luPixel pos = 0;
02419         for ( uint i = 0; i < count; ++i ) {
02420             MultilineSequenceElement* line = content.at(i);
02421             if ( t < line->tabCount() ) {
02422                 pos = QMAX( pos, line->tab( t )->getX() );
02423             }
02424             else {
02425                 pos = QMAX( pos, line->getWidth() );
02426             }
02427         }
02428         for ( uint i = 0; i < count; ++i ) {
02429             MultilineSequenceElement* line = content.at(i);
02430             if ( t < line->tabCount() ) {
02431                 line->moveTabTo( t, pos );
02432                 width = QMAX( width, line->getWidth() );
02433             }
02434         }
02435     }
02436 
02437     setHeight( height );
02438     setWidth( width );
02439     if ( count == 1 ) {
02440         setBaseline( content.at( 0 )->getBaseline() );
02441     }
02442     else {
02443         // There's always a first line. No formulas without lines.
02444         setBaseline( height/2 + context.axisHeight( tstyle, factor ) );
02445     }
02446 }
02447 
02448 void MultilineElement::draw( QPainter& painter, const LuPixelRect& r,
02449                              const ContextStyle& context,
02450                              ContextStyle::TextStyle tstyle,
02451                              ContextStyle::IndexStyle istyle,
02452                              StyleAttributes& style,
02453                              const LuPixelPoint& parentOrigin )
02454 {
02455     LuPixelPoint myPos( parentOrigin.x() + getX(), parentOrigin.y() + getY() );
02456     uint count = content.count();
02457 
02458     if ( context.edit() ) {
02459         uint tabCount = 0;
02460         painter.setPen( context.getHelpColor() );
02461         for ( uint i = 0; i < count; ++i ) {
02462             MultilineSequenceElement* line = content.at(i);
02463             if ( tabCount < line->tabCount() ) {
02464                 for ( uint t = tabCount; t < line->tabCount(); ++t ) {
02465                     BasicElement* marker = line->tab( t );
02466                     painter.drawLine( context.layoutUnitToPixelX( myPos.x()+marker->getX() ),
02467                                       context.layoutUnitToPixelY( myPos.y() ),
02468                                       context.layoutUnitToPixelX( myPos.x()+marker->getX() ),
02469                                       context.layoutUnitToPixelY( myPos.y()+getHeight() ) );
02470                 }
02471                 tabCount = line->tabCount();
02472             }
02473         }
02474     }
02475 
02476     for ( uint i = 0; i < count; ++i ) {
02477         MultilineSequenceElement* line = content.at(i);
02478         line->draw( painter, r, context, tstyle, istyle, style, myPos );
02479     }
02480 }
02481 
02482 
02483 void MultilineElement::dispatchFontCommand( FontCommand* cmd )
02484 {
02485     uint count = content.count();
02486     for ( uint i = 0; i < count; ++i ) {
02487         MultilineSequenceElement* line = content.at(i);
02488         line->dispatchFontCommand( cmd );
02489     }
02490 }
02491 
02492 void MultilineElement::insert( FormulaCursor* cursor,
02493                                QPtrList<BasicElement>& newChildren,
02494                                Direction direction )
02495 {
02496     MultilineSequenceElement* e = static_cast<MultilineSequenceElement*>(newChildren.take(0));
02497     e->setParent(this);
02498     content.insert( cursor->getPos(), e );
02499 
02500     if (direction == beforeCursor) {
02501         e->moveLeft(cursor, this);
02502     }
02503     else {
02504         e->moveRight(cursor, this);
02505     }
02506     cursor->setSelection(false);
02507     formula()->changed();
02508 }
02509 
02510 void MultilineElement::remove( FormulaCursor* cursor,
02511                                QPtrList<BasicElement>& removedChildren,
02512                                Direction direction )
02513 {
02514     if ( content.count() == 1 ) { //&& ( cursor->getPos() == 0 ) ) {
02515         getParent()->selectChild(cursor, this);
02516         getParent()->remove(cursor, removedChildren, direction);
02517     }
02518     else {
02519         MultilineSequenceElement* e = content.take( cursor->getPos() );
02520         removedChildren.append( e );
02521         formula()->elementRemoval( e );
02522         //cursor->setTo( this, denominatorPos );
02523         formula()->changed();
02524     }
02525 }
02526 
02527 void MultilineElement::normalize( FormulaCursor* cursor, Direction direction )
02528 {
02529     int pos = cursor->getPos();
02530     if ( ( cursor->getElement() == this ) &&
02531          ( pos > -1 ) && ( static_cast<unsigned>( pos ) <= content.count() ) ) {
02532         switch ( direction ) {
02533         case beforeCursor:
02534             if ( pos > 0 ) {
02535                 content.at( pos-1 )->moveLeft( cursor, this );
02536                 break;
02537             }
02538             // no break! intended!
02539         case afterCursor:
02540             if ( static_cast<unsigned>( pos ) < content.count() ) {
02541                 content.at( pos )->moveRight( cursor, this );
02542             }
02543             else {
02544                 content.at( pos-1 )->moveLeft( cursor, this );
02545             }
02546             break;
02547         }
02548     }
02549     else {
02550         inherited::normalize( cursor, direction );
02551     }
02552 }
02553 
02554 SequenceElement* MultilineElement::getMainChild()
02555 {
02556     return content.at( 0 );
02557 }
02558 
02559 void MultilineElement::selectChild(FormulaCursor* cursor, BasicElement* child)
02560 {
02561     int pos = content.find( dynamic_cast<MultilineSequenceElement*>( child ) );
02562     if ( pos > -1 ) {
02563         cursor->setTo( this, pos );
02564         //content.at( pos )->moveRight( cursor, this );
02565     }
02566 }
02567 
02568 
02572 void MultilineElement::writeDom(QDomElement element)
02573 {
02574     BasicElement::writeDom(element);
02575 
02576     uint lineCount = content.count();
02577     element.setAttribute( "LINES", lineCount );
02578 
02579     QDomDocument doc = element.ownerDocument();
02580     for ( uint i = 0; i < lineCount; ++i ) {
02581         QDomElement tmp = content.at( i )->getElementDom(doc);
02582         element.appendChild(tmp);
02583     }
02584 }
02585 
02586 void MultilineElement::writeMathML( QDomDocument& doc, QDomNode& parent, bool oasisFormat ) const
02587 {
02588     QDomElement de = doc.createElement( oasisFormat ? "math:mtable" : "mtable" );
02589     QDomElement row; QDomElement cell;
02590 
02591     for ( QPtrListIterator < MultilineSequenceElement > it( content ); it.current(); ++it ) {
02592         row = doc.createElement( oasisFormat ? "math:mtr" : "mtr" );
02593         de.appendChild( row );
02594         //cell = doc.createElement( "mtd" );
02595         //row.appendChild( cell );
02596 
02597         //content.at( i )->writeMathML( doc, cell );
02598         it.current()->writeMathML( doc, row, oasisFormat );
02599     }
02600 
02601     parent.appendChild( de );
02602 }
02603 
02608 bool MultilineElement::readAttributesFromDom(QDomElement element)
02609 {
02610     if (!BasicElement::readAttributesFromDom(element)) {
02611         return false;
02612     }
02613     uint lineCount = 0;
02614     QString lineCountStr = element.attribute("LINES");
02615     if(!lineCountStr.isNull()) {
02616         lineCount = lineCountStr.toInt();
02617     }
02618     if (lineCount == 0) {
02619         kdWarning( DEBUGID ) << "lineCount <= 0 in MultilineElement." << endl;
02620         return false;
02621     }
02622 
02623     content.clear();
02624     for ( uint i = 0; i < lineCount; ++i ) {
02625         MultilineSequenceElement* element = new MultilineSequenceElement(this);
02626         content.append(element);
02627     }
02628     return true;
02629 }
02630 
02636 bool MultilineElement::readContentFromDom(QDomNode& node)
02637 {
02638     if (!BasicElement::readContentFromDom(node)) {
02639         return false;
02640     }
02641 
02642     uint lineCount = content.count();
02643     uint i = 0;
02644     while ( !node.isNull() && i < lineCount ) {
02645         if ( node.isElement() ) {
02646             SequenceElement* element = content.at( i );
02647             QDomElement e = node.toElement();
02648             if ( !element->buildFromDom( e ) ) {
02649                 return false;
02650             }
02651             ++i;
02652         }
02653         node = node.nextSibling();
02654     }
02655     return true;
02656 }
02657 
02658 QString MultilineElement::toLatex()
02659 {
02660     uint lineCount = content.count();
02661     QString muliline = "\\begin{split} ";
02662     for ( uint i = 0; i < lineCount; ++i ) {
02663         muliline += content.at( i )->toLatex();
02664         muliline += " \\\\ ";
02665     }
02666     muliline += "\\end{split}";
02667     return muliline;
02668 }
02669 
02670 // Does this make any sense at all?
02671 QString MultilineElement::formulaString()
02672 {
02673     uint lineCount = content.count();
02674     QString muliline = "";
02675     for ( uint i = 0; i < lineCount; ++i ) {
02676         muliline += content.at( i )->formulaString();
02677         muliline += "\n";
02678     }
02679     //muliline += "";
02680     return muliline;
02681 }
02682 
02683 
02684 KFORMULA_NAMESPACE_END
KDE Home | KDE Accessibility Home | Description of Access Keys