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                  2006 Martin Pfeiffer <hubipete@gmx.net>
5                  2009 Jeremias Epperlein <jeeree@web.de>
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public
9    License as published by the Free Software Foundation; either
10    version 2 of the License, or (at your option) any later version.
11 
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16 
17    You should have received a copy of the GNU Library General Public License
18    along with this library; see the file COPYING.LIB.  If not, write to
19    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20    Boston, MA 02110-1301, USA.
21 */
22 
23 #include "FractionElement.h"
24 
25 #include "FormulaCursor.h"
26 #include "AttributeManager.h"
27 #include "FormulaDebug.h"
28 
29 #include <KoXmlWriter.h>
30 #include <KoXmlReader.h>
31 
32 #include <QPainter>
33 
FractionElement(BasicElement * parent)34 FractionElement::FractionElement( BasicElement* parent ) : FixedElement( parent )
35 {
36     m_numerator = new RowElement( this );
37     m_denominator = new RowElement( this );
38     m_lineThickness = 1.0;
39 }
40 
~FractionElement()41 FractionElement::~FractionElement()
42 {
43     delete m_numerator;
44     delete m_denominator;
45 }
46 
paint(QPainter & painter,AttributeManager * am)47 void FractionElement::paint( QPainter& painter, AttributeManager* am )
48 {
49     Q_UNUSED( am )
50     // return if there is nothing to paint
51     if( m_lineThickness == 0.0 )
52         return;
53 
54     // paint the fraction line with the specified line width
55     QPen pen;
56     pen.setWidthF( m_lineThickness );
57     painter.setPen( pen );
58     painter.drawLine( m_fractionLine );
59 }
60 
layout(const AttributeManager * am)61 void FractionElement::layout( const AttributeManager* am )
62 {
63     // get values of all attributes
64     QString value = am->findValue( "linethickness", this );
65     Length length;
66     if(value == "thick")
67         length.value = 2;
68     else if(value == "medium")
69         length.value = 1;
70     else if(value == "thin")
71         length.value = 0.5;
72     else
73         length = am->parseUnit( value, this );
74 
75     if(length.unit == Length::None)
76         m_lineThickness = am->lineThickness(this) * length.value;
77     else
78         m_lineThickness = am->lengthToPixels(length, this, "linethickness");
79 
80     // decide which layout is wanted
81     if( am->boolOf( "bevelled", this ) )
82     {
83         layoutBevelledFraction( am );
84         return;
85     }
86 
87     qreal distY = am->layoutSpacing( this );
88     Align numalign = am->alignOf( "numalign", this );
89     Align denomalign = am->alignOf( "denomalign", this );
90 
91     // align the numerator and the denominator
92     QPointF numeratorOrigin;
93     QPointF denominatorOrigin( 0.0, m_numerator->height() + m_lineThickness + 2*distY );
94     setWidth( qMax( m_numerator->width(), m_denominator->width() ) + m_lineThickness*2 );
95 
96     if( numalign == Right )
97         numeratorOrigin.setX( width() - m_numerator->width() - m_lineThickness );
98     else if( numalign == Center )
99 	numeratorOrigin.setX( ( width() - m_numerator->width() ) / 2 );
100 
101     if( denomalign == Right )
102         denominatorOrigin.setX( width() - m_denominator->width() - m_lineThickness );
103     else if( numalign == Center )
104 	denominatorOrigin.setX( ( width() - m_denominator->width() ) / 2 );
105 
106     m_numerator->setOrigin( numeratorOrigin );
107     m_denominator->setOrigin( denominatorOrigin );
108 
109     // construct the fraction's line
110     qreal fractionLineY =  m_numerator->height() + m_lineThickness/2 + distY;
111     m_fractionLine = QLineF( QPointF( m_lineThickness, fractionLineY ),
112                              QPointF( width()-m_lineThickness, fractionLineY ) );
113 
114     setHeight( m_numerator->height() + m_denominator->height() +
115                m_lineThickness + 2*distY );
116     setBaseLine( denominatorOrigin.y() );
117 }
118 
layoutBevelledFraction(const AttributeManager * am)119 void FractionElement::layoutBevelledFraction( const AttributeManager* am )
120 {
121     // the shown line should have a width that has 1/3 of the height
122     // the line is higher as the content by 2*thinmathspace = 2*borderY
123 
124     qreal borderY = am->layoutSpacing( this );
125     setHeight( m_numerator->height() + m_denominator->height() + 2*borderY );
126     setWidth( m_numerator->width() + m_denominator->width() + height()/3 );
127     setBaseLine( height()/2 );
128 
129     m_numerator->setOrigin( QPointF( 0.0, borderY ) );
130     m_denominator->setOrigin( QPointF( width()-m_denominator->width(),
131                                        borderY+m_numerator->height() ) );
132     m_fractionLine = QLineF( QPointF( m_numerator->width(), height() ),
133                              QPointF( width()-m_denominator->width(), 0.0 ) );
134 }
135 
childElements() const136 const QList<BasicElement*> FractionElement::childElements() const
137 {
138     QList<BasicElement*> list;
139     list << m_numerator<<m_denominator;
140     return list;
141 }
142 
endPosition() const143 int FractionElement::endPosition() const {
144     return 3;
145 }
146 
147 // QLineF FractionElement::cursorLine(int position) const {
148 //     QPointF top=absoluteBoundingRect().topLeft();
149 //     QPointF bottom;
150 //     switch (position) {
151 // 	case 0:
152 // 	    top+=m_numerator->origin();
153 // 	    break;
154 // 	case 1:
155 // 	    top+=m_numerator->origin()+QPointF(m_numerator->width(),0.0);
156 // 	    break;
157 // 	case 2:
158 // 	    top+=m_denominator->origin();
159 // 	    break;
160 // 	case 3:
161 // 	    top+=m_denominator->origin()+QPointF(m_denominator->width(),0.0);
162 // 	    break;
163 //     }
164 //     if (position<=1) {
165 // 	bottom=top+QPointF(0.0,m_numerator->height());
166 //     }
167 //     else {
168 // 	bottom=top+QPointF(0.0,m_denominator->height());
169 //     }
170 //     return QLineF(top, bottom);
171 // }
172 
173 
elementsBetween(int pos1,int pos2) const174 QList< BasicElement* > FractionElement::elementsBetween ( int pos1, int pos2 ) const
175 {
176     QList<BasicElement*> tmp;
177     if (pos1==0 && pos2 >0) {
178         tmp.append(m_numerator);
179     }
180     if (pos1<3 && pos2==3) {
181         tmp.append(m_denominator);
182     }
183     return tmp;
184 }
185 
186 
positionOfChild(BasicElement * child) const187 int FractionElement::positionOfChild(BasicElement* child) const {
188     if (m_numerator==child){
189 	return 0;
190     }
191     else if (m_denominator==child) {
192 	return 2;
193     }
194     return -1;
195 }
196 
moveCursor(FormulaCursor & newcursor,FormulaCursor & oldcursor)197 bool FractionElement::moveCursor(FormulaCursor& newcursor, FormulaCursor& oldcursor)  {
198     if (newcursor.isSelecting()) {
199         return false;
200     } else {
201         //TODO: How can I get the attribute of the Attributemanager here?
202         // The movement should be different in the bevelled case
203         //if (bevelled) {
204         // return moveHorSituation(newcursor,oldcursor,0,1 )
205         return moveVertSituation(newcursor,oldcursor,0,1);
206     }
207 }
208 
setCursorTo(FormulaCursor & cursor,QPointF point)209 bool FractionElement::setCursorTo( FormulaCursor& cursor, QPointF point )
210 {
211     //check if the point is above the fraction line, the origin is in the top left corner
212     bool inNumerator=point.y() < (m_numerator->boundingRect().bottom() +  m_denominator->boundingRect().top())/2 ;
213     if (cursor.isSelecting()) {
214         return false;
215     } else {
216         if (point.x() > width()) {
217             cursor.moveTo(this,inNumerator? 1 : 3);
218             return true;
219         }
220         if (point.x() < 0) {
221             cursor.moveTo(this,inNumerator? 0 : 2);
222             return true;
223         }
224         if ( inNumerator ) {
225             point-=m_numerator->origin();
226             //TODO: maybe place it directly in the fraction if this fails
227             return m_numerator->setCursorTo(cursor,point);
228         } else {
229             point-=m_denominator->origin();
230             return m_denominator->setCursorTo(cursor,point);
231         }
232     }
233 }
234 
replaceChild(BasicElement * oldelement,BasicElement * newelement)235 bool FractionElement::replaceChild ( BasicElement* oldelement, BasicElement* newelement )
236 {
237     //TODO: investigate, if we really need this
238     if (newelement->elementType()==Row) {
239         RowElement* newrow=static_cast<RowElement*>(newelement);
240         if( oldelement == m_numerator ) {
241             m_numerator = newrow;
242             return true;
243         } else if( oldelement == m_denominator ) {
244             m_denominator = newrow;
245             return true;
246         }
247     }
248     return false;
249 }
250 
attributesDefaultValue(const QString & attribute) const251 QString FractionElement::attributesDefaultValue( const QString& attribute ) const
252 {
253     if( attribute == "linethickness" )
254         return "1";
255     else if( attribute == "numalign" || attribute == "denomalign" )
256         return "center";
257     else if( attribute == "bevelled" )
258         return "false";
259     else
260         return QString();
261 }
262 
readMathMLContent(const KoXmlElement & parent)263 bool FractionElement::readMathMLContent( const KoXmlElement& parent )
264 {
265     KoXmlElement tmp;
266     int counter=0;
267     forEachElement( tmp, parent ) {
268         if (counter==0) {
269             loadElement(tmp,&m_numerator);
270         } else if (counter==1) {
271             loadElement(tmp,&m_denominator);
272         } else {
273             debugFormula << "Too many arguments to mfrac";
274         }
275         counter++;
276     }
277     if (counter<2) {
278         debugFormula << "Not enough arguments to mfrac";
279     }
280     return true;
281 }
282 
writeMathMLContent(KoXmlWriter * writer,const QString & ns) const283 void FractionElement::writeMathMLContent( KoXmlWriter* writer, const QString& ns ) const
284 {
285     m_numerator->writeMathML( writer, ns );
286     m_denominator->writeMathML( writer, ns );
287 }
288 
elementType() const289 ElementType FractionElement::elementType() const
290 {
291     return Fraction;
292 }
293