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.
681 lines
19 KiB
681 lines
19 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 <tqpainter.h>
|
|
#include <tqpen.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <tdelocale.h>
|
|
|
|
#include "elementvisitor.h"
|
|
#include "formulacursor.h"
|
|
#include "formulaelement.h"
|
|
#include "kformulacommand.h"
|
|
#include "rootelement.h"
|
|
#include "sequenceelement.h"
|
|
|
|
KFORMULA_NAMESPACE_BEGIN
|
|
|
|
|
|
class RootSequenceElement : public SequenceElement {
|
|
typedef SequenceElement inherited;
|
|
public:
|
|
|
|
RootSequenceElement( BasicElement* parent = 0 ) : SequenceElement( parent ) {}
|
|
virtual RootSequenceElement* clone() {
|
|
return new RootSequenceElement( *this );
|
|
}
|
|
|
|
/**
|
|
* This is called by the container to get a command depending on
|
|
* the current cursor position (this is how the element gets chosen)
|
|
* and the request.
|
|
*
|
|
* @returns the command that performs the requested action with
|
|
* the containers active cursor.
|
|
*/
|
|
virtual KCommand* buildCommand( Container*, Request* );
|
|
};
|
|
|
|
|
|
KCommand* RootSequenceElement::buildCommand( Container* container, Request* request )
|
|
{
|
|
FormulaCursor* cursor = container->activeCursor();
|
|
if ( cursor->isReadOnly() ) {
|
|
return 0;
|
|
}
|
|
|
|
switch ( *request ) {
|
|
case req_addIndex: {
|
|
FormulaCursor* cursor = container->activeCursor();
|
|
if ( cursor->isSelection() ||
|
|
( cursor->getPos() > 0 && cursor->getPos() < countChildren() ) ) {
|
|
break;
|
|
}
|
|
IndexRequest* ir = static_cast<IndexRequest*>( request );
|
|
if ( ir->index() == upperLeftPos ) {
|
|
RootElement* element = static_cast<RootElement*>( getParent() );
|
|
ElementIndexPtr index = element->getIndex();
|
|
if ( !index->hasIndex() ) {
|
|
KFCAddGenericIndex* command = new KFCAddGenericIndex( container, index );
|
|
return command;
|
|
}
|
|
else {
|
|
index->moveToIndex( cursor, afterCursor );
|
|
cursor->setSelection( false );
|
|
formula()->cursorHasMoved( cursor );
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return inherited::buildCommand( container, request );
|
|
}
|
|
|
|
|
|
RootElement::RootElement(BasicElement* parent)
|
|
: BasicElement(parent)
|
|
{
|
|
content = new RootSequenceElement( this );
|
|
index = 0;
|
|
}
|
|
|
|
RootElement::~RootElement()
|
|
{
|
|
delete index;
|
|
delete content;
|
|
}
|
|
|
|
|
|
RootElement::RootElement( const RootElement& other )
|
|
: BasicElement( other )
|
|
{
|
|
content = new RootSequenceElement( *dynamic_cast<RootSequenceElement*>( other.content ) );
|
|
if ( other.index ) {
|
|
index = new SequenceElement( *( other.index ) );
|
|
index->setParent( this );
|
|
}
|
|
else {
|
|
index = 0;
|
|
}
|
|
}
|
|
|
|
|
|
bool RootElement::accept( ElementVisitor* visitor )
|
|
{
|
|
return visitor->visit( this );
|
|
}
|
|
|
|
|
|
void RootElement::entered( SequenceElement* child )
|
|
{
|
|
if ( child == content ) {
|
|
formula()->tell( i18n( "Main list of root" ) );
|
|
}
|
|
else {
|
|
formula()->tell( i18n( "Index" ) );
|
|
}
|
|
}
|
|
|
|
|
|
BasicElement* RootElement::goToPos( FormulaCursor* cursor, bool& handled,
|
|
const LuPixelPoint& point, const LuPixelPoint& parentOrigin)
|
|
{
|
|
BasicElement* e = BasicElement::goToPos(cursor, handled, point, parentOrigin);
|
|
if (e != 0) {
|
|
LuPixelPoint myPos(parentOrigin.x() + getX(),
|
|
parentOrigin.y() + getY());
|
|
|
|
e = content->goToPos(cursor, handled, point, myPos);
|
|
if (e != 0) {
|
|
return e;
|
|
}
|
|
if (hasIndex()) {
|
|
e = index->goToPos(cursor, handled, point, myPos);
|
|
if (e != 0) {
|
|
return e;
|
|
}
|
|
}
|
|
|
|
//int dx = point.x() - myPos.x();
|
|
luPixel dy = point.y() - myPos.y();
|
|
|
|
// the position after the index
|
|
if (hasIndex()) {
|
|
if (dy < index->getHeight()) {
|
|
index->moveLeft(cursor, this);
|
|
handled = true;
|
|
return index;
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Calculates our width and height and
|
|
* our children's parentPosition.
|
|
*/
|
|
void RootElement::calcSizes( const ContextStyle& context,
|
|
ContextStyle::TextStyle tstyle,
|
|
ContextStyle::IndexStyle istyle,
|
|
StyleAttributes& style )
|
|
{
|
|
content->calcSizes( context, tstyle,
|
|
context.convertIndexStyleLower(istyle), style );
|
|
|
|
luPixel indexWidth = 0;
|
|
luPixel indexHeight = 0;
|
|
if (hasIndex()) {
|
|
index->calcSizes( context,
|
|
context.convertTextStyleIndex(tstyle),
|
|
context.convertIndexStyleUpper(istyle),
|
|
style );
|
|
indexWidth = index->getWidth();
|
|
indexHeight = index->getHeight();
|
|
}
|
|
|
|
double factor = style.sizeFactor();
|
|
luPixel distX = context.ptToPixelX( context.getThinSpace( tstyle, factor ) );
|
|
luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );
|
|
luPixel unit = (content->getHeight() + distY)/ 3;
|
|
|
|
if (hasIndex()) {
|
|
if (indexWidth > unit) {
|
|
index->setX(0);
|
|
rootOffset.setX( indexWidth - unit );
|
|
}
|
|
else {
|
|
index->setX( ( unit - indexWidth )/2 );
|
|
rootOffset.setX(0);
|
|
}
|
|
if (indexHeight > unit) {
|
|
index->setY(0);
|
|
rootOffset.setY( indexHeight - unit );
|
|
}
|
|
else {
|
|
index->setY( unit - indexHeight );
|
|
rootOffset.setY(0);
|
|
}
|
|
}
|
|
else {
|
|
rootOffset.setX(0);
|
|
rootOffset.setY(0);
|
|
}
|
|
|
|
setWidth( content->getWidth() + unit+unit/3+ rootOffset.x() + distX/2 );
|
|
setHeight( content->getHeight() + distY*2 + rootOffset.y() );
|
|
|
|
content->setX( rootOffset.x() + unit+unit/3 );
|
|
content->setY( rootOffset.y() + distY );
|
|
setBaseline(content->getBaseline() + content->getY());
|
|
}
|
|
|
|
/**
|
|
* Draws the whole element including its children.
|
|
* The `parentOrigin' is the point this element's parent starts.
|
|
* We can use our parentPosition to get our own origin then.
|
|
*/
|
|
void RootElement::draw( TQPainter& painter, const LuPixelRect& r,
|
|
const ContextStyle& context,
|
|
ContextStyle::TextStyle tstyle,
|
|
ContextStyle::IndexStyle istyle,
|
|
StyleAttributes& style,
|
|
const LuPixelPoint& parentOrigin )
|
|
{
|
|
LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
|
|
//if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
|
|
// return;
|
|
|
|
content->draw( painter, r, context, tstyle,
|
|
context.convertIndexStyleLower(istyle), style, myPos);
|
|
if (hasIndex()) {
|
|
index->draw(painter, r, context,
|
|
context.convertTextStyleIndex(tstyle),
|
|
context.convertIndexStyleUpper(istyle), style, myPos);
|
|
}
|
|
|
|
luPixel x = myPos.x() + rootOffset.x();
|
|
luPixel y = myPos.y() + rootOffset.y();
|
|
//int distX = context.getDistanceX(tstyle);
|
|
double factor = style.sizeFactor();
|
|
luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );
|
|
luPixel unit = (content->getHeight() + distY)/ 3;
|
|
|
|
painter.setPen( TQPen( style.color(),
|
|
context.layoutUnitToPixelX( 2*context.getLineWidth( factor ) ) ) );
|
|
painter.drawLine( context.layoutUnitToPixelX( x+unit/3 ),
|
|
context.layoutUnitToPixelY( y+unit+distY/3 ),
|
|
context.layoutUnitToPixelX( x+unit/2+unit/3 ),
|
|
context.layoutUnitToPixelY( myPos.y()+getHeight() ) );
|
|
|
|
painter.setPen( TQPen( style.color(),
|
|
context.layoutUnitToPixelY( context.getLineWidth( factor ) ) ) );
|
|
|
|
painter.drawLine( context.layoutUnitToPixelX( x+unit+unit/3 ),
|
|
context.layoutUnitToPixelY( y+distY/3 ),
|
|
context.layoutUnitToPixelX( x+unit/2+unit/3 ),
|
|
context.layoutUnitToPixelY( myPos.y()+getHeight() ) );
|
|
painter.drawLine( context.layoutUnitToPixelX( x+unit+unit/3 ),
|
|
context.layoutUnitToPixelY( y+distY/3 ),
|
|
context.layoutUnitToPixelX( x+unit+unit/3+content->getWidth() ),
|
|
context.layoutUnitToPixelY( y+distY/3 ) );
|
|
painter.drawLine( context.layoutUnitToPixelX( x+unit/3 ),
|
|
context.layoutUnitToPixelY( y+unit+distY/2 ),
|
|
context.layoutUnitToPixelX( x ),
|
|
context.layoutUnitToPixelY( y+unit+unit/2 ) );
|
|
}
|
|
|
|
|
|
void RootElement::dispatchFontCommand( FontCommand* cmd )
|
|
{
|
|
content->dispatchFontCommand( cmd );
|
|
if (hasIndex()) {
|
|
index->dispatchFontCommand( cmd );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enters this element while moving to the left starting inside
|
|
* the element `from'. Searches for a cursor position inside
|
|
* this element or to the left of it.
|
|
*/
|
|
void RootElement::moveLeft(FormulaCursor* cursor, BasicElement* from)
|
|
{
|
|
if (cursor->isSelectionMode()) {
|
|
getParent()->moveLeft(cursor, this);
|
|
}
|
|
else {
|
|
bool linear = cursor->getLinearMovement();
|
|
if (from == getParent()) {
|
|
content->moveLeft(cursor, this);
|
|
}
|
|
else if (from == content) {
|
|
if (linear && hasIndex()) {
|
|
index->moveLeft(cursor, this);
|
|
}
|
|
else {
|
|
getParent()->moveLeft(cursor, this);
|
|
}
|
|
}
|
|
else {
|
|
getParent()->moveLeft(cursor, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enters this element while moving to the right starting inside
|
|
* the element `from'. Searches for a cursor position inside
|
|
* this element or to the right of it.
|
|
*/
|
|
void RootElement::moveRight(FormulaCursor* cursor, BasicElement* from)
|
|
{
|
|
if (cursor->isSelectionMode()) {
|
|
getParent()->moveRight(cursor, this);
|
|
}
|
|
else {
|
|
bool linear = cursor->getLinearMovement();
|
|
if (from == getParent()) {
|
|
if (linear && hasIndex()) {
|
|
index->moveRight(cursor, this);
|
|
}
|
|
else {
|
|
content->moveRight(cursor, this);
|
|
}
|
|
}
|
|
else if (from == index) {
|
|
content->moveRight(cursor, this);
|
|
}
|
|
else {
|
|
getParent()->moveRight(cursor, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enters this element while moving up starting inside
|
|
* the element `from'. Searches for a cursor position inside
|
|
* this element or above it.
|
|
*/
|
|
void RootElement::moveUp(FormulaCursor* cursor, BasicElement* from)
|
|
{
|
|
if (cursor->isSelectionMode()) {
|
|
getParent()->moveUp(cursor, this);
|
|
}
|
|
else {
|
|
if (from == getParent()) {
|
|
content->moveRight(cursor, this);
|
|
}
|
|
else if (from == content) {
|
|
if (hasIndex()) {
|
|
index->moveRight(cursor, this);
|
|
}
|
|
else {
|
|
getParent()->moveUp(cursor, this);
|
|
}
|
|
}
|
|
else {
|
|
getParent()->moveUp(cursor, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enters this element while moving down starting inside
|
|
* the element `from'. Searches for a cursor position inside
|
|
* this element or below it.
|
|
*/
|
|
void RootElement::moveDown(FormulaCursor* cursor, BasicElement* from)
|
|
{
|
|
if (cursor->isSelectionMode()) {
|
|
getParent()->moveDown(cursor, this);
|
|
}
|
|
else {
|
|
if (from == getParent()) {
|
|
if (hasIndex()) {
|
|
index->moveRight(cursor, this);
|
|
}
|
|
else {
|
|
content->moveRight(cursor, this);
|
|
}
|
|
}
|
|
else if (from == index) {
|
|
content->moveRight(cursor, this);
|
|
}
|
|
else {
|
|
getParent()->moveDown(cursor, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reinserts the index if it has been removed.
|
|
*/
|
|
void RootElement::insert(FormulaCursor* cursor,
|
|
TQPtrList<BasicElement>& newChildren,
|
|
Direction direction)
|
|
{
|
|
if (cursor->getPos() == upperLeftPos) {
|
|
index = static_cast<SequenceElement*>(newChildren.take(0));
|
|
index->setParent(this);
|
|
|
|
if (direction == beforeCursor) {
|
|
index->moveLeft(cursor, this);
|
|
}
|
|
else {
|
|
index->moveRight(cursor, this);
|
|
}
|
|
cursor->setSelection(false);
|
|
formula()->changed();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes all selected children and returns them. Places the
|
|
* cursor to where the children have been.
|
|
*
|
|
* We remove ourselve if we are requested to remove our content.
|
|
*/
|
|
void RootElement::remove(FormulaCursor* cursor,
|
|
TQPtrList<BasicElement>& removedChildren,
|
|
Direction direction)
|
|
{
|
|
switch (cursor->getPos()) {
|
|
case contentPos:
|
|
getParent()->selectChild(cursor, this);
|
|
getParent()->remove(cursor, removedChildren, direction);
|
|
break;
|
|
case upperLeftPos:
|
|
removedChildren.append(index);
|
|
formula()->elementRemoval(index);
|
|
index = 0;
|
|
cursor->setTo(this, upperLeftPos);
|
|
formula()->changed();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Moves the cursor to a normal place where new elements
|
|
* might be inserted.
|
|
*/
|
|
void RootElement::normalize(FormulaCursor* cursor, Direction direction)
|
|
{
|
|
if (direction == beforeCursor) {
|
|
content->moveLeft(cursor, this);
|
|
}
|
|
else {
|
|
content->moveRight(cursor, this);
|
|
}
|
|
}
|
|
|
|
|
|
// main child
|
|
//
|
|
// If an element has children one has to become the main one.
|
|
|
|
SequenceElement* RootElement::getMainChild()
|
|
{
|
|
return content;
|
|
}
|
|
|
|
// void RootElement::setMainChild(SequenceElement* child)
|
|
// {
|
|
// formula()->elementRemoval(content);
|
|
// content = child;
|
|
// content->setParent(this);
|
|
// formula()->changed();
|
|
// }
|
|
|
|
|
|
/**
|
|
* Sets the cursor to select the child. The mark is placed before,
|
|
* the position behind it.
|
|
*/
|
|
void RootElement::selectChild(FormulaCursor* cursor, BasicElement* child)
|
|
{
|
|
if (child == content) {
|
|
cursor->setTo(this, contentPos);
|
|
}
|
|
else if (child == index) {
|
|
cursor->setTo(this, upperLeftPos);
|
|
}
|
|
}
|
|
|
|
|
|
void RootElement::moveToIndex(FormulaCursor* cursor, Direction direction)
|
|
{
|
|
if (hasIndex()) {
|
|
if (direction == beforeCursor) {
|
|
index->moveLeft(cursor, this);
|
|
}
|
|
else {
|
|
index->moveRight(cursor, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RootElement::setToIndex(FormulaCursor* cursor)
|
|
{
|
|
cursor->setTo(this, upperLeftPos);
|
|
}
|
|
|
|
|
|
/**
|
|
* Appends our attributes to the dom element.
|
|
*/
|
|
void RootElement::writeDom(TQDomElement element)
|
|
{
|
|
BasicElement::writeDom(element);
|
|
|
|
TQDomDocument doc = element.ownerDocument();
|
|
|
|
TQDomElement con = doc.createElement("CONTENT");
|
|
con.appendChild(content->getElementDom(doc));
|
|
element.appendChild(con);
|
|
|
|
if(hasIndex()) {
|
|
TQDomElement ind = doc.createElement("ROOTINDEX");
|
|
ind.appendChild(index->getElementDom(doc));
|
|
element.appendChild(ind);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads our attributes from the element.
|
|
* Returns false if it failed.
|
|
*/
|
|
bool RootElement::readAttributesFromDom(TQDomElement element)
|
|
{
|
|
return BasicElement::readAttributesFromDom(element);
|
|
}
|
|
|
|
/**
|
|
* Reads our content from the node. Sets the node to the next node
|
|
* that needs to be read.
|
|
* Returns false if it failed.
|
|
*/
|
|
bool RootElement::readContentFromDom(TQDomNode& node)
|
|
{
|
|
if (!BasicElement::readContentFromDom(node)) {
|
|
return false;
|
|
}
|
|
|
|
if ( !buildChild( content, node, "CONTENT" ) ) {
|
|
kdWarning( DEBUGID ) << "Empty content in RootElement." << endl;
|
|
return false;
|
|
}
|
|
node = node.nextSibling();
|
|
|
|
if ( node.nodeName().upper() == "ROOTINDEX" ) {
|
|
if ( !buildChild( index=new SequenceElement( this ), node, "ROOTINDEX" ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
// backward compatibility
|
|
else if ( node.nodeName().upper() == "INDEX" ) {
|
|
if ( !buildChild( index=new SequenceElement( this ), node, "INDEX" ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
node = node.nextSibling();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Reads our attributes from the MathML element.
|
|
* Also checks whether it's a msqrt or mroot.
|
|
* Returns false if it failed.
|
|
*/
|
|
bool RootElement::readAttributesFromMathMLDom(const TQDomElement& element)
|
|
{
|
|
if ( element.tagName().lower() == "mroot" )
|
|
square = false;
|
|
else
|
|
square = true;
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Reads our content from the MathML node. Sets the node to the next node
|
|
* that needs to be read.
|
|
* Returns false if it failed.
|
|
*/
|
|
int RootElement::readContentFromMathMLDom(TQDomNode& node)
|
|
{
|
|
if ( BasicElement::readContentFromMathMLDom( node ) == -1 ) {
|
|
return -1;
|
|
}
|
|
|
|
if ( square ) {
|
|
// Any number of arguments are allowed
|
|
if ( content->readContentFromMathMLDom( node ) == -1 ) {
|
|
kdWarning( DEBUGID ) << "Empty content in RootElement." << endl;
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
// Exactly two arguments are required
|
|
int contentNumber = content->buildMathMLChild( node );
|
|
if ( contentNumber == -1 ) {
|
|
kdWarning( DEBUGID ) << "Empty content in RootElement." << endl;
|
|
return -1;
|
|
}
|
|
for (int i = 0; i < contentNumber; i++ ) {
|
|
if ( node.isNull() ) {
|
|
return -1;
|
|
}
|
|
node = node.nextSibling();
|
|
}
|
|
|
|
index = new SequenceElement( this );
|
|
if ( index->buildMathMLChild( node ) == -1 ) {
|
|
kdWarning( DEBUGID ) << "Empty index in RootElement." << endl;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
TQString RootElement::toLatex()
|
|
{
|
|
TQString root;
|
|
root="\\sqrt";
|
|
if(hasIndex()) {
|
|
root+="[";
|
|
root+=index->toLatex();
|
|
root+="]";
|
|
}
|
|
root+="{";
|
|
root+=content->toLatex();
|
|
root+="}";
|
|
|
|
return root;
|
|
}
|
|
|
|
TQString RootElement::formulaString()
|
|
{
|
|
if ( hasIndex() ) {
|
|
return "(" + content->formulaString() + ")**(1.0/(" + index->formulaString() + "))";
|
|
}
|
|
return "sqrt(" + content->formulaString() + ")";
|
|
}
|
|
|
|
void RootElement::writeMathMLContent( TQDomDocument& doc, TQDomElement& element, bool oasisFormat ) const
|
|
{
|
|
content->writeMathML( doc, element, oasisFormat );
|
|
if( hasIndex() )
|
|
{
|
|
index->writeMathML( doc, element, oasisFormat );
|
|
}
|
|
}
|
|
|
|
KFORMULA_NAMESPACE_END
|