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