lib

fractionelement.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
3 Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include <tqpainter.h>
22
23#include <kdebug.h>
24#include <tdelocale.h>
25
26#include "elementvisitor.h"
27#include "formulaelement.h"
28#include "formulacursor.h"
29#include "fractionelement.h"
30#include "sequenceelement.h"
31
32KFORMULA_NAMESPACE_BEGIN
33using namespace std;
34
35FractionElement::FractionElement(BasicElement* parent) : BasicElement(parent),
36 m_lineThicknessType( NoSize ),
37 m_numAlign( NoHorizontalAlign ),
38 m_denomAlign( NoHorizontalAlign ),
39 m_customBevelled( false )
40{
41 numerator = new SequenceElement(this);
42 denominator = new SequenceElement(this);
43}
44
45FractionElement::~FractionElement()
46{
47 delete denominator;
48 delete numerator;
49}
50
51FractionElement::FractionElement( const FractionElement& other )
52 : BasicElement( other ),
53 m_lineThicknessType( other.m_lineThicknessType ),
54 m_lineThickness( other.m_lineThickness ),
55 m_numAlign( other.m_numAlign ),
56 m_denomAlign( other.m_denomAlign ),
57 m_customBevelled( other.m_customBevelled ),
58 m_bevelled( other.m_bevelled )
59{
60 numerator = new SequenceElement( *( other.numerator ) );
61 denominator = new SequenceElement( *( other.denominator ) );
62 numerator->setParent( this );
63 denominator->setParent( this );
64}
65
66
68{
69 return visitor->visit( this );
70}
71
72void FractionElement::entered( SequenceElement* child )
73{
74 if ( child == numerator ) {
75 formula()->tell( i18n( "Numerator" ) );
76 }
77 else {
78 formula()->tell( i18n( "Denominator" ) );
79 }
80}
81
82
83BasicElement* FractionElement::goToPos( FormulaCursor* cursor, bool& handled,
84 const LuPixelPoint& point, const LuPixelPoint& parentOrigin )
85{
86 BasicElement* e = BasicElement::goToPos(cursor, handled, point, parentOrigin);
87 if (e != 0) {
88 LuPixelPoint myPos(parentOrigin.x() + getX(),
89 parentOrigin.y() + getY());
90 e = numerator->goToPos(cursor, handled, point, myPos);
91 if (e != 0) {
92 return e;
93 }
94 e = denominator->goToPos(cursor, handled, point, myPos);
95 if (e != 0) {
96 return e;
97 }
98
99 luPixel dx = point.x() - myPos.x();
100 luPixel dy = point.y() - myPos.y();
101
102 // the positions after the numerator / denominator
103 if ((dx > numerator->getX()) &&
104 (dy < numerator->getHeight())) {
105 numerator->moveLeft(cursor, this);
106 handled = true;
107 return numerator;
108 }
109 else if ((dx > denominator->getX()) &&
110 (dy > denominator->getY())) {
111 denominator->moveLeft(cursor, this);
112 handled = true;
113 return denominator;
114 }
115
116 return this;
117 }
118 return 0;
119}
120
121
128 ContextStyle::IndexStyle istyle,
129 StyleAttributes& style )
130{
131 ContextStyle::TextStyle i_tstyle = context.convertTextStyleFraction( tstyle );
132 ContextStyle::IndexStyle u_istyle = context.convertIndexStyleUpper( istyle );
133 ContextStyle::IndexStyle l_istyle = context.convertIndexStyleLower( istyle );
134 double factor = style.sizeFactor();
135
136 numerator->calcSizes( context, i_tstyle, u_istyle, style );
137 denominator->calcSizes( context, i_tstyle, l_istyle, style );
138
139 luPixel distY = context.ptToPixelY( context.getThinSpace( tstyle, factor ) );
140
141 double linethickness = lineThickness( context, factor );
142
143 setWidth( TQMAX( numerator->getWidth(), denominator->getWidth() ) );
144 setHeight( numerator->getHeight() + denominator->getHeight() +
145 2*distY + linethickness );
146 setBaseline( tqRound( numerator->getHeight() + distY + .5*linethickness
147 + context.axisHeight( tstyle, factor ) ) );
148
149 numerator->setX( ( getWidth() - numerator->getWidth() ) / 2 );
150 denominator->setX( ( getWidth() - denominator->getWidth() ) / 2 );
151
152 numerator->setY( 0 );
153 denominator->setY( getHeight() - denominator->getHeight() );
154}
155
156
162void FractionElement::draw( TQPainter& painter, const LuPixelRect& r,
163 const ContextStyle& context,
165 ContextStyle::IndexStyle istyle,
166 StyleAttributes& style,
167 const LuPixelPoint& parentOrigin )
168{
169 LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
170 //if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
171 // return;
172
173 numerator->draw( painter, r, context,
174 context.convertTextStyleFraction( tstyle ),
175 context.convertIndexStyleUpper( istyle ), style, myPos);
176 if (denominator) { // Can be temporarily 0 see FractionElement::remove
177 denominator->draw( painter, r, context,
178 context.convertTextStyleFraction( tstyle ),
179 context.convertIndexStyleLower( istyle ), style,
180 myPos);
181 }
182
183 if ( withLine() ) {
184 // TODO: thickness
185 double factor = style.sizeFactor();
186 double linethickness = lineThickness( context, factor );
187 painter.setPen( TQPen( style.color(),
188 context.layoutUnitToPixelY( linethickness ) ) );
189 painter.drawLine( context.layoutUnitToPixelX( myPos.x() ),
190 context.layoutUnitToPixelY( myPos.y() + axis( context, tstyle, factor ) ),
191 context.layoutUnitToPixelX( myPos.x() + getWidth() ),
192 context.layoutUnitToPixelY( myPos.y() + axis( context, tstyle, factor ) ) );
193 }
194}
195
196
198{
199 numerator->dispatchFontCommand( cmd );
200 denominator->dispatchFontCommand( cmd );
201}
202
208void FractionElement::moveLeft(FormulaCursor* cursor, BasicElement* from)
209{
210 if (cursor->isSelectionMode()) {
211 getParent()->moveLeft(cursor, this);
212 }
213 else {
214 bool linear = cursor->getLinearMovement();
215 if (from == getParent()) {
216 if (linear) {
217 denominator->moveLeft(cursor, this);
218 }
219 else {
220 numerator->moveLeft(cursor, this);
221 }
222 }
223 else if (from == denominator) {
224 numerator->moveLeft(cursor, this);
225 }
226 else {
227 getParent()->moveLeft(cursor, this);
228 }
229 }
230}
231
232
238void FractionElement::moveRight(FormulaCursor* cursor, BasicElement* from)
239{
240 if (cursor->isSelectionMode()) {
241 getParent()->moveRight(cursor, this);
242 }
243 else {
244 bool linear = cursor->getLinearMovement();
245 if (from == getParent()) {
246 numerator->moveRight(cursor, this);
247 }
248 else if (from == numerator) {
249 if (linear) {
250 denominator->moveRight(cursor, this);
251 }
252 else {
253 getParent()->moveRight(cursor, this);
254 }
255 }
256 else {
257 getParent()->moveRight(cursor, this);
258 }
259 }
260}
261
262
268void FractionElement::moveUp(FormulaCursor* cursor, BasicElement* from)
269{
270 if (cursor->isSelectionMode()) {
271 getParent()->moveUp(cursor, this);
272 }
273 else {
274 if (from == getParent()) {
275 denominator->moveRight(cursor, this);
276 }
277 else if (from == denominator) {
278 numerator->moveRight(cursor, this);
279 }
280 else {
281 getParent()->moveUp(cursor, this);
282 }
283 }
284}
285
286
292void FractionElement::moveDown(FormulaCursor* cursor, BasicElement* from)
293{
294 if (cursor->isSelectionMode()) {
295 getParent()->moveDown(cursor, this);
296 }
297 else {
298 if (from == getParent()) {
299 numerator->moveRight(cursor, this);
300 }
301 else if (from == numerator) {
302 denominator->moveRight(cursor, this);
303 }
304 else {
305 getParent()->moveDown(cursor, this);
306 }
307 }
308}
309
310
315 TQPtrList<BasicElement>& newChildren,
316 Direction direction)
317{
318 if (cursor->getPos() == denominatorPos) {
319 denominator = static_cast<SequenceElement*>(newChildren.take(0));
320 denominator->setParent(this);
321
322 if (direction == beforeCursor) {
323 denominator->moveLeft(cursor, this);
324 }
325 else {
326 denominator->moveRight(cursor, this);
327 }
328 cursor->setSelection(false);
329 formula()->changed();
330 }
331}
332
333
344 TQPtrList<BasicElement>& removedChildren,
345 Direction direction)
346{
347 switch (cursor->getPos()) {
348 case numeratorPos:
349 getParent()->selectChild(cursor, this);
350 getParent()->remove(cursor, removedChildren, direction);
351 break;
352 case denominatorPos:
353 removedChildren.append(denominator);
354 formula()->elementRemoval(denominator);
355 denominator = 0;
356 cursor->setTo(this, denominatorPos);
357 formula()->changed();
358 break;
359 }
360}
361
362
369{
370 return denominator == 0;
371}
372
373
374// main child
375//
376// If an element has children one has to become the main one.
377
379{
380 return numerator;
381}
382
383// void FractionElement::setMainChild(SequenceElement* child)
384// {
385// formula()->elementRemoval(numerator);
386// numerator = child;
387// numerator->setParent(this);
388// formula()->changed();
389// }
390
391
396void FractionElement::selectChild(FormulaCursor* cursor, BasicElement* child)
397{
398 if (child == numerator) {
399 cursor->setTo(this, numeratorPos);
400 }
401 else if (child == denominator) {
402 cursor->setTo(this, denominatorPos);
403 }
404}
405
406
410void FractionElement::writeDom(TQDomElement element)
411{
412 BasicElement::writeDom(element);
413
414 TQDomDocument doc = element.ownerDocument();
415 if (!withLine()) element.setAttribute("NOLINE", 1);
416
417 TQDomElement num = doc.createElement("NUMERATOR");
418 num.appendChild(numerator->getElementDom(doc));
419 element.appendChild(num);
420
421 TQDomElement den = doc.createElement("DENOMINATOR");
422 den.appendChild(denominator->getElementDom(doc));
423 element.appendChild(den);
424}
425
431{
433 return false;
434 }
435 TQString lineStr = element.attribute("NOLINE");
436 if(!lineStr.isNull()) {
437 m_lineThicknessType = RelativeSize;
438 m_lineThickness = lineStr.toInt();
439 }
440 return true;
441}
442
447bool FractionElement::readAttributesFromMathMLDom(const TQDomElement& element)
448{
450 return false;
451 }
452 TQString linethicknessStr = element.attribute( "linethickness" ).lower();
453 if ( ! linethicknessStr.isNull() ) {
454 if ( linethicknessStr == "thin" ) {
455 m_lineThicknessType = RelativeSize;
456 m_lineThickness = 0.5; // ### Arbitrary size
457 }
458 else if ( linethicknessStr == "medium" ) {
459 m_lineThicknessType = RelativeSize;
460 m_lineThickness = 1.0;
461 }
462 else if ( linethicknessStr == "thick" ) {
463 m_lineThicknessType = RelativeSize;
464 m_lineThickness = 2.0; // ### Arbitrary size
465 }
466 else {
467 m_lineThickness = getSize( linethicknessStr, &m_lineThicknessType );
468 }
469 }
470 TQString numalignStr = element.attribute( "numalign" ).lower();
471 if ( ! numalignStr.isNull() ) {
472 if ( numalignStr == "left" ) {
473 m_numAlign = LeftHorizontalAlign;
474 }
475 else if ( numalignStr == "center" ) {
476 m_numAlign = CenterHorizontalAlign;
477 }
478 else if ( numalignStr == "right" ) {
479 m_numAlign = RightHorizontalAlign;
480 }
481 }
482 TQString denomalignStr = element.attribute( "denomalign" ).lower();
483 if ( ! denomalignStr.isNull() ) {
484 if ( denomalignStr == "left" ) {
485 m_denomAlign = LeftHorizontalAlign;
486 }
487 else if ( denomalignStr == "center" ) {
488 m_denomAlign = CenterHorizontalAlign;
489 }
490 else if ( denomalignStr == "right" ) {
491 m_denomAlign = RightHorizontalAlign;
492 }
493 }
494 TQString bevelledStr = element.attribute( "bevelled" ).lower();
495 if ( ! bevelledStr.isNull() ) {
496 m_customBevelled = true;
497 if ( bevelledStr == "true" ) {
498 m_bevelled = true;
499 }
500 else {
501 m_bevelled = false;
502 }
503 }
504
505 return true;
506}
507
514{
516 return false;
517 }
518
519 if ( !buildChild( numerator, node, "NUMERATOR" ) ) {
520 kdWarning( DEBUGID ) << "Empty numerator in FractionElement." << endl;
521 return false;
522 }
523 node = node.nextSibling();
524
525 if ( !buildChild( denominator, node, "DENOMINATOR" ) ) {
526 kdWarning( DEBUGID ) << "Empty denominator in FractionElement." << endl;
527 return false;
528 }
529 node = node.nextSibling();
530
531 return true;
532}
533
540{
541 if ( BasicElement::readContentFromMathMLDom( node ) == -1 ) {
542 return -1;
543 }
544
545 int numeratorNumber = numerator->buildMathMLChild( node );
546 if ( numeratorNumber == -1 ) {
547 kdWarning( DEBUGID ) << "Empty numerator in FractionElement." << endl;
548 return -1;
549 }
550 for (int i = 0; i < numeratorNumber; i++ ) {
551 if ( node.isNull() ) {
552 return -1;
553 }
554 node = node.nextSibling();
555 }
556
557 if ( denominator->buildMathMLChild( node ) == -1 ) {
558 kdWarning( DEBUGID ) << "Empty denominator in FractionElement." << endl;
559 return -1;
560 }
561
562 return 1;
563}
564
566{
567 if ( withLine() ) {
568 return "\\frac{" + numerator->toLatex() +"}{" + denominator->toLatex() + "}";
569 }
570 else {
571 return "{" + numerator->toLatex() + "\\atop " + denominator->toLatex() + "}";
572 }
573}
574
575TQString FractionElement::formulaString()
576{
577 return "(" + numerator->formulaString() + ")/(" + denominator->formulaString() + ")";
578}
579
580void FractionElement::writeMathMLAttributes( TQDomElement& element ) const
581{
582 switch ( m_lineThicknessType ) {
583 case AbsoluteSize:
584 element.setAttribute( "linethickness", TQString( "%1pt" ).arg( m_lineThickness ) );
585 break;
586 case RelativeSize:
587 element.setAttribute( "linethickness", TQString( "%1%" ).arg( m_lineThickness * 100.0 ) );
588 break;
589 case PixelSize:
590 element.setAttribute( "linethickness", TQString( "%1px" ).arg( m_lineThickness ) );
591 break;
592 default:
593 break;
594 }
595
596 switch ( m_numAlign ) {
597 case LeftHorizontalAlign:
598 element.setAttribute( "numalign", "left" );
599 break;
600 case CenterHorizontalAlign:
601 element.setAttribute( "numalign", "center" );
602 break;
603 case RightHorizontalAlign:
604 element.setAttribute( "numalign", "right" );
605 break;
606 default:
607 break;
608 }
609
610 switch ( m_denomAlign ) {
611 case LeftHorizontalAlign:
612 element.setAttribute( "denomalign", "left" );
613 break;
614 case CenterHorizontalAlign:
615 element.setAttribute( "denomalign", "center" );
616 break;
617 case RightHorizontalAlign:
618 element.setAttribute( "denomalign", "right" );
619 break;
620 default:
621 break;
622 }
623
624 if ( m_customBevelled ) {
625 element.setAttribute( "bevelled", m_bevelled ? "true" : "false" );
626 }
627}
628
629void FractionElement::writeMathMLContent( TQDomDocument& doc,
630 TQDomElement& element,
631 bool oasisFormat ) const
632{
633 numerator->writeMathML( doc, element, oasisFormat );
634 denominator->writeMathML( doc, element, oasisFormat );
635}
636
637double FractionElement::lineThickness( const ContextStyle& context, double factor )
638{
639 double linethickness = context.getLineWidth( factor );
640 switch ( m_lineThicknessType ) {
641 case AbsoluteSize:
642 linethickness = context.ptToLayoutUnitPixX( m_lineThickness );
643 break;
644 case RelativeSize:
645 linethickness *= m_lineThickness;
646 break;
647 case PixelSize:
648 linethickness = m_lineThickness;
649 break;
650 default:
651 break;
652 }
653 return linethickness;
654}
655
656
657KFORMULA_NAMESPACE_END
Basis of every formula element.
virtual FormulaElement * formula()
Provide fast access to the rootElement for each child.
bool buildChild(SequenceElement *child, TQDomNode node, TQString name)
Returns if the SequenceElement could be constructed from the nodes first child.
virtual bool readAttributesFromMathMLDom(const TQDomElement &element)
Reads our attributes from the MathML element.
virtual bool readContentFromDom(TQDomNode &node)
Reads our content from the node.
virtual int readContentFromMathMLDom(TQDomNode &node)
Reads our content from the MathML node.
virtual bool readAttributesFromDom(TQDomElement element)
Reads our attributes from the element.
virtual BasicElement * goToPos(FormulaCursor *, bool &handled, const LuPixelPoint &point, const LuPixelPoint &parentOrigin)
Sets the cursor and returns the element the point is in.
double getSize(const TQString &str, SizeType *st)
Utility function that sets the size type and returns the size value from a MathML attribute string wi...
virtual void writeDom(TQDomElement element)
Appends our attributes to the dom element.
Contains all the style information for the formela.
TextStyle
Textstyles like in TeX.
luPixel getLineWidth(double factor) const
All simple lines like the one that makes up a fraction.
Base for all font commands that act on the current selection.
The selection.
void setSelection(bool selection)
Sets the selection mode.
bool isSelectionMode() const
Returns wether we are in selection mode.
void setTo(BasicElement *element, uint cursor, int mark=-1)
Sets the cursor to a new position.
void elementRemoval(BasicElement *child)
Gets called just before the child is removed from the element tree.
void tell(const TQString &msg)
Tell the user something has happened.
virtual void changed()
Gets called whenever something changes and we need to recalc.
virtual void remove(FormulaCursor *, TQPtrList< BasicElement > &, Direction)
Removes all selected children and returns them.
virtual void calcSizes(const ContextStyle &cstyle, ContextStyle::TextStyle tstyle, ContextStyle::IndexStyle istyle, StyleAttributes &style)
Calculates our width and height and our children's parentPosition.
virtual TQString toLatex()
virtual void dispatchFontCommand(FontCommand *cmd)
Draws the whole element including its children.
virtual int readContentFromMathMLDom(TQDomNode &node)
Reads our content from the MathML node.
virtual bool readAttributesFromDom(TQDomElement element)
Reads our attributes from the element.
virtual bool accept(ElementVisitor *visitor)
Visit this element.
virtual void writeDom(TQDomElement element)
Appends our attributes to the dom element.
virtual void insert(FormulaCursor *, TQPtrList< BasicElement > &, Direction)
Enters this element while moving to the left starting inside the element `from'.
virtual bool isSenseless()
Returns wether the element has no more useful children (except its main child) and should therefore b...
virtual bool readContentFromDom(TQDomNode &node)
Reads our content from the node.
virtual BasicElement * goToPos(FormulaCursor *, bool &handled, const LuPixelPoint &point, const LuPixelPoint &parentOrigin)
Sets the cursor and returns the element the point is in.
virtual bool readAttributesFromMathMLDom(const TQDomElement &element)
Reads our attributes from the MathML element.
virtual void entered(SequenceElement *child)
The cursor has entered one of our child sequences.
virtual void selectChild(FormulaCursor *cursor, BasicElement *child)
Sets the cursor to select the child.
virtual SequenceElement * getMainChild()
Removes the child.
int ptToLayoutUnitPixX(double x_pt) const
The "document pt" -> "Layout Unit pixels" conversions, for convenience.
int layoutUnitToPixelX(int lupix) const
The "layout unit pixel" -> "[zoomed] view pixel" conversions.
The element that contains a number of children.
virtual void dispatchFontCommand(FontCommand *cmd)
Draws the whole element including its children.