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 "FormulaCursor.h"
24
25 #include "BasicElement.h"
26 #include "RowElement.h"
27 #include "FixedElement.h"
28 #include "NumberElement.h"
29 #include "ElementFactory.h"
30 #include "OperatorElement.h"
31 #include "IdentifierElement.h"
32 #include "FormulaCommand.h"
33 #include "FormulaDebug.h"
34
35 #include <KoOdfLoadingContext.h>
36
37 #include <QPainter>
38 #include <QPen>
39 #include <algorithm>
40
41
FormulaCursor(BasicElement * element,bool selecting,int position,int mark)42 FormulaCursor::FormulaCursor(BasicElement* element, bool selecting, int position, int mark) {
43 m_currentElement=element;
44 m_selecting=selecting;
45 m_position=position;
46 m_mark=mark;
47 }
48
FormulaCursor(BasicElement * element,int position)49 FormulaCursor::FormulaCursor ( BasicElement* element, int position )
50 {
51 m_currentElement=element;
52 m_position=position;
53 m_mark=0;
54 m_selecting=false;
55 }
56
57
FormulaCursor()58 FormulaCursor::FormulaCursor()
59 {
60 FormulaCursor(0,0);
61 }
62
FormulaCursor(const FormulaCursor & other)63 FormulaCursor::FormulaCursor (const FormulaCursor& other )
64 {
65 m_currentElement=other.currentElement();
66 m_position=other.position();
67 m_mark=other.mark();
68 m_selecting=other.isSelecting();
69 }
70
71
paint(QPainter & painter) const72 void FormulaCursor::paint( QPainter& painter ) const
73 {
74 debugFormula << "Drawing cursor with selecting: "<< isSelecting() << " from "
75 << mark()<<" to " << position() << " in "<<ElementFactory::elementName(m_currentElement->elementType());
76 if( !m_currentElement )
77 return;
78 painter.save();
79 QPointF origin=m_currentElement->absoluteBoundingRect().topLeft();
80 qreal baseline=m_currentElement->baseLine();
81 QPen pen;
82 pen.setWidthF( 0.5 );
83 pen.setColor(Qt::black);
84 painter.setPen( pen );
85 painter.drawLine(m_currentElement->cursorLine( m_position ));
86 pen.setWidth( 0.1);
87 pen.setColor(Qt::blue);
88 pen.setStyle(Qt::DashLine);
89 painter.setPen( pen );
90 painter.drawLine( origin+QPointF(0.0,baseline),origin+QPointF(m_currentElement->width(), baseline) );
91 pen.setStyle(Qt::DotLine);
92 //Only here for debug purpose for now
93 switch(m_currentElement->elementType()) {
94 case Number:
95 pen.setColor(Qt::red);
96 break;
97 case Identifier:
98 pen.setColor(Qt::darkRed);
99 break;
100 case Row:
101 pen.setColor(Qt::yellow);
102 break;
103 case Fraction:
104 pen.setColor(Qt::blue);
105 break;
106 case Table:
107 pen.setColor(Qt::darkGreen);
108 break;
109 case TableRow:
110 pen.setColor(Qt::green);
111 break;
112 default:
113 pen.setColor(Qt::darkGray);
114 break;
115 }
116 painter.setPen(pen);
117 painter.drawRect( m_currentElement->absoluteBoundingRect() );
118 //draw the selection rectangle
119 if ( m_selecting ) {
120 QBrush brush;
121 QColor color(Qt::blue);
122 color.setAlpha(128);
123 brush.setColor(color);
124 brush.setStyle(Qt::SolidPattern);
125 painter.setBrush(brush);
126 painter.setPen(Qt::NoPen);
127 int p1=position()<mark()? position() : mark();
128 int p2=position()<mark()? mark() : position() ;
129 painter.drawPath(m_currentElement->selectionRegion(p1,p2));
130 }
131 painter.restore();
132 }
133
selectElement(BasicElement * element)134 void FormulaCursor::selectElement(BasicElement* element)
135 {
136 m_selecting=true;
137 m_currentElement=element;
138 m_mark=0;
139 m_position=m_currentElement->endPosition();
140 }
141
move(CursorDirection direction)142 void FormulaCursor::move( CursorDirection direction )
143 {
144 FormulaCursor oldcursor(*this);
145 m_direction = direction;
146 if (performMovement(oldcursor)==false) {
147 (*this)=oldcursor;
148 }
149 m_direction=NoDirection;
150 }
151
moveCloseTo(BasicElement * element,FormulaCursor & cursor)152 bool FormulaCursor::moveCloseTo(BasicElement* element, FormulaCursor& cursor)
153 {
154 if (element->setCursorTo(*this,cursor.getCursorPosition()-element->absoluteBoundingRect().topLeft())) {
155 return true;
156 } else {
157 return false;
158 }
159 }
160
getCursorPosition()161 QPointF FormulaCursor::getCursorPosition()
162 {
163 return ( m_currentElement->cursorLine(m_position).p1()
164 + m_currentElement->cursorLine(m_position).p2())/2.;
165 }
166
moveTo(const FormulaCursor & pos)167 void FormulaCursor::moveTo ( const FormulaCursor& pos )
168 {
169 m_currentElement=pos.currentElement();
170 m_position=pos.position();
171 m_selecting=pos.isSelecting();
172 m_mark=pos.mark();
173 }
174
175
moveTo(BasicElement * element)176 void FormulaCursor::moveTo ( BasicElement* element )
177 {
178 moveTo(element,0);
179 if (direction()==MoveLeft) {
180 moveEnd();
181 }
182 }
183
184
moveTo(BasicElement * element,int position)185 void FormulaCursor::moveTo ( BasicElement* element, int position )
186 {
187 moveTo(FormulaCursor(element,position));
188 }
189
190
setCursorTo(const QPointF & point)191 void FormulaCursor::setCursorTo( const QPointF& point )
192 {
193 if (m_selecting) {
194 while (!m_currentElement->absoluteBoundingRect().contains(point)) {
195 if ( m_currentElement->parentElement() ) {
196 m_position=0;
197 if (point.x()<m_currentElement->cursorLine(m_mark).p1().x()) {
198 //the point is left of the old selection start, so we move the selection
199 //start after the old current element
200 m_mark=m_currentElement->parentElement()->positionOfChild(m_currentElement)+1;
201 } else {
202 m_mark=m_currentElement->parentElement()->positionOfChild(m_currentElement);
203 }
204 m_currentElement=m_currentElement->parentElement();
205 } else {
206 return;
207 }
208 }
209 while (!m_currentElement->setCursorTo(*this,point-m_currentElement->absoluteBoundingRect().topLeft())) {
210 if ( m_currentElement->parentElement() ) {
211 m_mark=m_currentElement->parentElement()->positionOfChild(m_currentElement);
212 m_position=0;
213 if (point.x()<m_currentElement->cursorLine(m_mark).p1().x()) {
214 //the point is left of the old selection start, so we move the selection
215 //start after the old current element
216 m_mark++;
217 }
218 m_currentElement=m_currentElement->parentElement();
219 } else {
220 return;
221 }
222 }
223 } else {
224 BasicElement* formulaElement = m_currentElement;
225 while( formulaElement->parentElement() != 0 ) {
226 formulaElement = formulaElement->parentElement();
227 }
228 formulaElement->setCursorTo(*this,point);
229 }
230 }
231
mark() const232 int FormulaCursor::mark() const
233 {
234 return m_mark;
235 }
236
moveHome()237 void FormulaCursor::moveHome()
238 {
239 m_position = 0;
240 }
241
moveEnd()242 void FormulaCursor::moveEnd()
243 {
244 m_position=m_currentElement->endPosition();
245 }
246
isHome() const247 bool FormulaCursor::isHome() const
248 {
249 return m_position == 0;
250 }
251
isEnd() const252 bool FormulaCursor::isEnd() const
253 {
254 return m_position == m_currentElement->endPosition();
255 }
256
insideToken() const257 bool FormulaCursor::insideToken() const
258 {
259 if( m_currentElement->elementType() == Number ||
260 m_currentElement->elementType() == Operator ||
261 m_currentElement->elementType() == Identifier ) {
262 return true;
263 }
264 return false;
265 }
266
insideInferredRow() const267 bool FormulaCursor::insideInferredRow() const
268 {
269 return m_currentElement->isInferredRow();
270 }
271
insideFixedElement() const272 bool FormulaCursor::insideFixedElement() const
273 {
274 if (m_currentElement->elementType() == Fraction ||
275 m_currentElement->elementType() == Root ||
276 m_currentElement->elementType() == SubScript ||
277 m_currentElement->elementType() == SupScript ||
278 m_currentElement->elementType() == SubScript ||
279 m_currentElement->elementType() == SubSupScript ) {
280 return true;
281 }
282 return false;
283 }
284
285
286
currentElement() const287 BasicElement* FormulaCursor::currentElement() const
288 {
289 return m_currentElement;
290 }
291
position() const292 int FormulaCursor::position() const
293 {
294 return m_position;
295 }
296
setCurrentElement(BasicElement * element)297 void FormulaCursor::setCurrentElement(BasicElement* element) {
298 m_currentElement=element;
299 }
300
setPosition(int position)301 void FormulaCursor::setPosition(int position) {
302 m_position=position;
303 }
304
direction() const305 CursorDirection FormulaCursor::direction() const
306 {
307 return m_direction;
308 }
309
isSelecting() const310 bool FormulaCursor::isSelecting() const
311 {
312 return m_selecting;
313 }
314
setSelecting(bool selecting)315 void FormulaCursor::setSelecting( bool selecting )
316 {
317 if (selecting) {
318 if (!m_selecting) {
319 //we start a new selection
320 m_selecting = selecting;
321 m_mark=m_position;
322 }
323 } else {
324 m_selecting = selecting;
325 m_mark=0;
326 }
327 }
328
setMark(int position)329 void FormulaCursor::setMark(int position) {
330 m_mark=position;
331 }
332
selection() const333 QPair< int,int > FormulaCursor::selection() const
334 {
335 if (m_mark<m_position) {
336 return QPair<int,int>(m_mark,m_position);
337 } else {
338 return QPair<int,int>(m_position,m_mark);
339 }
340 }
341
342
hasSelection() const343 bool FormulaCursor::hasSelection() const
344 {
345 return (m_selecting && m_mark!=m_position);
346 }
347
348
isAccepted() const349 bool FormulaCursor::isAccepted() const
350 {
351 if (mark()<0 || mark()>m_currentElement->endPosition() ||
352 position()<0 || position()>m_currentElement->endPosition()) {
353 return false;
354 }
355 return m_currentElement->acceptCursor(*this);
356 }
357
performMovement(FormulaCursor & oldcursor)358 bool FormulaCursor::performMovement ( FormulaCursor& oldcursor )
359 {
360 //handle selecting and not selecting case separately, which makes more clear
361 if (isSelecting()) {
362 while ( m_currentElement ) {
363 if ( m_currentElement->moveCursor( *this, oldcursor ) ) {
364 if (isAccepted()) {
365 return true;
366 }
367 } else {
368 if ( m_currentElement->parentElement() ) {
369 bool ltr=m_mark<=m_position;
370 //update the starting point of the selection
371 m_mark=m_currentElement->parentElement()->positionOfChild(m_currentElement);
372 //move the cursor to the parent and place it before the old element
373 m_position=m_currentElement->parentElement()->positionOfChild(m_currentElement);
374 m_currentElement=m_currentElement->parentElement();
375 if (ltr) {
376 m_position++; //place the cursor behind
377 } else {
378 m_mark++; //place the selection beginning behind
379 }
380 if (isAccepted()) {
381 return true;
382 }
383 } else {
384 //we arrived at the toplevel element
385 return false;
386 }
387 }
388 }
389 } else {
390 while ( m_currentElement ) {
391 if ( m_currentElement->moveCursor( *this, oldcursor ) ) {
392 if (isAccepted()) {
393 return true;
394 }
395 } else {
396 if ( m_currentElement->parentElement() ) {
397 //move the cursor to the parent and place it before the old element
398 m_position=m_currentElement->parentElement()->positionOfChild(m_currentElement);
399 m_currentElement=m_currentElement->parentElement();
400 if (m_direction==MoveRight || m_direction==MoveDown) {
401 m_position++; //place the cursor behind
402 }
403 if (m_direction==MoveRight || m_direction==MoveLeft) {
404 if (isAccepted()) {
405 return true;
406 }
407 }
408 } else {
409 //We arrived at the top level element
410 return false;
411 }
412 }
413 }
414 }
415 return false;
416 }
417
operator +=(int step)418 FormulaCursor& FormulaCursor::operator+= ( int step )
419 {
420 m_position+=step;
421 return *this;
422 }
423
offset()424 int FormulaCursor::offset ( )
425 {
426 if (m_direction==MoveDown || m_direction==MoveRight) {
427 return -1;
428 } else if (m_direction==MoveUp || m_direction==MoveLeft) {
429 return 1;
430 }
431 return 0;
432 }
433
434