00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
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
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
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
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 )
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
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 const ContextStyle& context = document()->getContextStyle();
00551 MathML2KFormula filter( doc, context, oasisFormat );
00552 filter.startConversion();
00553 if (filter.m_error) {
00554 return false;
00555 }
00556
00557 if ( load( filter.getKFormulaDom().documentElement() ) ) {
00558 getHistory()->clear();
00559 return true;
00560 }
00561 return false;
00562 }
00563
00564
00565 void Container::print(KPrinter& printer)
00566 {
00567
00568 QPainter painter;
00569 if (painter.begin(&printer)) {
00570 rootElement()->draw( painter, LuPixelRect( rootElement()->getX(),
00571 rootElement()->getY(),
00572 rootElement()->getWidth(),
00573 rootElement()->getHeight() ),
00574 document()->getContextStyle( false ) );
00575 }
00576 }
00577
00578 QImage Container::drawImage( int width, int height )
00579 {
00580 ContextStyle& context = document()->getContextStyle( false );
00581 QRect rect(impl->rootElement->getX(), impl->rootElement->getY(),
00582 impl->rootElement->getWidth(), impl->rootElement->getHeight());
00583
00584 int realWidth = context.layoutUnitToPixelX( impl->rootElement->getWidth() );
00585 int realHeight = context.layoutUnitToPixelY( impl->rootElement->getHeight() );
00586
00587 double f = QMAX( static_cast<double>( width )/static_cast<double>( realWidth ),
00588 static_cast<double>( height )/static_cast<double>( realHeight ) );
00589
00590 int oldZoom = context.zoom();
00591 context.setZoomAndResolution( qRound( oldZoom*f ), KoGlobal::dpiX(), KoGlobal::dpiY() );
00592
00593 kdDebug( DEBUGID ) << "Container::drawImage "
00594 << "(" << width << " " << height << ")"
00595 << "(" << context.layoutUnitToPixelX( impl->rootElement->getWidth() )
00596 << " " << context.layoutUnitToPixelY( impl->rootElement->getHeight() ) << ")"
00597 << endl;
00598
00599 QPixmap pm( context.layoutUnitToPixelX( impl->rootElement->getWidth() ),
00600 context.layoutUnitToPixelY( impl->rootElement->getHeight() ) );
00601 pm.fill();
00602 QPainter paint(&pm);
00603 impl->rootElement->draw(paint, rect, context);
00604 paint.end();
00605 context.setZoomAndResolution( oldZoom, KoGlobal::dpiX(), KoGlobal::dpiY() );
00606
00607 return pm.convertToImage();
00608 }
00609
00610 QString Container::texString()
00611 {
00612 return rootElement()->toLatex();
00613 }
00614
00615 QString Container::formulaString()
00616 {
00617 return rootElement()->formulaString();
00618 }
00619
00620 KFORMULA_NAMESPACE_END
00621
00622 using namespace KFormula;
00623 #include "kformulacontainer.moc"