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