lib

fractionelement.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
00003                   Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include <qpainter.h>
00022 
00023 #include <kdebug.h>
00024 #include <klocale.h>
00025 
00026 #include "elementvisitor.h"
00027 #include "formulaelement.h"
00028 #include "formulacursor.h"
00029 #include "fractionelement.h"
00030 #include "sequenceelement.h"
00031 
00032 KFORMULA_NAMESPACE_BEGIN
00033 using namespace std;
00034 
00035 FractionElement::FractionElement(BasicElement* parent) : BasicElement(parent),
00036                                                          m_lineThicknessType( NoSize ),
00037                                                          m_numAlign( NoHorizontalAlign ),
00038                                                          m_denomAlign( NoHorizontalAlign ),
00039                                                          m_customBevelled( false )
00040 {
00041     numerator = new SequenceElement(this);
00042     denominator = new SequenceElement(this);
00043 }
00044 
00045 FractionElement::~FractionElement()
00046 {
00047     delete denominator;
00048     delete numerator;
00049 }
00050 
00051 FractionElement::FractionElement( const FractionElement& other )
00052     : BasicElement( other ),
00053       m_lineThicknessType( other.m_lineThicknessType ),
00054       m_lineThickness( other.m_lineThickness ),
00055       m_numAlign( other.m_numAlign ),
00056       m_denomAlign( other.m_denomAlign ),
00057       m_customBevelled( other.m_customBevelled ),
00058       m_bevelled( other.m_bevelled )
00059 {
00060     numerator = new SequenceElement( *( other.numerator ) );
00061     denominator = new SequenceElement( *( other.denominator ) );
00062     numerator->setParent( this );
00063     denominator->setParent( this );
00064 }
00065 
00066 
00067 bool FractionElement::accept( ElementVisitor* visitor )
00068 {
00069     return visitor->visit( this );
00070 }
00071 
00072 void FractionElement::entered( SequenceElement* child )
00073 {
00074     if ( child == numerator ) {
00075         formula()->tell( i18n( "Numerator" ) );
00076     }
00077     else {
00078         formula()->tell( i18n( "Denominator" ) );
00079     }
00080 }
00081 
00082 
00083 BasicElement* FractionElement::goToPos( FormulaCursor* cursor, bool& handled,
00084                                         const LuPixelPoint& point, const LuPixelPoint& parentOrigin )
00085 {
00086     BasicElement* e = BasicElement::goToPos(cursor, handled, point, parentOrigin);
00087     if (e != 0) {
00088         LuPixelPoint myPos(parentOrigin.x() + getX(),
00089                            parentOrigin.y() + getY());
00090         e = numerator->goToPos(cursor, handled, point, myPos);
00091         if (e != 0) {
00092             return e;
00093         }
00094         e = denominator->goToPos(cursor, handled, point, myPos);
00095         if (e != 0) {
00096             return e;
00097         }
00098 
00099         luPixel dx = point.x() - myPos.x();
00100         luPixel dy = point.y() - myPos.y();
00101 
00102         // the positions after the numerator / denominator
00103         if ((dx > numerator->getX()) &&
00104             (dy < numerator->getHeight())) {
00105             numerator->moveLeft(cursor, this);
00106             handled = true;
00107             return numerator;
00108         }
00109         else if ((dx > denominator->getX()) &&
00110                  (dy > denominator->getY())) {
00111             denominator->moveLeft(cursor, this);
00112             handled = true;
00113             return denominator;
00114         }
00115 
00116         return this;
00117     }
00118     return 0;
00119 }
00120 
00121 
00126 void FractionElement::calcSizes( const ContextStyle& context, 
00127                                  ContextStyle::TextStyle tstyle,
00128                                  ContextStyle::IndexStyle istyle,
00129                                  StyleAttributes& style )
00130 {
00131     ContextStyle::TextStyle i_tstyle = context.convertTextStyleFraction( tstyle );
00132     ContextStyle::IndexStyle u_istyle = context.convertIndexStyleUpper( istyle );
00133     ContextStyle::IndexStyle l_istyle = context.convertIndexStyleLower( istyle );
00134     double factor = style.sizeFactor();
00135 
00136     numerator->calcSizes( context, i_tstyle, u_istyle, style );
00137     denominator->calcSizes( context, i_tstyle, l_istyle, style );
00138 
00139     luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );
00140 
00141     double linethickness = lineThickness( context, factor );
00142 
00143     setWidth( QMAX( numerator->getWidth(), denominator->getWidth() ) );
00144     setHeight( numerator->getHeight() + denominator->getHeight() +
00145                2*distY + linethickness );
00146     setBaseline( qRound( numerator->getHeight() + distY + .5*linethickness 
00147                          + context.axisHeight( tstyle, factor ) ) );
00148 
00149     numerator->setX( ( getWidth() - numerator->getWidth() ) / 2 );
00150     denominator->setX( ( getWidth() - denominator->getWidth() ) / 2 );
00151 
00152     numerator->setY( 0 );
00153     denominator->setY( getHeight() - denominator->getHeight() );
00154 }
00155 
00156 
00162 void FractionElement::draw( QPainter& painter, const LuPixelRect& r,
00163                             const ContextStyle& context,
00164                             ContextStyle::TextStyle tstyle,
00165                             ContextStyle::IndexStyle istyle,
00166                             StyleAttributes& style,
00167                             const LuPixelPoint& parentOrigin )
00168 {
00169     LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
00170     //if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
00171     //    return;
00172 
00173     numerator->draw( painter, r, context,
00174                      context.convertTextStyleFraction( tstyle ),
00175                      context.convertIndexStyleUpper( istyle ), style, myPos);
00176     if (denominator) { // Can be temporarily 0 see FractionElement::remove
00177         denominator->draw( painter, r, context,
00178                            context.convertTextStyleFraction( tstyle ),
00179                            context.convertIndexStyleLower( istyle ), style,
00180                            myPos);
00181     }
00182 
00183     if ( withLine() ) {
00184         // TODO: thickness
00185         double factor = style.sizeFactor();
00186         double linethickness = lineThickness( context, factor );
00187         painter.setPen( QPen( style.color(),
00188                               context.layoutUnitToPixelY( linethickness ) ) );
00189         painter.drawLine( context.layoutUnitToPixelX( myPos.x() ),
00190                           context.layoutUnitToPixelY( myPos.y() + axis( context, tstyle, factor ) ),
00191                           context.layoutUnitToPixelX( myPos.x() + getWidth() ),
00192                           context.layoutUnitToPixelY( myPos.y() + axis( context, tstyle, factor ) ) );
00193     }
00194 }
00195 
00196 
00197 void FractionElement::dispatchFontCommand( FontCommand* cmd )
00198 {
00199     numerator->dispatchFontCommand( cmd );
00200     denominator->dispatchFontCommand( cmd );
00201 }
00202 
00208 void FractionElement::moveLeft(FormulaCursor* cursor, BasicElement* from)
00209 {
00210     if (cursor->isSelectionMode()) {
00211         getParent()->moveLeft(cursor, this);
00212     }
00213     else {
00214         bool linear = cursor->getLinearMovement();
00215         if (from == getParent()) {
00216             if (linear) {
00217                 denominator->moveLeft(cursor, this);
00218             }
00219             else {
00220                 numerator->moveLeft(cursor, this);
00221             }
00222         }
00223         else if (from == denominator) {
00224             numerator->moveLeft(cursor, this);
00225         }
00226         else {
00227             getParent()->moveLeft(cursor, this);
00228         }
00229     }
00230 }
00231 
00232 
00238 void FractionElement::moveRight(FormulaCursor* cursor, BasicElement* from)
00239 {
00240     if (cursor->isSelectionMode()) {
00241         getParent()->moveRight(cursor, this);
00242     }
00243     else {
00244         bool linear = cursor->getLinearMovement();
00245         if (from == getParent()) {
00246             numerator->moveRight(cursor, this);
00247         }
00248         else if (from == numerator) {
00249             if (linear) {
00250                 denominator->moveRight(cursor, this);
00251             }
00252             else {
00253                 getParent()->moveRight(cursor, this);
00254             }
00255         }
00256         else {
00257             getParent()->moveRight(cursor, this);
00258         }
00259     }
00260 }
00261 
00262 
00268 void FractionElement::moveUp(FormulaCursor* cursor, BasicElement* from)
00269 {
00270     if (cursor->isSelectionMode()) {
00271         getParent()->moveUp(cursor, this);
00272     }
00273     else {
00274         if (from == getParent()) {
00275             denominator->moveRight(cursor, this);
00276         }
00277         else if (from == denominator) {
00278             numerator->moveRight(cursor, this);
00279         }
00280         else {
00281             getParent()->moveUp(cursor, this);
00282         }
00283     }
00284 }
00285 
00286 
00292 void FractionElement::moveDown(FormulaCursor* cursor, BasicElement* from)
00293 {
00294     if (cursor->isSelectionMode()) {
00295         getParent()->moveDown(cursor, this);
00296     }
00297     else {
00298         if (from == getParent()) {
00299             numerator->moveRight(cursor, this);
00300         }
00301         else if (from == numerator) {
00302             denominator->moveRight(cursor, this);
00303         }
00304         else {
00305             getParent()->moveDown(cursor, this);
00306         }
00307     }
00308 }
00309 
00310 
00314 void FractionElement::insert(FormulaCursor* cursor,
00315                              QPtrList<BasicElement>& newChildren,
00316                              Direction direction)
00317 {
00318     if (cursor->getPos() == denominatorPos) {
00319         denominator = static_cast<SequenceElement*>(newChildren.take(0));
00320         denominator->setParent(this);
00321 
00322         if (direction == beforeCursor) {
00323             denominator->moveLeft(cursor, this);
00324         }
00325         else {
00326             denominator->moveRight(cursor, this);
00327         }
00328         cursor->setSelection(false);
00329         formula()->changed();
00330     }
00331 }
00332 
00333 
00343 void FractionElement::remove(FormulaCursor* cursor,
00344                              QPtrList<BasicElement>& removedChildren,
00345                              Direction direction)
00346 {
00347     switch (cursor->getPos()) {
00348     case numeratorPos:
00349         getParent()->selectChild(cursor, this);
00350         getParent()->remove(cursor, removedChildren, direction);
00351         break;
00352     case denominatorPos:
00353         removedChildren.append(denominator);
00354         formula()->elementRemoval(denominator);
00355         denominator = 0;
00356         cursor->setTo(this, denominatorPos);
00357         formula()->changed();
00358         break;
00359     }
00360 }
00361 
00362 
00368 bool FractionElement::isSenseless()
00369 {
00370     return denominator == 0;
00371 }
00372 
00373 
00374 // main child
00375 //
00376 // If an element has children one has to become the main one.
00377 
00378 SequenceElement* FractionElement::getMainChild()
00379 {
00380     return numerator;
00381 }
00382 
00383 // void FractionElement::setMainChild(SequenceElement* child)
00384 // {
00385 //     formula()->elementRemoval(numerator);
00386 //     numerator = child;
00387 //     numerator->setParent(this);
00388 //     formula()->changed();
00389 // }
00390 
00391 
00396 void FractionElement::selectChild(FormulaCursor* cursor, BasicElement* child)
00397 {
00398     if (child == numerator) {
00399         cursor->setTo(this, numeratorPos);
00400     }
00401     else if (child == denominator) {
00402         cursor->setTo(this, denominatorPos);
00403     }
00404 }
00405 
00406 
00410 void FractionElement::writeDom(QDomElement element)
00411 {
00412     BasicElement::writeDom(element);
00413 
00414     QDomDocument doc = element.ownerDocument();
00415     if (!withLine()) element.setAttribute("NOLINE", 1);
00416 
00417     QDomElement num = doc.createElement("NUMERATOR");
00418     num.appendChild(numerator->getElementDom(doc));
00419     element.appendChild(num);
00420 
00421     QDomElement den = doc.createElement("DENOMINATOR");
00422     den.appendChild(denominator->getElementDom(doc));
00423     element.appendChild(den);
00424 }
00425 
00430 bool FractionElement::readAttributesFromDom(QDomElement element)
00431 {
00432     if (!BasicElement::readAttributesFromDom(element)) {
00433         return false;
00434     }
00435     QString lineStr = element.attribute("NOLINE");
00436     if(!lineStr.isNull()) {
00437         m_lineThicknessType = RelativeSize;
00438         m_lineThickness = lineStr.toInt();
00439     }
00440     return true;
00441 }
00442 
00447 bool FractionElement::readAttributesFromMathMLDom(const QDomElement& element)
00448 {
00449     if ( ! BasicElement::readAttributesFromMathMLDom( element ) ) {
00450         return false;
00451     }
00452     QString linethicknessStr = element.attribute( "linethickness" ).lower();
00453     if ( ! linethicknessStr.isNull() ) {
00454         if ( linethicknessStr == "thin" ) {
00455             m_lineThicknessType = RelativeSize;
00456             m_lineThickness = 0.5; // ### Arbitrary size
00457         }
00458         else if ( linethicknessStr == "medium" ) {
00459             m_lineThicknessType = RelativeSize;
00460             m_lineThickness = 1.0;
00461         }
00462         else if ( linethicknessStr == "thick" ) {
00463             m_lineThicknessType = RelativeSize;
00464             m_lineThickness = 2.0; // ### Arbitrary size
00465         }
00466         else {
00467             m_lineThickness = getSize( linethicknessStr, &m_lineThicknessType );
00468         }
00469     }
00470     QString numalignStr = element.attribute( "numalign" ).lower();
00471     if ( ! numalignStr.isNull() ) {
00472         if ( numalignStr == "left" ) {
00473             m_numAlign = LeftHorizontalAlign;
00474         }
00475         else if ( numalignStr == "center" ) {
00476             m_numAlign = CenterHorizontalAlign;
00477         }
00478         else if ( numalignStr == "right" ) {
00479             m_numAlign = RightHorizontalAlign;
00480         }
00481     }
00482     QString denomalignStr = element.attribute( "denomalign" ).lower();
00483     if ( ! denomalignStr.isNull() ) {
00484         if ( denomalignStr == "left" ) {
00485             m_denomAlign = LeftHorizontalAlign;
00486         }
00487         else if ( denomalignStr == "center" ) {
00488             m_denomAlign = CenterHorizontalAlign;
00489         }
00490         else if ( denomalignStr == "right" ) {
00491             m_denomAlign = RightHorizontalAlign;
00492         }
00493     }
00494     QString bevelledStr = element.attribute( "bevelled" ).lower();
00495     if ( ! bevelledStr.isNull() ) {
00496         m_customBevelled = true;
00497         if ( bevelledStr == "true" ) {
00498             m_bevelled = true;
00499         }
00500         else {
00501             m_bevelled = false;
00502         }
00503     }
00504 
00505     return true;
00506 }
00507 
00513 bool FractionElement::readContentFromDom(QDomNode& node)
00514 {
00515     if (!BasicElement::readContentFromDom(node)) {
00516         return false;
00517     }
00518 
00519     if ( !buildChild( numerator, node, "NUMERATOR" ) ) {
00520         kdWarning( DEBUGID ) << "Empty numerator in FractionElement." << endl;
00521         return false;
00522     }
00523     node = node.nextSibling();
00524 
00525     if ( !buildChild( denominator, node, "DENOMINATOR" ) ) {
00526         kdWarning( DEBUGID ) << "Empty denominator in FractionElement." << endl;
00527         return false;
00528     }
00529     node = node.nextSibling();
00530 
00531     return true;
00532 }
00533 
00539 int FractionElement::readContentFromMathMLDom(QDomNode& node)
00540 {
00541     if ( BasicElement::readContentFromMathMLDom( node ) == -1 ) {
00542         return -1;
00543     }
00544 
00545     int numeratorNumber = numerator->buildMathMLChild( node );
00546     if ( numeratorNumber == -1 ) {
00547         kdWarning( DEBUGID ) << "Empty numerator in FractionElement." << endl;
00548         return -1;
00549     }
00550     for (int i = 0; i < numeratorNumber; i++ ) {
00551         if ( node.isNull() ) {
00552             return -1;
00553         }
00554         node = node.nextSibling();
00555     }
00556 
00557     if ( denominator->buildMathMLChild( node ) == -1 ) {
00558         kdWarning( DEBUGID ) << "Empty denominator in FractionElement." << endl;
00559         return -1;
00560     }
00561 
00562     return 1;
00563 }
00564 
00565 QString FractionElement::toLatex()
00566 {
00567     if ( withLine() ) {
00568         return "\\frac{" + numerator->toLatex() +"}{" + denominator->toLatex() + "}";
00569     }
00570     else {
00571         return "{" + numerator->toLatex() + "\\atop " + denominator->toLatex() + "}";
00572     }
00573 }
00574 
00575 QString FractionElement::formulaString()
00576 {
00577     return "(" + numerator->formulaString() + ")/(" + denominator->formulaString() + ")";
00578 }
00579 
00580 void FractionElement::writeMathMLAttributes( QDomElement& element ) const
00581 {
00582     switch ( m_lineThicknessType ) {
00583     case AbsoluteSize:
00584         element.setAttribute( "linethickness", QString( "%1pt" ).arg( m_lineThickness ) );
00585         break;
00586     case RelativeSize:
00587         element.setAttribute( "linethickness", QString( "%1%" ).arg( m_lineThickness * 100.0 ) );
00588         break;
00589     case PixelSize:
00590         element.setAttribute( "linethickness", QString( "%1px" ).arg( m_lineThickness ) );
00591         break;
00592     default:
00593         break;
00594     }
00595 
00596     switch ( m_numAlign ) {
00597     case LeftHorizontalAlign:
00598         element.setAttribute( "numalign", "left" );
00599         break;
00600     case CenterHorizontalAlign:
00601         element.setAttribute( "numalign", "center" );
00602         break;
00603     case RightHorizontalAlign:
00604         element.setAttribute( "numalign", "right" );
00605         break;
00606     default:
00607         break;
00608     }
00609 
00610     switch ( m_denomAlign ) {
00611     case LeftHorizontalAlign:
00612         element.setAttribute( "denomalign", "left" );
00613         break;
00614     case CenterHorizontalAlign:
00615         element.setAttribute( "denomalign", "center" );
00616         break;
00617     case RightHorizontalAlign:
00618         element.setAttribute( "denomalign", "right" );
00619         break;
00620     default:
00621         break;
00622     }
00623 
00624     if ( m_customBevelled ) {
00625         element.setAttribute( "bevelled", m_bevelled ? "true" : "false" );
00626     }
00627 }
00628 
00629 void FractionElement::writeMathMLContent( QDomDocument& doc, 
00630                                           QDomElement& element,
00631                                           bool oasisFormat ) const
00632 {
00633     numerator->writeMathML( doc, element, oasisFormat );
00634     denominator->writeMathML( doc, element, oasisFormat );
00635 }
00636 
00637 double FractionElement::lineThickness( const ContextStyle& context, double factor )
00638 {
00639     double linethickness = context.getLineWidth( factor );
00640     switch ( m_lineThicknessType ) {
00641     case AbsoluteSize:
00642         linethickness = context.ptToLayoutUnitPixX( m_lineThickness );
00643         break;
00644     case RelativeSize:
00645         linethickness *= m_lineThickness;
00646         break;
00647     case PixelSize:
00648         linethickness = m_lineThickness;
00649         break;
00650     default:
00651         break;
00652     }
00653     return linethickness;
00654 }
00655         
00656 
00657 KFORMULA_NAMESPACE_END
KDE Home | KDE Accessibility Home | Description of Access Keys