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