summaryrefslogtreecommitdiffstats
path: root/lib/kformula/fractionelement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kformula/fractionelement.cpp')
-rw-r--r--lib/kformula/fractionelement.cpp657
1 files changed, 657 insertions, 0 deletions
diff --git a/lib/kformula/fractionelement.cpp b/lib/kformula/fractionelement.cpp
new file mode 100644
index 000000000..9f1125e12
--- /dev/null
+++ b/lib/kformula/fractionelement.cpp
@@ -0,0 +1,657 @@
+/* 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 <kdebug.h>
+#include <tdelocale.h>
+
+#include "elementvisitor.h"
+#include "formulaelement.h"
+#include "formulacursor.h"
+#include "fractionelement.h"
+#include "sequenceelement.h"
+
+KFORMULA_NAMESPACE_BEGIN
+using namespace std;
+
+FractionElement::FractionElement(BasicElement* parent) : BasicElement(parent),
+ m_lineThicknessType( NoSize ),
+ m_numAlign( NoHorizontalAlign ),
+ m_denomAlign( NoHorizontalAlign ),
+ m_customBevelled( false )
+{
+ numerator = new SequenceElement(this);
+ denominator = new SequenceElement(this);
+}
+
+FractionElement::~FractionElement()
+{
+ delete denominator;
+ delete numerator;
+}
+
+FractionElement::FractionElement( const FractionElement& other )
+ : BasicElement( other ),
+ m_lineThicknessType( other.m_lineThicknessType ),
+ m_lineThickness( other.m_lineThickness ),
+ m_numAlign( other.m_numAlign ),
+ m_denomAlign( other.m_denomAlign ),
+ m_customBevelled( other.m_customBevelled ),
+ m_bevelled( other.m_bevelled )
+{
+ numerator = new SequenceElement( *( other.numerator ) );
+ denominator = new SequenceElement( *( other.denominator ) );
+ numerator->setParent( this );
+ denominator->setParent( this );
+}
+
+
+bool FractionElement::accept( ElementVisitor* visitor )
+{
+ return visitor->visit( this );
+}
+
+void FractionElement::entered( SequenceElement* child )
+{
+ if ( child == numerator ) {
+ formula()->tell( i18n( "Numerator" ) );
+ }
+ else {
+ formula()->tell( i18n( "Denominator" ) );
+ }
+}
+
+
+BasicElement* FractionElement::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 = numerator->goToPos(cursor, handled, point, myPos);
+ if (e != 0) {
+ return e;
+ }
+ e = denominator->goToPos(cursor, handled, point, myPos);
+ if (e != 0) {
+ return e;
+ }
+
+ luPixel dx = point.x() - myPos.x();
+ luPixel dy = point.y() - myPos.y();
+
+ // the positions after the numerator / denominator
+ if ((dx > numerator->getX()) &&
+ (dy < numerator->getHeight())) {
+ numerator->moveLeft(cursor, this);
+ handled = true;
+ return numerator;
+ }
+ else if ((dx > denominator->getX()) &&
+ (dy > denominator->getY())) {
+ denominator->moveLeft(cursor, this);
+ handled = true;
+ return denominator;
+ }
+
+ return this;
+ }
+ return 0;
+}
+
+
+/**
+ * Calculates our width and height and
+ * our children's parentPosition.
+ */
+void FractionElement::calcSizes( const ContextStyle& context,
+ ContextStyle::TextStyle tstyle,
+ ContextStyle::IndexStyle istyle,
+ StyleAttributes& style )
+{
+ ContextStyle::TextStyle i_tstyle = context.convertTextStyleFraction( tstyle );
+ ContextStyle::IndexStyle u_istyle = context.convertIndexStyleUpper( istyle );
+ ContextStyle::IndexStyle l_istyle = context.convertIndexStyleLower( istyle );
+ double factor = style.sizeFactor();
+
+ numerator->calcSizes( context, i_tstyle, u_istyle, style );
+ denominator->calcSizes( context, i_tstyle, l_istyle, style );
+
+ luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );
+
+ double linethickness = lineThickness( context, factor );
+
+ setWidth( TQMAX( numerator->getWidth(), denominator->getWidth() ) );
+ setHeight( numerator->getHeight() + denominator->getHeight() +
+ 2*distY + linethickness );
+ setBaseline( tqRound( numerator->getHeight() + distY + .5*linethickness
+ + context.axisHeight( tstyle, factor ) ) );
+
+ numerator->setX( ( getWidth() - numerator->getWidth() ) / 2 );
+ denominator->setX( ( getWidth() - denominator->getWidth() ) / 2 );
+
+ numerator->setY( 0 );
+ denominator->setY( getHeight() - denominator->getHeight() );
+}
+
+
+/**
+ * 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 FractionElement::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;
+
+ numerator->draw( painter, r, context,
+ context.convertTextStyleFraction( tstyle ),
+ context.convertIndexStyleUpper( istyle ), style, myPos);
+ if (denominator) { // Can be temporarily 0 see FractionElement::remove
+ denominator->draw( painter, r, context,
+ context.convertTextStyleFraction( tstyle ),
+ context.convertIndexStyleLower( istyle ), style,
+ myPos);
+ }
+
+ if ( withLine() ) {
+ // TODO: thickness
+ double factor = style.sizeFactor();
+ double linethickness = lineThickness( context, factor );
+ painter.setPen( TQPen( style.color(),
+ context.layoutUnitToPixelY( linethickness ) ) );
+ painter.drawLine( context.layoutUnitToPixelX( myPos.x() ),
+ context.layoutUnitToPixelY( myPos.y() + axis( context, tstyle, factor ) ),
+ context.layoutUnitToPixelX( myPos.x() + getWidth() ),
+ context.layoutUnitToPixelY( myPos.y() + axis( context, tstyle, factor ) ) );
+ }
+}
+
+
+void FractionElement::dispatchFontCommand( FontCommand* cmd )
+{
+ numerator->dispatchFontCommand( cmd );
+ denominator->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 FractionElement::moveLeft(FormulaCursor* cursor, BasicElement* from)
+{
+ if (cursor->isSelectionMode()) {
+ getParent()->moveLeft(cursor, this);
+ }
+ else {
+ bool linear = cursor->getLinearMovement();
+ if (from == getParent()) {
+ if (linear) {
+ denominator->moveLeft(cursor, this);
+ }
+ else {
+ numerator->moveLeft(cursor, this);
+ }
+ }
+ else if (from == denominator) {
+ numerator->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 FractionElement::moveRight(FormulaCursor* cursor, BasicElement* from)
+{
+ if (cursor->isSelectionMode()) {
+ getParent()->moveRight(cursor, this);
+ }
+ else {
+ bool linear = cursor->getLinearMovement();
+ if (from == getParent()) {
+ numerator->moveRight(cursor, this);
+ }
+ else if (from == numerator) {
+ if (linear) {
+ denominator->moveRight(cursor, this);
+ }
+ else {
+ getParent()->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 FractionElement::moveUp(FormulaCursor* cursor, BasicElement* from)
+{
+ if (cursor->isSelectionMode()) {
+ getParent()->moveUp(cursor, this);
+ }
+ else {
+ if (from == getParent()) {
+ denominator->moveRight(cursor, this);
+ }
+ else if (from == denominator) {
+ numerator->moveRight(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 FractionElement::moveDown(FormulaCursor* cursor, BasicElement* from)
+{
+ if (cursor->isSelectionMode()) {
+ getParent()->moveDown(cursor, this);
+ }
+ else {
+ if (from == getParent()) {
+ numerator->moveRight(cursor, this);
+ }
+ else if (from == numerator) {
+ denominator->moveRight(cursor, this);
+ }
+ else {
+ getParent()->moveDown(cursor, this);
+ }
+ }
+}
+
+
+/**
+ * Reinserts the denominator if it has been removed.
+ */
+void FractionElement::insert(FormulaCursor* cursor,
+ TQPtrList<BasicElement>& newChildren,
+ Direction direction)
+{
+ if (cursor->getPos() == denominatorPos) {
+ denominator = static_cast<SequenceElement*>(newChildren.take(0));
+ denominator->setParent(this);
+
+ if (direction == beforeCursor) {
+ denominator->moveLeft(cursor, this);
+ }
+ else {
+ denominator->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 numerator.
+ *
+ * It is possible to remove the denominator. But after this we
+ * are senseless and the caller is required to replace us.
+ */
+void FractionElement::remove(FormulaCursor* cursor,
+ TQPtrList<BasicElement>& removedChildren,
+ Direction direction)
+{
+ switch (cursor->getPos()) {
+ case numeratorPos:
+ getParent()->selectChild(cursor, this);
+ getParent()->remove(cursor, removedChildren, direction);
+ break;
+ case denominatorPos:
+ removedChildren.append(denominator);
+ formula()->elementRemoval(denominator);
+ denominator = 0;
+ cursor->setTo(this, denominatorPos);
+ formula()->changed();
+ break;
+ }
+}
+
+
+/**
+ * Returns wether the element has no more useful
+ * children (except its main child) and should therefore
+ * be replaced by its main child's content.
+ */
+bool FractionElement::isSenseless()
+{
+ return denominator == 0;
+}
+
+
+// main child
+//
+// If an element has children one has to become the main one.
+
+SequenceElement* FractionElement::getMainChild()
+{
+ return numerator;
+}
+
+// void FractionElement::setMainChild(SequenceElement* child)
+// {
+// formula()->elementRemoval(numerator);
+// numerator = child;
+// numerator->setParent(this);
+// formula()->changed();
+// }
+
+
+/**
+ * Sets the cursor to select the child. The mark is placed before,
+ * the position behind it.
+ */
+void FractionElement::selectChild(FormulaCursor* cursor, BasicElement* child)
+{
+ if (child == numerator) {
+ cursor->setTo(this, numeratorPos);
+ }
+ else if (child == denominator) {
+ cursor->setTo(this, denominatorPos);
+ }
+}
+
+
+/**
+ * Appends our attributes to the dom element.
+ */
+void FractionElement::writeDom(TQDomElement element)
+{
+ BasicElement::writeDom(element);
+
+ TQDomDocument doc = element.ownerDocument();
+ if (!withLine()) element.setAttribute("NOLINE", 1);
+
+ TQDomElement num = doc.createElement("NUMERATOR");
+ num.appendChild(numerator->getElementDom(doc));
+ element.appendChild(num);
+
+ TQDomElement den = doc.createElement("DENOMINATOR");
+ den.appendChild(denominator->getElementDom(doc));
+ element.appendChild(den);
+}
+
+/**
+ * Reads our attributes from the element.
+ * Returns false if it failed.
+ */
+bool FractionElement::readAttributesFromDom(TQDomElement element)
+{
+ if (!BasicElement::readAttributesFromDom(element)) {
+ return false;
+ }
+ TQString lineStr = element.attribute("NOLINE");
+ if(!lineStr.isNull()) {
+ m_lineThicknessType = RelativeSize;
+ m_lineThickness = lineStr.toInt();
+ }
+ return true;
+}
+
+/**
+ * Reads our attributes from the MathML element.
+ * Returns false if it failed.
+ */
+bool FractionElement::readAttributesFromMathMLDom(const TQDomElement& element)
+{
+ if ( ! BasicElement::readAttributesFromMathMLDom( element ) ) {
+ return false;
+ }
+ TQString linethicknessStr = element.attribute( "linethickness" ).lower();
+ if ( ! linethicknessStr.isNull() ) {
+ if ( linethicknessStr == "thin" ) {
+ m_lineThicknessType = RelativeSize;
+ m_lineThickness = 0.5; // ### Arbitrary size
+ }
+ else if ( linethicknessStr == "medium" ) {
+ m_lineThicknessType = RelativeSize;
+ m_lineThickness = 1.0;
+ }
+ else if ( linethicknessStr == "thick" ) {
+ m_lineThicknessType = RelativeSize;
+ m_lineThickness = 2.0; // ### Arbitrary size
+ }
+ else {
+ m_lineThickness = getSize( linethicknessStr, &m_lineThicknessType );
+ }
+ }
+ TQString numalignStr = element.attribute( "numalign" ).lower();
+ if ( ! numalignStr.isNull() ) {
+ if ( numalignStr == "left" ) {
+ m_numAlign = LeftHorizontalAlign;
+ }
+ else if ( numalignStr == "center" ) {
+ m_numAlign = CenterHorizontalAlign;
+ }
+ else if ( numalignStr == "right" ) {
+ m_numAlign = RightHorizontalAlign;
+ }
+ }
+ TQString denomalignStr = element.attribute( "denomalign" ).lower();
+ if ( ! denomalignStr.isNull() ) {
+ if ( denomalignStr == "left" ) {
+ m_denomAlign = LeftHorizontalAlign;
+ }
+ else if ( denomalignStr == "center" ) {
+ m_denomAlign = CenterHorizontalAlign;
+ }
+ else if ( denomalignStr == "right" ) {
+ m_denomAlign = RightHorizontalAlign;
+ }
+ }
+ TQString bevelledStr = element.attribute( "bevelled" ).lower();
+ if ( ! bevelledStr.isNull() ) {
+ m_customBevelled = true;
+ if ( bevelledStr == "true" ) {
+ m_bevelled = true;
+ }
+ else {
+ m_bevelled = false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Reads our content from the node. Sets the node to the next node
+ * that needs to be read.
+ * Returns false if it failed.
+ */
+bool FractionElement::readContentFromDom(TQDomNode& node)
+{
+ if (!BasicElement::readContentFromDom(node)) {
+ return false;
+ }
+
+ if ( !buildChild( numerator, node, "NUMERATOR" ) ) {
+ kdWarning( DEBUGID ) << "Empty numerator in FractionElement." << endl;
+ return false;
+ }
+ node = node.nextSibling();
+
+ if ( !buildChild( denominator, node, "DENOMINATOR" ) ) {
+ kdWarning( DEBUGID ) << "Empty denominator in FractionElement." << endl;
+ return false;
+ }
+ node = node.nextSibling();
+
+ 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 FractionElement::readContentFromMathMLDom(TQDomNode& node)
+{
+ if ( BasicElement::readContentFromMathMLDom( node ) == -1 ) {
+ return -1;
+ }
+
+ int numeratorNumber = numerator->buildMathMLChild( node );
+ if ( numeratorNumber == -1 ) {
+ kdWarning( DEBUGID ) << "Empty numerator in FractionElement." << endl;
+ return -1;
+ }
+ for (int i = 0; i < numeratorNumber; i++ ) {
+ if ( node.isNull() ) {
+ return -1;
+ }
+ node = node.nextSibling();
+ }
+
+ if ( denominator->buildMathMLChild( node ) == -1 ) {
+ kdWarning( DEBUGID ) << "Empty denominator in FractionElement." << endl;
+ return -1;
+ }
+
+ return 1;
+}
+
+TQString FractionElement::toLatex()
+{
+ if ( withLine() ) {
+ return "\\frac{" + numerator->toLatex() +"}{" + denominator->toLatex() + "}";
+ }
+ else {
+ return "{" + numerator->toLatex() + "\\atop " + denominator->toLatex() + "}";
+ }
+}
+
+TQString FractionElement::formulaString()
+{
+ return "(" + numerator->formulaString() + ")/(" + denominator->formulaString() + ")";
+}
+
+void FractionElement::writeMathMLAttributes( TQDomElement& element ) const
+{
+ switch ( m_lineThicknessType ) {
+ case AbsoluteSize:
+ element.setAttribute( "linethickness", TQString( "%1pt" ).arg( m_lineThickness ) );
+ break;
+ case RelativeSize:
+ element.setAttribute( "linethickness", TQString( "%1%" ).arg( m_lineThickness * 100.0 ) );
+ break;
+ case PixelSize:
+ element.setAttribute( "linethickness", TQString( "%1px" ).arg( m_lineThickness ) );
+ break;
+ default:
+ break;
+ }
+
+ switch ( m_numAlign ) {
+ case LeftHorizontalAlign:
+ element.setAttribute( "numalign", "left" );
+ break;
+ case CenterHorizontalAlign:
+ element.setAttribute( "numalign", "center" );
+ break;
+ case RightHorizontalAlign:
+ element.setAttribute( "numalign", "right" );
+ break;
+ default:
+ break;
+ }
+
+ switch ( m_denomAlign ) {
+ case LeftHorizontalAlign:
+ element.setAttribute( "denomalign", "left" );
+ break;
+ case CenterHorizontalAlign:
+ element.setAttribute( "denomalign", "center" );
+ break;
+ case RightHorizontalAlign:
+ element.setAttribute( "denomalign", "right" );
+ break;
+ default:
+ break;
+ }
+
+ if ( m_customBevelled ) {
+ element.setAttribute( "bevelled", m_bevelled ? "true" : "false" );
+ }
+}
+
+void FractionElement::writeMathMLContent( TQDomDocument& doc,
+ TQDomElement& element,
+ bool oasisFormat ) const
+{
+ numerator->writeMathML( doc, element, oasisFormat );
+ denominator->writeMathML( doc, element, oasisFormat );
+}
+
+double FractionElement::lineThickness( const ContextStyle& context, double factor )
+{
+ double linethickness = context.getLineWidth( factor );
+ switch ( m_lineThicknessType ) {
+ case AbsoluteSize:
+ linethickness = context.ptToLayoutUnitPixX( m_lineThickness );
+ break;
+ case RelativeSize:
+ linethickness *= m_lineThickness;
+ break;
+ case PixelSize:
+ linethickness = m_lineThickness;
+ break;
+ default:
+ break;
+ }
+ return linethickness;
+}
+
+
+KFORMULA_NAMESPACE_END