lib

kformulacontainer.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 <qapplication.h>
00022 #include <qdom.h>
00023 #include <qevent.h>
00024 #include <qfile.h>
00025 #include <qpainter.h>
00026 #include <qpixmap.h>
00027 #include <qstring.h>
00028 #include <qtextstream.h>
00029 
00030 #include <kdebug.h>
00031 #include <klocale.h>
00032 #include <kprinter.h>
00033 
00034 #include "KoGlobal.h"
00035 #include "bracketelement.h"
00036 #include "contextstyle.h"
00037 #include "formulacursor.h"
00038 #include "formulaelement.h"
00039 #include "fractionelement.h"
00040 #include "indexelement.h"
00041 #include "kformulacommand.h"
00042 #include "kformulacompatibility.h"
00043 #include "kformulacontainer.h"
00044 #include "kformuladocument.h"
00045 #include "kformulamathmlread.h"
00046 #include "kformulamimesource.h"
00047 #include "matrixelement.h"
00048 #include "rootelement.h"
00049 #include "sequenceelement.h"
00050 #include "symbolelement.h"
00051 #include "symboltable.h"
00052 #include "spaceelement.h"
00053 #include "textelement.h"
00054 
00055 #include <assert.h>
00056 
00057 KFORMULA_NAMESPACE_BEGIN
00058 using namespace std;
00059 
00060 
00061 struct Container::Container_Impl {
00062 
00063     Container_Impl( Document* doc )
00064             : dirty( true ), cursorMoved( false ), document( doc )
00065     {
00066     }
00067 
00068     ~Container_Impl()
00069     {
00070         delete internCursor;
00071         delete rootElement;
00072         document = 0;
00073     }
00074 
00078     bool dirty;
00079 
00083     bool cursorMoved;
00084 
00088     FormulaElement* rootElement;
00089 
00093     FormulaCursor* activeCursor;
00094 
00098     FormulaCursor* internCursor;
00099 
00103     Document* document;
00104 };
00105 
00106 
00107 FormulaElement* Container::rootElement() const { return impl->rootElement; }
00108 Document* Container::document() const { return impl->document; }
00109 
00110 Container::Container( Document* doc, int pos, bool registerMe )
00111 {
00112     impl = new Container_Impl( doc );
00113     impl->rootElement = 0;
00114     if ( registerMe ) {
00115         registerFormula( pos );
00116     }
00117 }
00118 
00119 Container::~Container()
00120 {
00121     unregisterFormula();
00122     delete impl;
00123     impl = 0;
00124 }
00125 
00126 
00127 void Container::initialize()
00128 {
00129     assert( impl->rootElement == 0 );
00130     impl->rootElement = createMainSequence();
00131     impl->activeCursor = impl->internCursor = createCursor();
00132     recalc();
00133 }
00134 
00135 
00136 FormulaElement* Container::createMainSequence()
00137 {
00138     return new FormulaElement( this );
00139 }
00140 
00141 
00142 FormulaCursor* Container::createCursor()
00143 {
00144     return new FormulaCursor(rootElement());
00145 }
00146 
00147 
00148 KoCommandHistory* Container::getHistory() const
00149 {
00150     return document()->getHistory();
00151 }
00152 
00153 
00158 void Container::elementRemoval(BasicElement* child)
00159 {
00160     emit elementWillVanish(child);
00161 }
00162 
00167 void Container::changed()
00168 {
00169     impl->dirty = true;
00170 }
00171 
00172 void Container::cursorHasMoved( FormulaCursor* )
00173 {
00174     impl->cursorMoved = true;
00175 }
00176 
00177 void Container::moveOutLeft( FormulaCursor* cursor )
00178 {
00179     emit leaveFormula( this, cursor, EXIT_LEFT );
00180 }
00181 
00182 void Container::moveOutRight( FormulaCursor* cursor )
00183 {
00184     emit leaveFormula( this, cursor, EXIT_RIGHT );
00185 }
00186 
00187 void Container::moveOutAbove( FormulaCursor* cursor )
00188 {
00189     emit leaveFormula( this, cursor, EXIT_ABOVE );
00190 }
00191 
00192 void Container::moveOutBelow( FormulaCursor* cursor )
00193 {
00194     emit leaveFormula( this, cursor, EXIT_BELOW );
00195 }
00196 
00197 void Container::tell( const QString& msg )
00198 {
00199     emit statusMsg( msg );
00200 }
00201 
00202 void Container::removeFormula( FormulaCursor* cursor )
00203 {
00204     emit leaveFormula( this, cursor, REMOVE_FORMULA );
00205 }
00206 
00207 
00208 void Container::registerFormula( int pos )
00209 {
00210     document()->registerFormula( this, pos );
00211 }
00212 
00213 void Container::unregisterFormula()
00214 {
00215     document()->unregisterFormula( this );
00216 }
00217 
00218 
00219 void Container::baseSizeChanged( int size, bool owned )
00220 {
00221     if ( owned ) {
00222         emit baseSizeChanged( size );
00223     }
00224     else {
00225         const ContextStyle& context = document()->getContextStyle();
00226         emit baseSizeChanged( context.baseSize() );
00227     }
00228 }
00229 
00230 FormulaCursor* Container::activeCursor()
00231 {
00232     return impl->activeCursor;
00233 }
00234 
00235 const FormulaCursor* Container::activeCursor() const
00236 {
00237     return impl->activeCursor;
00238 }
00239 
00240 
00245 void Container::setActiveCursor(FormulaCursor* cursor)
00246 {
00247     document()->activate(this);
00248     if (cursor != 0) {
00249         impl->activeCursor = cursor;
00250     }
00251     else {
00252         *(impl->internCursor) = *(impl->activeCursor);
00253         impl->activeCursor = impl->internCursor;
00254     }
00255 }
00256 
00257 
00258 bool Container::hasValidCursor() const
00259 {
00260     return (impl->activeCursor != 0) && !impl->activeCursor->isReadOnly();
00261 }
00262 
00263 void Container::testDirty()
00264 {
00265     if (impl->dirty) {
00266         recalc();
00267     }
00268 }
00269 
00270 void Container::recalc()
00271 {
00272     impl->dirty = false;
00273     ContextStyle& context = impl->document->getContextStyle();
00274     rootElement()->calcSizes( context );
00275 
00276     emit formulaChanged( context.layoutUnitToPixelX( rootElement()->getWidth() ),
00277                          context.layoutUnitToPixelY( rootElement()->getHeight() ) );
00278     emit formulaChanged( context.layoutUnitPtToPt( context.pixelXToPt( rootElement()->getWidth() ) ),
00279                          context.layoutUnitPtToPt( context.pixelYToPt( rootElement()->getHeight() ) ) );
00280     emit cursorMoved( activeCursor() );
00281 }
00282 
00283 bool Container::isEmpty()
00284 {
00285     return rootElement()->countChildren() == 0;
00286 }
00287 
00288 
00289 const SymbolTable& Container::getSymbolTable() const
00290 {
00291     return document()->getSymbolTable();
00292 }
00293 
00294 
00295 void Container::draw( QPainter& painter, const QRect& r, const QColorGroup& cg, bool edit )
00296 {
00297     painter.fillRect( r, cg.base() );
00298     draw( painter, r, edit );
00299 }
00300 
00301 
00302 void Container::draw( QPainter& painter, const QRect& r, bool edit )
00303 {
00304     //ContextStyle& context = document()->getContextStyle( painter.device()->devType() == QInternal::Printer );
00305     ContextStyle& context = document()->getContextStyle( edit );
00306     rootElement()->draw( painter, context.pixelToLayoutUnit( r ), context );
00307 }
00308 
00309 
00310 void Container::checkCursor()
00311 {
00312     if ( impl->cursorMoved ) {
00313         impl->cursorMoved = false;
00314         emit cursorMoved( activeCursor() );
00315     }
00316 }
00317 
00318 void Container::input( QKeyEvent* event )
00319 {
00320     //if ( !hasValidCursor() )
00321     if ( impl->activeCursor == 0 ) {
00322         return;
00323     }
00324     execute( activeCursor()->getElement()->input( this, event ) );
00325     checkCursor();
00326 }
00327 
00328 
00329 void Container::performRequest( Request* request )
00330 {
00331     if ( !hasValidCursor() )
00332         return;
00333     execute( activeCursor()->getElement()->buildCommand( this, request ) );
00334     checkCursor();
00335 }
00336 
00337 
00338 void Container::paste()
00339 {
00340     if (!hasValidCursor())
00341         return;
00342     QClipboard* clipboard = QApplication::clipboard();
00343     const QMimeSource* source = clipboard->data();
00344     if (source->provides( MimeSource::selectionMimeType() )) {
00345         QByteArray data = source->encodedData( MimeSource::selectionMimeType() );
00346         QDomDocument formula;
00347         formula.setContent(data);
00348         paste( formula, i18n("Paste") );
00349     }
00350 }
00351 
00352 void Container::paste( const QDomDocument& document, QString desc )
00353 {
00354     FormulaCursor* cursor = activeCursor();
00355     QPtrList<BasicElement> list;
00356     list.setAutoDelete( true );
00357     if ( cursor->buildElementsFromDom( document.documentElement(), list ) ) {
00358         uint count = list.count();
00359         // You must not execute an add command that adds nothing.
00360         if (count > 0) {
00361             KFCReplace* command = new KFCReplace( desc, this );
00362             for (uint i = 0; i < count; i++) {
00363                 command->addElement(list.take(0));
00364             }
00365             execute(command);
00366         }
00367     }
00368 }
00369 
00370 void Container::copy()
00371 {
00372     // read-only cursors are fine for copying.
00373     FormulaCursor* cursor = activeCursor();
00374     if (cursor != 0) {
00375         QDomDocument formula = document()->createDomDocument();
00376         cursor->copy( formula );
00377         QClipboard* clipboard = QApplication::clipboard();
00378         clipboard->setData(new MimeSource(document(), formula));
00379     }
00380 }
00381 
00382 void Container::cut()
00383 {
00384     if (!hasValidCursor())
00385         return;
00386     FormulaCursor* cursor = activeCursor();
00387     if (cursor->isSelection()) {
00388         copy();
00389         DirectedRemove r( req_remove, beforeCursor );
00390         performRequest( &r );
00391     }
00392 }
00393 
00394 
00395 void Container::emitErrorMsg( const QString& msg )
00396 {
00397     emit errorMsg( msg );
00398 }
00399 
00400 void Container::execute(KCommand* command)
00401 {
00402     if ( command != 0 ) {
00403         getHistory()->addCommand(command);
00404     }
00405 }
00406 
00407 
00408 QRect Container::boundingRect() const
00409 {
00410     const ContextStyle& context = document()->getContextStyle();
00411     return QRect( context.layoutUnitToPixelX( rootElement()->getX() ),
00412                   context.layoutUnitToPixelY( rootElement()->getY() ),
00413                   context.layoutUnitToPixelX( rootElement()->getWidth() ),
00414                   context.layoutUnitToPixelY( rootElement()->getHeight() ) );
00415 }
00416 
00417 QRect Container::coveredRect()
00418 {
00419     if ( impl->activeCursor != 0 ) {
00420         const ContextStyle& context = document()->getContextStyle();
00421         const LuPixelRect& cursorRect = impl->activeCursor->getCursorSize();
00422         return QRect( context.layoutUnitToPixelX( rootElement()->getX() ),
00423                       context.layoutUnitToPixelY( rootElement()->getY() ),
00424                       context.layoutUnitToPixelX( rootElement()->getWidth() ),
00425                       context.layoutUnitToPixelY( rootElement()->getHeight() ) ) |
00426             QRect( context.layoutUnitToPixelX( cursorRect.x() ),
00427                    context.layoutUnitToPixelY( cursorRect.y() ),
00428                    context.layoutUnitToPixelX( cursorRect.width() ),
00429                    context.layoutUnitToPixelY( cursorRect.height() ) );
00430     }
00431     return boundingRect();
00432 }
00433 
00434 double Container::width() const
00435 {
00436     const ContextStyle& context = document()->getContextStyle();
00437     return context.layoutUnitPtToPt( context.pixelXToPt( rootElement()->getWidth() ) );
00438 }
00439 
00440 double Container::height() const
00441 {
00442     const ContextStyle& context = document()->getContextStyle();
00443     return context.layoutUnitPtToPt( context.pixelYToPt( rootElement()->getHeight() ) );
00444 }
00445 
00446 double Container::baseline() const
00447 {
00448     const ContextStyle& context = document()->getContextStyle();
00449     //return context.layoutUnitToPixelY( rootElement()->getBaseline() );
00450     return context.layoutUnitPtToPt( context.pixelYToPt( rootElement()->getBaseline() ) );
00451 }
00452 
00453 void Container::moveTo( int x, int y )
00454 {
00455     const ContextStyle& context = document()->getContextStyle();
00456     rootElement()->setX( context.pixelToLayoutUnitX( x ) );
00457     rootElement()->setY( context.pixelToLayoutUnitY( y ) );
00458 }
00459 
00460 int Container::fontSize() const
00461 {
00462     if ( rootElement()->hasOwnBaseSize() ) {
00463         return rootElement()->getBaseSize();
00464     }
00465     else {
00466         const ContextStyle& context = document()->getContextStyle();
00467         return qRound( context.baseSize() );
00468     }
00469 }
00470 
00471 void Container::setFontSize( int pointSize, bool /*forPrint*/ )
00472 {
00473     if ( rootElement()->getBaseSize() != pointSize ) {
00474         execute( new KFCChangeBaseSize( i18n( "Base Size Change" ), this, rootElement(), pointSize ) );
00475     }
00476 }
00477 
00478 void Container::setFontSizeDirect( int pointSize )
00479 {
00480     rootElement()->setBaseSize( pointSize );
00481     recalc();
00482 }
00483 
00484 void Container::updateMatrixActions()
00485 {
00486     BasicElement *currentElement = activeCursor()->getElement();
00487     if ( ( currentElement = currentElement->getParent() ) != 0 )
00488         document()->wrapper()->enableMatrixActions( dynamic_cast<MatrixElement*>(currentElement) );
00489     else
00490         document()->wrapper()->enableMatrixActions( false );
00491 }
00492 
00493 void Container::save( QDomElement &root )
00494 {
00495     QDomDocument ownerDoc = root.ownerDocument();
00496     root.appendChild(rootElement()->getElementDom(ownerDoc));
00497 }
00498 
00499 
00503 bool Container::load( const QDomElement &fe )
00504 {
00505     if (!fe.isNull()) {
00506         FormulaElement* root = createMainSequence();
00507         if (root->buildFromDom(fe)) {
00508             delete impl->rootElement;
00509             impl->rootElement = root;
00510             emit formulaLoaded(rootElement());
00511 
00512             recalc();
00513             return true;
00514         }
00515         else {
00516             delete root;
00517             kdWarning( DEBUGID ) << "Error constructing element tree." << endl;
00518         }
00519     }
00520     else {
00521         kdWarning( DEBUGID ) << "Empty element." << endl;
00522     }
00523     return false;
00524 }
00525 
00526 
00527 void Container::saveMathML( QTextStream& stream, bool oasisFormat )
00528 {
00529     if ( !oasisFormat )
00530     {
00531         // ### TODO: Are we really using MathML 2.0 or would be MathMl 1.01 enough (like for OO)?
00532         QDomDocumentType dt = QDomImplementation().createDocumentType( "math",
00533                                                                        "-//W3C//DTD MathML 2.0//EN",
00534                                                                        "http://www.w3.org/TR/MathML2/dtd/mathml2.dtd");
00535         QDomDocument doc( dt );
00536         doc.insertBefore( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ), doc.documentElement() );
00537         rootElement()->writeMathML( doc, doc, oasisFormat );
00538         stream << doc;
00539     }
00540     else
00541     {
00542         QDomDocument doc;
00543         rootElement()->writeMathML( doc, doc, oasisFormat );
00544         stream << doc;
00545     }
00546 }
00547 
00548 bool Container::loadMathML( const QDomDocument &doc, bool oasisFormat )
00549 {
00550     return loadMathML( doc.documentElement(), oasisFormat );
00551 }
00552 
00553 bool Container::loadMathML( const QDomElement &element, bool oasisFormat )
00554 {
00555     const ContextStyle& context = document()->getContextStyle();
00556     MathML2KFormula filter( element, context, oasisFormat );
00557     filter.startConversion();
00558     if (filter.m_error) {
00559         return false;
00560     }
00561 
00562     if ( load( filter.getKFormulaDom().documentElement() ) ) {
00563         getHistory()->clear();
00564         return true;
00565     }
00566     return false;
00567 }
00568 
00569 
00570 void Container::print(KPrinter& printer)
00571 {
00572     //printer.setFullPage(true);
00573     QPainter painter;
00574     if (painter.begin(&printer)) {
00575         rootElement()->draw( painter, LuPixelRect( rootElement()->getX(),
00576                                                    rootElement()->getY(),
00577                                                    rootElement()->getWidth(),
00578                                                    rootElement()->getHeight() ),
00579                              document()->getContextStyle( false ) );
00580     }
00581 }
00582 
00583 QImage Container::drawImage( int width, int height )
00584 {
00585     ContextStyle& context = document()->getContextStyle( false );
00586     QRect rect(impl->rootElement->getX(), impl->rootElement->getY(),
00587                impl->rootElement->getWidth(), impl->rootElement->getHeight());
00588 
00589     int realWidth = context.layoutUnitToPixelX( impl->rootElement->getWidth() );
00590     int realHeight = context.layoutUnitToPixelY( impl->rootElement->getHeight() );
00591 
00592     double f = QMAX( static_cast<double>( width )/static_cast<double>( realWidth ),
00593                      static_cast<double>( height )/static_cast<double>( realHeight ) );
00594 
00595     int oldZoom = context.zoom();
00596     context.setZoomAndResolution( qRound( oldZoom*f ), KoGlobal::dpiX(), KoGlobal::dpiY() );
00597 
00598     kdDebug( DEBUGID ) << "Container::drawImage "
00599                        << "(" << width << " " << height << ")"
00600                        << "(" << context.layoutUnitToPixelX( impl->rootElement->getWidth() )
00601                        << " " << context.layoutUnitToPixelY( impl->rootElement->getHeight() ) << ")"
00602                        << endl;
00603 
00604     QPixmap pm( context.layoutUnitToPixelX( impl->rootElement->getWidth() ),
00605                 context.layoutUnitToPixelY( impl->rootElement->getHeight() ) );
00606     pm.fill();
00607     QPainter paint(&pm);
00608     impl->rootElement->draw(paint, rect, context);
00609     paint.end();
00610     context.setZoomAndResolution( oldZoom, KoGlobal::dpiX(), KoGlobal::dpiY() );
00611     //return pm.convertToImage().smoothScale( width, height );
00612     return pm.convertToImage();
00613 }
00614 
00615 QString Container::texString()
00616 {
00617     return rootElement()->toLatex();
00618 }
00619 
00620 QString Container::formulaString()
00621 {
00622     return rootElement()->formulaString();
00623 }
00624 
00625 KFORMULA_NAMESPACE_END
00626 
00627 using namespace KFormula;
00628 #include "kformulacontainer.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys