You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/lib/kformula/kformulacontainer.cc

644 lines
17 KiB

/* This file is part of the KDE project
Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
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 <tqapplication.h>
#include <tqdom.h>
#include <tqevent.h>
#include <tqfile.h>
#include <tqpainter.h>
#include <tqpixmap.h>
#include <tqstring.h>
#include <tqtextstream.h>
#include <kdebug.h>
#include <klocale.h>
#include <kprinter.h>
#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 <assert.h>
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.layoutUnitToPixelX( rootElement()->getWidth() ),
context.layoutUnitToPixelY( rootElement()->getHeight() ) );
emit formulaChanged( context.layoutUnitPtToPt( context.pixelXToPt( rootElement()->getWidth() ) ),
context.layoutUnitPtToPt( 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::clipboard();
const TQMimeSource* source = clipboard->data();
if (source->provides( MimeSource::selectionMimeType() )) {
TQByteArray data = source->encodedData( MimeSource::selectionMimeType() );
TQDomDocument formula;
formula.setContent(data);
paste( formula, i18n("Paste") );
}
}
void Container::paste( const TQDomDocument& document, TQString desc )
{
FormulaCursor* cursor = activeCursor();
TQPtrList<BasicElement> 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::clipboard();
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.layoutUnitToPixelX( rootElement()->getX() ),
context.layoutUnitToPixelY( rootElement()->getY() ),
context.layoutUnitToPixelX( rootElement()->getWidth() ),
context.layoutUnitToPixelY( rootElement()->getHeight() ) );
}
TQRect Container::coveredRect()
{
if ( impl->activeCursor != 0 ) {
const ContextStyle& context = document()->getContextStyle();
const LuPixelRect& cursorRect = impl->activeCursor->getCursorSize();
return TQRect( context.layoutUnitToPixelX( rootElement()->getX() ),
context.layoutUnitToPixelY( rootElement()->getY() ),
context.layoutUnitToPixelX( rootElement()->getWidth() ),
context.layoutUnitToPixelY( rootElement()->getHeight() ) ) |
TQRect( context.layoutUnitToPixelX( cursorRect.x() ),
context.layoutUnitToPixelY( cursorRect.y() ),
context.layoutUnitToPixelX( cursorRect.width() ),
context.layoutUnitToPixelY( cursorRect.height() ) );
}
return boundingRect();
}
double Container::width() const
{
const ContextStyle& context = document()->getContextStyle();
return context.layoutUnitPtToPt( context.pixelXToPt( rootElement()->getWidth() ) );
}
double Container::height() const
{
const ContextStyle& context = document()->getContextStyle();
return context.layoutUnitPtToPt( context.pixelYToPt( rootElement()->getHeight() ) );
}
double Container::baseline() const
{
const ContextStyle& context = document()->getContextStyle();
//return context.layoutUnitToPixelY( rootElement()->getBaseline() );
return context.layoutUnitPtToPt( 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<MatrixElement*>(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.layoutUnitToPixelX( impl->rootElement->getWidth() );
int realHeight = context.layoutUnitToPixelY( impl->rootElement->getHeight() );
double f = TQMAX( static_cast<double>( width )/static_cast<double>( realWidth ),
static_cast<double>( height )/static_cast<double>( realHeight ) );
int oldZoom = context.zoom();
context.setZoomAndResolution( tqRound( oldZoom*f ), KoGlobal::dpiX(), KoGlobal::dpiY() );
kdDebug( DEBUGID ) << "Container::drawImage "
<< "(" << width << " " << height << ")"
<< "(" << context.layoutUnitToPixelX( impl->rootElement->getWidth() )
<< " " << context.layoutUnitToPixelY( impl->rootElement->getHeight() ) << ")"
<< endl;
TQPixmap pm( context.layoutUnitToPixelX( impl->rootElement->getWidth() ),
context.layoutUnitToPixelY( 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"