File indexing completed on 2024-05-12 16:34:02

0001 /* This file is part of the KDE project
0002    Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
0003                       Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
0004                  2006 Martin Pfeiffer <hubipete@gmx.net>
0005                  2009 Jeremias Epperlein <jeeree@web.de>
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020    Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "FormulaCursor.h"
0024 
0025 #include "BasicElement.h"
0026 #include "RowElement.h"
0027 #include "FixedElement.h"
0028 #include "NumberElement.h"
0029 #include "ElementFactory.h"
0030 #include "OperatorElement.h"
0031 #include "IdentifierElement.h"
0032 #include "FormulaCommand.h"
0033 #include "FormulaDebug.h"
0034 
0035 #include <KoOdfLoadingContext.h>
0036 
0037 #include <QPainter>
0038 #include <QPen>
0039 #include <algorithm>
0040 
0041 
0042 FormulaCursor::FormulaCursor(BasicElement* element, bool selecting, int position, int mark) {
0043     m_currentElement=element;
0044     m_selecting=selecting;
0045     m_position=position;
0046     m_mark=mark;
0047 }
0048 
0049 FormulaCursor::FormulaCursor ( BasicElement* element, int position )
0050 {
0051     m_currentElement=element;
0052     m_position=position;
0053     m_mark=0;
0054     m_selecting=false;
0055 }
0056 
0057 
0058 FormulaCursor::FormulaCursor()
0059 {
0060     FormulaCursor(0,0);
0061 }
0062 
0063 FormulaCursor::FormulaCursor (const FormulaCursor& other )
0064 {
0065     m_currentElement=other.currentElement();
0066     m_position=other.position();
0067     m_mark=other.mark();
0068     m_selecting=other.isSelecting();
0069 }
0070 
0071 
0072 void FormulaCursor::paint( QPainter& painter ) const
0073 {
0074     debugFormula << "Drawing cursor with selecting: "<< isSelecting() << " from "
0075     << mark()<<" to " << position() << " in "<<ElementFactory::elementName(m_currentElement->elementType());
0076     if( !m_currentElement )
0077         return;
0078     painter.save();
0079     QPointF origin=m_currentElement->absoluteBoundingRect().topLeft();
0080     qreal baseline=m_currentElement->baseLine();
0081     QPen pen;
0082     pen.setWidthF( 0.5 );
0083     pen.setColor(Qt::black);
0084     painter.setPen( pen );
0085     painter.drawLine(m_currentElement->cursorLine( m_position ));
0086     pen.setWidth( 0.1);
0087     pen.setColor(Qt::blue);
0088     pen.setStyle(Qt::DashLine);
0089     painter.setPen( pen );
0090     painter.drawLine( origin+QPointF(0.0,baseline),origin+QPointF(m_currentElement->width(), baseline) );
0091     pen.setStyle(Qt::DotLine);
0092     //Only here for debug purpose for now
0093     switch(m_currentElement->elementType()) {
0094     case Number:
0095         pen.setColor(Qt::red);
0096         break;
0097     case Identifier:
0098         pen.setColor(Qt::darkRed);
0099         break;
0100     case Row:
0101         pen.setColor(Qt::yellow);
0102         break;
0103     case Fraction:
0104         pen.setColor(Qt::blue);
0105         break;
0106     case Table:
0107         pen.setColor(Qt::darkGreen);
0108         break;
0109     case TableRow:
0110         pen.setColor(Qt::green);
0111         break;
0112     default:
0113         pen.setColor(Qt::darkGray);
0114         break;
0115     }
0116     painter.setPen(pen);
0117     painter.drawRect( m_currentElement->absoluteBoundingRect() );
0118     //draw the selection rectangle
0119     if ( m_selecting ) {
0120         QBrush brush;
0121         QColor color(Qt::blue);
0122         color.setAlpha(128);
0123         brush.setColor(color);
0124         brush.setStyle(Qt::SolidPattern);
0125         painter.setBrush(brush);
0126         painter.setPen(Qt::NoPen);
0127         int p1=position()<mark()? position() : mark();
0128         int p2=position()<mark()? mark() : position() ;
0129         painter.drawPath(m_currentElement->selectionRegion(p1,p2));
0130     }
0131     painter.restore();
0132 }
0133 
0134 void FormulaCursor::selectElement(BasicElement* element)
0135 {
0136     m_selecting=true;
0137     m_currentElement=element;
0138     m_mark=0;
0139     m_position=m_currentElement->endPosition();
0140 }
0141 
0142 void FormulaCursor::move( CursorDirection direction )
0143 {
0144     FormulaCursor oldcursor(*this);
0145     m_direction = direction;
0146     if (performMovement(oldcursor)==false) {
0147         (*this)=oldcursor;
0148     }
0149     m_direction=NoDirection;
0150 }
0151 
0152 bool FormulaCursor::moveCloseTo(BasicElement* element, FormulaCursor& cursor)
0153 {
0154     if (element->setCursorTo(*this,cursor.getCursorPosition()-element->absoluteBoundingRect().topLeft())) {
0155         return true;
0156     } else {
0157         return false;
0158     }
0159 }
0160 
0161 QPointF FormulaCursor::getCursorPosition() 
0162 {
0163     return ( m_currentElement->cursorLine(m_position).p1()
0164            + m_currentElement->cursorLine(m_position).p2())/2.;
0165 }
0166 
0167 void FormulaCursor::moveTo ( const FormulaCursor& pos )
0168 {
0169     m_currentElement=pos.currentElement();
0170     m_position=pos.position();
0171     m_selecting=pos.isSelecting();
0172     m_mark=pos.mark();
0173 }
0174 
0175 
0176 void FormulaCursor::moveTo ( BasicElement* element )
0177 {
0178     moveTo(element,0);
0179     if (direction()==MoveLeft) {
0180         moveEnd();
0181     }
0182 }
0183 
0184 
0185 void FormulaCursor::moveTo ( BasicElement* element, int position )
0186 {
0187     moveTo(FormulaCursor(element,position));
0188 }
0189 
0190 
0191 void FormulaCursor::setCursorTo( const QPointF& point )
0192 {
0193     if (m_selecting) {
0194         while (!m_currentElement->absoluteBoundingRect().contains(point)) {
0195             if ( m_currentElement->parentElement() ) {
0196                 m_position=0;
0197                 if (point.x()<m_currentElement->cursorLine(m_mark).p1().x()) {
0198                     //the point is left of the old selection start, so we move the selection 
0199                     //start after the old current element
0200                     m_mark=m_currentElement->parentElement()->positionOfChild(m_currentElement)+1;
0201                 } else {
0202                     m_mark=m_currentElement->parentElement()->positionOfChild(m_currentElement);
0203                 }
0204                 m_currentElement=m_currentElement->parentElement();
0205             } else {
0206                 return;
0207             }
0208         }
0209         while (!m_currentElement->setCursorTo(*this,point-m_currentElement->absoluteBoundingRect().topLeft())) {
0210             if ( m_currentElement->parentElement() ) {
0211                 m_mark=m_currentElement->parentElement()->positionOfChild(m_currentElement);
0212                 m_position=0;
0213                 if (point.x()<m_currentElement->cursorLine(m_mark).p1().x()) {
0214                     //the point is left of the old selection start, so we move the selection 
0215                     //start after the old current element
0216                     m_mark++;
0217                 }
0218                 m_currentElement=m_currentElement->parentElement();
0219             } else {
0220                     return;
0221             }
0222         }
0223     } else {
0224         BasicElement* formulaElement = m_currentElement;
0225         while( formulaElement->parentElement() != 0 ) {
0226             formulaElement = formulaElement->parentElement();
0227         }
0228         formulaElement->setCursorTo(*this,point);
0229     }
0230 }
0231 
0232 int FormulaCursor::mark() const 
0233 {
0234     return m_mark;
0235 }
0236 
0237 void FormulaCursor::moveHome()
0238 {
0239     m_position = 0;
0240 }
0241 
0242 void FormulaCursor::moveEnd()
0243 {
0244     m_position=m_currentElement->endPosition();
0245 }
0246 
0247 bool FormulaCursor::isHome() const
0248 {
0249     return m_position == 0;
0250 }
0251 
0252 bool FormulaCursor::isEnd() const
0253 {
0254     return m_position == m_currentElement->endPosition();
0255 }
0256 
0257 bool FormulaCursor::insideToken() const
0258 {
0259     if( m_currentElement->elementType() == Number ||
0260         m_currentElement->elementType() == Operator ||
0261         m_currentElement->elementType() == Identifier ) {
0262         return true;
0263     }
0264     return false;
0265 }
0266 
0267 bool FormulaCursor::insideInferredRow() const
0268 {
0269     return m_currentElement->isInferredRow();
0270 }
0271 
0272 bool FormulaCursor::insideFixedElement() const
0273 {
0274     if (m_currentElement->elementType() == Fraction ||
0275         m_currentElement->elementType() == Root ||
0276         m_currentElement->elementType() == SubScript ||
0277         m_currentElement->elementType() == SupScript ||
0278         m_currentElement->elementType() == SubScript ||
0279         m_currentElement->elementType() == SubSupScript ) {
0280         return true;
0281     }
0282     return false;
0283 }
0284  
0285 
0286 
0287 BasicElement* FormulaCursor::currentElement() const
0288 {
0289     return m_currentElement;
0290 }
0291 
0292 int FormulaCursor::position() const
0293 {
0294     return m_position;
0295 }
0296 
0297 void FormulaCursor::setCurrentElement(BasicElement* element) {
0298     m_currentElement=element;
0299 }
0300 
0301 void FormulaCursor::setPosition(int position) {
0302     m_position=position;
0303 }
0304 
0305 CursorDirection FormulaCursor::direction() const
0306 {
0307     return m_direction;
0308 }
0309 
0310 bool FormulaCursor::isSelecting() const
0311 {
0312     return m_selecting;
0313 }
0314 
0315 void FormulaCursor::setSelecting( bool selecting )
0316 {
0317     if (selecting) {
0318         if (!m_selecting) {
0319             //we start a new selection
0320             m_selecting = selecting;
0321             m_mark=m_position;
0322         }
0323     } else {
0324         m_selecting = selecting;
0325         m_mark=0;
0326     }
0327 }
0328 
0329 void FormulaCursor::setMark(int position) {
0330     m_mark=position;
0331 }
0332 
0333 QPair< int,int > FormulaCursor::selection() const
0334 {
0335     if (m_mark<m_position) {
0336         return QPair<int,int>(m_mark,m_position);
0337     } else {
0338         return QPair<int,int>(m_position,m_mark);
0339     }
0340 }
0341 
0342 
0343 bool FormulaCursor::hasSelection() const
0344 {
0345     return (m_selecting && m_mark!=m_position);
0346 }
0347 
0348 
0349 bool FormulaCursor::isAccepted() const
0350 {
0351     if (mark()<0 || mark()>m_currentElement->endPosition() ||
0352         position()<0 || position()>m_currentElement->endPosition()) {
0353         return false;
0354     }
0355     return m_currentElement->acceptCursor(*this);
0356 }
0357 
0358 bool FormulaCursor::performMovement ( FormulaCursor& oldcursor )
0359 {
0360     //handle selecting and not selecting case separately, which makes more clear
0361     if (isSelecting()) {
0362         while ( m_currentElement ) {
0363             if ( m_currentElement->moveCursor( *this, oldcursor ) ) {
0364                 if (isAccepted()) {
0365                     return true;
0366                 }
0367             } else {
0368                 if ( m_currentElement->parentElement() ) {
0369                     bool ltr=m_mark<=m_position;
0370                     //update the starting point of the selection
0371                     m_mark=m_currentElement->parentElement()->positionOfChild(m_currentElement);
0372                     //move the cursor to the parent and place it before the old element
0373                     m_position=m_currentElement->parentElement()->positionOfChild(m_currentElement);
0374                     m_currentElement=m_currentElement->parentElement();
0375                     if (ltr) {
0376                         m_position++; //place the cursor behind
0377                     } else {
0378                         m_mark++; //place the selection beginning behind 
0379                     }
0380                     if (isAccepted()) {
0381                         return true;
0382                     }
0383                 } else {
0384                     //we arrived at the toplevel element
0385                     return false;
0386                 }
0387             }
0388         }
0389     } else {
0390         while ( m_currentElement ) {
0391             if ( m_currentElement->moveCursor( *this, oldcursor ) ) {
0392                 if (isAccepted()) {
0393                     return true;
0394                 }
0395             } else {
0396                 if ( m_currentElement->parentElement() ) {
0397                     //move the cursor to the parent and place it before the old element
0398                     m_position=m_currentElement->parentElement()->positionOfChild(m_currentElement);
0399                     m_currentElement=m_currentElement->parentElement();
0400                     if (m_direction==MoveRight || m_direction==MoveDown) {
0401                         m_position++; //place the cursor behind
0402                     }
0403                     if (m_direction==MoveRight || m_direction==MoveLeft) {
0404                         if (isAccepted()) {
0405                             return true;
0406                         }
0407                     }   
0408                 } else {
0409                     //We arrived at the top level element
0410                     return false;
0411                 }
0412             }
0413         }
0414     }
0415     return false;
0416 }
0417 
0418 FormulaCursor& FormulaCursor::operator+= ( int step )
0419 {
0420     m_position+=step;
0421     return *this;
0422 }
0423 
0424 int FormulaCursor::offset ( )
0425 {
0426     if (m_direction==MoveDown || m_direction==MoveRight) {
0427         return -1;
0428     } else if (m_direction==MoveUp || m_direction==MoveLeft) {
0429         return 1;
0430     }
0431     return 0;
0432 }
0433