/* This file is part of the KDE project Copyright (C) 2001 Andrea Rizzi Ulrich Kuettler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "KoGlobal.h" #include "bracketelement.h" #include "contextstyle.h" #include "formulacursor.h" #include "formulaelement.h" #include "fractionelement.h" #include "indexelement.h" #include "kformulacommand.h" #include "kformulacompatibility.h" #include "kformulacontainer.h" #include "kformuladocument.h" #include "kformulamathmlread.h" #include "kformulamimesource.h" #include "matrixelement.h" #include "rootelement.h" #include "sequenceelement.h" #include "symbolelement.h" #include "symboltable.h" #include "spaceelement.h" #include "textelement.h" #include KFORMULA_NAMESPACE_BEGIN using namespace std; struct Container::Container_Impl { Container_Impl( Document* doc ) : dirty( true ), cursorMoved( false ), document( doc ) { } ~Container_Impl() { delete internCursor; delete rootElement; document = 0; } /** * If true we need to recalc the formula. */ bool dirty; /** * Tells whether a request caused the cursor to move. */ bool cursorMoved; /** * The element tree's root. */ FormulaElement* rootElement; /** * The active cursor is the one that triggered the last command. */ FormulaCursor* activeCursor; /** * The cursor that is used if there is no view. */ FormulaCursor* internCursor; /** * The document we belong to. */ Document* document; }; FormulaElement* Container::rootElement() const { return impl->rootElement; } Document* Container::document() const { return impl->document; } Container::Container( Document* doc, int pos, bool registerMe ) { impl = new Container_Impl( doc ); impl->rootElement = 0; if ( registerMe ) { registerFormula( pos ); } } Container::~Container() { unregisterFormula(); delete impl; impl = 0; } void Container::initialize() { assert( impl->rootElement == 0 ); impl->rootElement = createMainSequence(); impl->activeCursor = impl->internCursor = createCursor(); recalc(); } FormulaElement* Container::createMainSequence() { return new FormulaElement( this ); } FormulaCursor* Container::createCursor() { return new FormulaCursor(rootElement()); } KoCommandHistory* Container::getHistory() const { return document()->getHistory(); } /** * Gets called just before the child is removed from * the element tree. */ void Container::elementRemoval(BasicElement* child) { emit elementWillVanish(child); } /** * Gets called whenever something changes and we need to * recalc. */ void Container::changed() { impl->dirty = true; } void Container::cursorHasMoved( FormulaCursor* ) { impl->cursorMoved = true; } void Container::moveOutLeft( FormulaCursor* cursor ) { emit leaveFormula( this, cursor, EXIT_LEFT ); } void Container::moveOutRight( FormulaCursor* cursor ) { emit leaveFormula( this, cursor, EXIT_RIGHT ); } void Container::moveOutAbove( FormulaCursor* cursor ) { emit leaveFormula( this, cursor, EXIT_ABOVE ); } void Container::moveOutBelow( FormulaCursor* cursor ) { emit leaveFormula( this, cursor, EXIT_BELOW ); } void Container::tell( const TQString& msg ) { emit statusMsg( msg ); } void Container::removeFormula( FormulaCursor* cursor ) { emit leaveFormula( this, cursor, REMOVE_FORMULA ); } void Container::registerFormula( int pos ) { document()->registerFormula( this, pos ); } void Container::unregisterFormula() { document()->unregisterFormula( this ); } void Container::baseSizeChanged( int size, bool owned ) { if ( owned ) { emit baseSizeChanged( size ); } else { const ContextStyle& context = document()->getContextStyle(); emit baseSizeChanged( context.baseSize() ); } } FormulaCursor* Container::activeCursor() { return impl->activeCursor; } const FormulaCursor* Container::activeCursor() const { return impl->activeCursor; } /** * Tells the formula that a view got the focus and might want to * edit the formula. */ void Container::setActiveCursor(FormulaCursor* cursor) { document()->activate(this); if (cursor != 0) { impl->activeCursor = cursor; } else { *(impl->internCursor) = *(impl->activeCursor); impl->activeCursor = impl->internCursor; } } bool Container::hasValidCursor() const { return (impl->activeCursor != 0) && !impl->activeCursor->isReadOnly(); } void Container::testDirty() { if (impl->dirty) { recalc(); } } void Container::recalc() { impl->dirty = false; ContextStyle& context = impl->document->getContextStyle(); rootElement()->calcSizes( context ); emit formulaChanged( context.tqlayoutUnitToPixelX( rootElement()->getWidth() ), context.tqlayoutUnitToPixelY( rootElement()->getHeight() ) ); emit formulaChanged( context.tqlayoutUnitPtToPt( context.pixelXToPt( rootElement()->getWidth() ) ), context.tqlayoutUnitPtToPt( context.pixelYToPt( rootElement()->getHeight() ) ) ); emit cursorMoved( activeCursor() ); } bool Container::isEmpty() { return rootElement()->countChildren() == 0; } const SymbolTable& Container::getSymbolTable() const { return document()->getSymbolTable(); } void Container::draw( TQPainter& painter, const TQRect& r, const TQColorGroup& cg, bool edit ) { painter.fillRect( r, cg.base() ); draw( painter, r, edit ); } void Container::draw( TQPainter& painter, const TQRect& r, bool edit ) { //ContextStyle& context = document()->getContextStyle( painter.device()->devType() == TQInternal::Printer ); ContextStyle& context = document()->getContextStyle( edit ); rootElement()->draw( painter, context.pixelToLayoutUnit( r ), context ); } void Container::checkCursor() { if ( impl->cursorMoved ) { impl->cursorMoved = false; emit cursorMoved( activeCursor() ); } } void Container::input( TQKeyEvent* event ) { //if ( !hasValidCursor() ) if ( impl->activeCursor == 0 ) { return; } execute( activeCursor()->getElement()->input( this, event ) ); checkCursor(); } void Container::performRequest( Request* request ) { if ( !hasValidCursor() ) return; execute( activeCursor()->getElement()->buildCommand( this, request ) ); checkCursor(); } void Container::paste() { if (!hasValidCursor()) return; TQClipboard* clipboard = TQApplication::tqclipboard(); const TQMimeSource* source = clipboard->data(); if (source->provides( MimeSource::selectionMimeType() )) { TQByteArray data = source->tqencodedData( MimeSource::selectionMimeType() ); TQDomDocument formula; formula.setContent(data); paste( formula, i18n("Paste") ); } } void Container::paste( const TQDomDocument& document, TQString desc ) { FormulaCursor* cursor = activeCursor(); TQPtrList list; list.setAutoDelete( true ); if ( cursor->buildElementsFromMathMLDom( document.documentElement(), list ) ) { uint count = list.count(); // You must not execute an add command that adds nothing. if (count > 0) { KFCReplace* command = new KFCReplace( desc, this ); for (uint i = 0; i < count; i++) { command->addElement(list.take(0)); } execute(command); } } } void Container::copy() { // read-only cursors are fine for copying. FormulaCursor* cursor = activeCursor(); if (cursor != 0) { TQDomDocument formula = document()->createMathMLDomDocument(); cursor->copy( formula ); TQClipboard* clipboard = TQApplication::tqclipboard(); clipboard->setData(new MimeSource(document(), formula)); } } void Container::cut() { if (!hasValidCursor()) return; FormulaCursor* cursor = activeCursor(); if (cursor->isSelection()) { copy(); DirectedRemove r( req_remove, beforeCursor ); performRequest( &r ); } } void Container::emitErrorMsg( const TQString& msg ) { emit errorMsg( msg ); } void Container::execute(KCommand* command) { if ( command != 0 ) { getHistory()->addCommand(command); } } TQRect Container::boundingRect() const { const ContextStyle& context = document()->getContextStyle(); return TQRect( context.tqlayoutUnitToPixelX( rootElement()->getX() ), context.tqlayoutUnitToPixelY( rootElement()->getY() ), context.tqlayoutUnitToPixelX( rootElement()->getWidth() ), context.tqlayoutUnitToPixelY( rootElement()->getHeight() ) ); } TQRect Container::coveredRect() { if ( impl->activeCursor != 0 ) { const ContextStyle& context = document()->getContextStyle(); const LuPixelRect& cursorRect = impl->activeCursor->getCursorSize(); return TQRect( context.tqlayoutUnitToPixelX( rootElement()->getX() ), context.tqlayoutUnitToPixelY( rootElement()->getY() ), context.tqlayoutUnitToPixelX( rootElement()->getWidth() ), context.tqlayoutUnitToPixelY( rootElement()->getHeight() ) ) | TQRect( context.tqlayoutUnitToPixelX( cursorRect.x() ), context.tqlayoutUnitToPixelY( cursorRect.y() ), context.tqlayoutUnitToPixelX( cursorRect.width() ), context.tqlayoutUnitToPixelY( cursorRect.height() ) ); } return boundingRect(); } double Container::width() const { const ContextStyle& context = document()->getContextStyle(); return context.tqlayoutUnitPtToPt( context.pixelXToPt( rootElement()->getWidth() ) ); } double Container::height() const { const ContextStyle& context = document()->getContextStyle(); return context.tqlayoutUnitPtToPt( context.pixelYToPt( rootElement()->getHeight() ) ); } double Container::baseline() const { const ContextStyle& context = document()->getContextStyle(); //return context.tqlayoutUnitToPixelY( rootElement()->getBaseline() ); return context.tqlayoutUnitPtToPt( context.pixelYToPt( rootElement()->getBaseline() ) ); } void Container::moveTo( int x, int y ) { const ContextStyle& context = document()->getContextStyle(); rootElement()->setX( context.pixelToLayoutUnitX( x ) ); rootElement()->setY( context.pixelToLayoutUnitY( y ) ); } int Container::fontSize() const { if ( rootElement()->hasOwnBaseSize() ) { return rootElement()->getBaseSize(); } else { const ContextStyle& context = document()->getContextStyle(); return tqRound( context.baseSize() ); } } void Container::setFontSize( int pointSize, bool /*forPrint*/ ) { if ( rootElement()->getBaseSize() != pointSize ) { execute( new KFCChangeBaseSize( i18n( "Base Size Change" ), this, rootElement(), pointSize ) ); } } void Container::setFontSizeDirect( int pointSize ) { rootElement()->setBaseSize( pointSize ); recalc(); } void Container::updateMatrixActions() { BasicElement *currentElement = activeCursor()->getElement(); if ( ( currentElement = currentElement->getParent() ) != 0 ) document()->wrapper()->enableMatrixActions( dynamic_cast(currentElement) ); else document()->wrapper()->enableMatrixActions( false ); } void Container::save( TQDomElement &root ) { TQDomDocument ownerDoc = root.ownerDocument(); root.appendChild(rootElement()->getElementDom(ownerDoc)); } /** * Loads a formula from the document. */ bool Container::load( const TQDomElement &fe ) { if (!fe.isNull()) { FormulaElement* root = createMainSequence(); if (root->buildFromDom(fe)) { delete impl->rootElement; impl->rootElement = root; emit formulaLoaded(rootElement()); recalc(); return true; } else { delete root; kdWarning( DEBUGID ) << "Error constructing element tree." << endl; } } else { kdWarning( DEBUGID ) << "Empty element." << endl; } return false; } void Container::saveMathML( TQTextStream& stream, bool oasisFormat ) { TQDomDocument doc; if ( !oasisFormat ) { doc = document()->createMathMLDomDocument(); } rootElement()->writeMathML( doc, doc, oasisFormat ); stream << doc; } bool Container::loadMathML( const TQDomDocument &doc, bool oasisFormat ) { return loadMathML( doc.documentElement(), oasisFormat ); } /* bool Container::loadMathML( const TQDomElement &element, bool oasisFormat ) { const ContextStyle& context = document()->getContextStyle(); MathML2KFormula filter( element, context, oasisFormat ); filter.startConversion(); if (filter.m_error) { return false; } if ( load( filter.getKFormulaDom().documentElement() ) ) { getHistory()->clear(); return true; } return false; } */ bool Container::loadMathML( const TQDomElement &fe, bool /*oasisFormat*/ ) { kdDebug( DEBUGID ) << "loadMathML" << endl; if (!fe.isNull()) { FormulaElement* root = createMainSequence(); if ( root->buildFromMathMLDom( fe ) != - 1) { delete impl->rootElement; impl->rootElement = root; emit formulaLoaded(rootElement()); recalc(); return true; } else { delete root; kdWarning( DEBUGID ) << "Error constructing element tree." << endl; } } else { kdWarning( DEBUGID ) << "Empty element." << endl; } return false; } void Container::print(KPrinter& printer) { //printer.setFullPage(true); TQPainter painter; if (painter.begin(&printer)) { rootElement()->draw( painter, LuPixelRect( rootElement()->getX(), rootElement()->getY(), rootElement()->getWidth(), rootElement()->getHeight() ), document()->getContextStyle( false ) ); } } TQImage Container::drawImage( int width, int height ) { ContextStyle& context = document()->getContextStyle( false ); TQRect rect(impl->rootElement->getX(), impl->rootElement->getY(), impl->rootElement->getWidth(), impl->rootElement->getHeight()); int realWidth = context.tqlayoutUnitToPixelX( impl->rootElement->getWidth() ); int realHeight = context.tqlayoutUnitToPixelY( impl->rootElement->getHeight() ); double f = TQMAX( static_cast( width )/static_cast( realWidth ), static_cast( height )/static_cast( realHeight ) ); int oldZoom = context.zoom(); context.setZoomAndResolution( tqRound( oldZoom*f ), KoGlobal::dpiX(), KoGlobal::dpiY() ); kdDebug( DEBUGID ) << "Container::drawImage " << "(" << width << " " << height << ")" << "(" << context.tqlayoutUnitToPixelX( impl->rootElement->getWidth() ) << " " << context.tqlayoutUnitToPixelY( impl->rootElement->getHeight() ) << ")" << endl; TQPixmap pm( context.tqlayoutUnitToPixelX( impl->rootElement->getWidth() ), context.tqlayoutUnitToPixelY( impl->rootElement->getHeight() ) ); pm.fill(); TQPainter paint(&pm); impl->rootElement->draw(paint, rect, context); paint.end(); context.setZoomAndResolution( oldZoom, KoGlobal::dpiX(), KoGlobal::dpiY() ); //return pm.convertToImage().smoothScale( width, height ); return pm.convertToImage(); } TQString Container::texString() { return rootElement()->toLatex(); } TQString Container::formulaString() { return rootElement()->formulaString(); } KFORMULA_NAMESPACE_END using namespace KFormula; #include "kformulacontainer.moc"