00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "KoPathTool.h"
00022 #include "KoCanvasBase.h"
00023 #include "KoSelection.h"
00024 #include "KoShapeManager.h"
00025 #include "KoPointerEvent.h"
00026 #include "KoPathCommand.h"
00027 #include "KoParameterShape.h"
00028 #include "KoInsets.h"
00029 #include "KoShapeBorderModel.h"
00030 #include "KoPathControlPointMoveStrategy.h"
00031 #include "KoParameterChangeStrategy.h"
00032 #include "KoPathPointMoveStrategy.h"
00033 #include "KoPathPointRubberSelectStrategy.h"
00034 #include <kdebug.h>
00035 #include <klocale.h>
00036 #include <QKeyEvent>
00037
00038 KoPathTool::KoPathTool(KoCanvasBase *canvas)
00039 : KoTool(canvas)
00040 , m_activeHandle( 0 )
00041 , m_handleRadius( 3 )
00042 , m_pointSelection( this )
00043 , m_currentStrategy(0)
00044 {
00045 }
00046
00047 KoPathTool::~KoPathTool() {
00048 }
00049
00050 void KoPathTool::paint( QPainter &painter, KoViewConverter &converter) {
00051 KoSelectionSet selectedShapes = m_canvas->shapeManager()->selection()->selectedShapes( KoFlake::TopLevelSelection );
00052
00053 painter.save();
00054 painter.setRenderHint( QPainter::Antialiasing, true );
00055
00056 painter.setBrush( Qt::white );
00057 painter.setPen( Qt::blue );
00058
00059 foreach( KoShape *shape, selectedShapes )
00060 {
00061 KoPathShape *pathShape = dynamic_cast<KoPathShape*>( shape );
00062
00063 if ( !shape->isLocked() && pathShape )
00064 {
00065 painter.save();
00066 painter.setMatrix( shape->transformationMatrix( &converter ) * painter.matrix() );
00067
00068 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>( shape );
00069 if ( parameterShape && parameterShape->isParametricShape() )
00070 {
00071 parameterShape->paintHandles( painter, converter );
00072 }
00073 else
00074 {
00075 pathShape->paintPoints( painter, converter );
00076 }
00077
00078 painter.restore();
00079 }
00080 }
00081
00082 painter.restore();
00083
00084 if ( m_currentStrategy )
00085 {
00086 painter.save();
00087 m_currentStrategy->paint( painter, converter );
00088 painter.restore();
00089 }
00090
00091 painter.save();
00092 painter.setBrush( Qt::green );
00093 painter.setPen( Qt::blue );
00094
00095 m_pointSelection.paint( painter, converter );
00096
00097 painter.setBrush( Qt::red );
00098 painter.setPen( Qt::blue );
00099
00100 if ( m_activeHandle )
00101 m_activeHandle->paint( painter, converter );
00102
00103 painter.restore();
00104 }
00105
00106 void KoPathTool::mousePressEvent( KoPointerEvent *event ) {
00107
00108 if ( m_activeHandle )
00109 {
00110 m_activeHandle->mousePressEvent( event );
00111 }
00112 else
00113 {
00114 if( event->button() & Qt::LeftButton )
00115 {
00116 if( event->modifiers() & Qt::ControlModifier == 0 )
00117 {
00118 m_pointSelection.clear();
00119 }
00120
00121 Q_ASSERT(m_currentStrategy == 0);
00122 m_currentStrategy = new KoPathPointRubberSelectStrategy( this, m_canvas, event->point );
00123 }
00124 }
00125 }
00126
00127 void KoPathTool::mouseDoubleClickEvent( KoPointerEvent * ) {
00128 }
00129
00130 void KoPathTool::mouseMoveEvent( KoPointerEvent *event ) {
00131 if( event->button() & Qt::RightButton )
00132 return;
00133
00134
00135 m_pointSelection.repaint();
00136 if ( m_activeHandle )
00137 m_activeHandle->repaint();
00138
00139 if ( m_currentStrategy )
00140 {
00141 m_lastPoint = event->point;
00142 m_currentStrategy->handleMouseMove( event->point, event->modifiers() );
00143
00144
00145 m_pointSelection.repaint();
00146 if ( m_activeHandle )
00147 m_activeHandle->repaint();
00148 return;
00149 }
00150
00151 KoSelectionSet selectedShapes = m_canvas->shapeManager()->selection()->selectedShapes( KoFlake::TopLevelSelection );
00152 foreach( KoShape *shape, selectedShapes )
00153 {
00154 KoPathShape *pathShape = dynamic_cast<KoPathShape*>( shape );
00155
00156 if ( !shape->isLocked() && pathShape )
00157 {
00158 QRectF roi = handleRect( pathShape->documentToShape( event->point ) );
00159 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>( pathShape );
00160 if ( parameterShape && parameterShape->isParametricShape() )
00161 {
00162 int handleId = parameterShape->handleIdAt( roi );
00163
00164 if ( handleId != -1 )
00165 {
00166 useCursor(Qt::SizeAllCursor);
00167 delete m_activeHandle;
00168 m_activeHandle = new ActiveParameterHandle( this, parameterShape, handleId );
00169 m_activeHandle->repaint();
00170 return;
00171 }
00172
00173 }
00174 else
00175 {
00176 QList<KoPathPoint*> points = pathShape->pointsAt( roi );
00177 if( ! points.empty() )
00178 {
00179 useCursor(Qt::SizeAllCursor);
00180
00181 KoPathPoint *p = points.first();
00182 KoPathPoint::KoPointType type = KoPathPoint::Node;
00183
00184
00185
00186 if( p->properties() & KoPathPoint::HasControlPoint1 && roi.contains( p->controlPoint1() ) )
00187 type = KoPathPoint::ControlPoint1;
00188 else if( p->properties() & KoPathPoint::HasControlPoint2 && roi.contains( p->controlPoint2() ) )
00189 type = KoPathPoint::ControlPoint2;
00190
00191 delete m_activeHandle;
00192 m_activeHandle = new ActivePointHandle( this, p, type );
00193 m_activeHandle->repaint();
00194 return;
00195 }
00196 }
00197 }
00198 }
00199
00200 useCursor(Qt::ArrowCursor);
00201 delete m_activeHandle;
00202 m_activeHandle = 0;
00203 }
00204
00205 void KoPathTool::mouseReleaseEvent( KoPointerEvent *event ) {
00206 if ( m_currentStrategy )
00207 {
00208 m_currentStrategy->finishInteraction( event->modifiers() );
00209 KCommand *command = m_currentStrategy->createCommand();
00210 if ( command )
00211 m_canvas->addCommand( command, false );
00212 delete m_currentStrategy;
00213 m_currentStrategy = 0;
00214 }
00215 }
00216
00217 void KoPathTool::keyPressEvent(QKeyEvent *event) {
00218 if ( m_currentStrategy )
00219 {
00220 switch ( event->key() )
00221 {
00222 case Qt::Key_Control:
00223 case Qt::Key_Alt:
00224 case Qt::Key_Shift:
00225 case Qt::Key_Meta:
00226 m_currentStrategy->handleMouseMove( m_lastPoint, event->modifiers() );
00227 break;
00228 case Qt::Key_Escape:
00229 m_currentStrategy->cancelInteraction();
00230 delete m_currentStrategy;
00231 m_currentStrategy = 0;
00232 break;
00233 default:
00234 break;
00235 }
00236 }
00237 else
00238 {
00239 QList<KoPathPoint*> selectedPoints = m_pointSelection.selectedPoints().toList();
00240 KoPathShape * pathShape = 0;
00241 if ( m_pointSelection.objectCount() == 1 )
00242 {
00243 pathShape = selectedPoints[0]->parent();
00244 }
00245
00246 switch(event->key())
00247 {
00248 case Qt::Key_I:
00249 if(event->modifiers() & Qt::ControlModifier)
00250 {
00251 if( m_handleRadius > 3 )
00252 m_handleRadius--;
00253 }
00254 else
00255 m_handleRadius++;
00256 m_pointSelection.repaint();
00257 break;
00258 case Qt::Key_Delete:
00259
00260 if ( m_pointSelection.size() > 0 )
00261 {
00262 KoPointRemoveCommand *cmd = new KoPointRemoveCommand( m_pointSelection.selectedPointMap() );
00263 ActivePointHandle *pointHandle = dynamic_cast<ActivePointHandle*>( m_activeHandle );
00264 if ( pointHandle && m_pointSelection.contains( pointHandle->m_activePoint ) )
00265 {
00266 delete m_activeHandle;
00267 m_activeHandle = 0;
00268 }
00269 m_pointSelection.clear();
00270 m_canvas->addCommand( cmd, true );
00271 }
00272 break;
00273 case Qt::Key_Insert:
00274 if ( pathShape )
00275 {
00276 QList<KoPathSegment> segments;
00277 foreach( KoPathPoint* p, selectedPoints )
00278 {
00279 KoPathPoint *n = pathShape->nextPoint( p );
00280 if( m_pointSelection.contains( n ) )
00281 segments << qMakePair( p, n );
00282 }
00283 if( segments.size() )
00284 {
00285 KoSegmentSplitCommand *cmd = new KoSegmentSplitCommand( pathShape, segments, 0.5 );
00286 m_canvas->addCommand( cmd, true );
00287 }
00288 }
00289 break;
00290 case Qt::Key_D:
00291 if ( pathShape )
00292 {
00293 if ( pathShape )
00294 {
00295 pathShape->debugPath();
00296 }
00297 }
00298 break;
00299 case Qt::Key_B:
00300 if ( pathShape )
00301 {
00302 KoSubpathBreakCommand *cmd = 0;
00303 if( selectedPoints.size() == 1 )
00304 cmd = new KoSubpathBreakCommand( pathShape, selectedPoints.first() );
00305 else if( selectedPoints.size() >= 2 )
00306 cmd = new KoSubpathBreakCommand( pathShape, KoPathSegment( selectedPoints[0], selectedPoints[1] ) );
00307 m_canvas->addCommand( cmd, true );
00308 }
00309 break;
00310 case Qt::Key_J:
00311 if ( pathShape )
00312 {
00313 if( selectedPoints.size() == 2 )
00314 {
00315 KoPointJoinCommand *cmd = new KoPointJoinCommand( pathShape, selectedPoints[0], selectedPoints[1] );
00316 m_canvas->addCommand( cmd, true );
00317 }
00318 }
00319 break;
00320 case Qt::Key_F:
00321 if ( pathShape )
00322 {
00323 QList<KoPathSegment> segments;
00324 foreach( KoPathPoint* p, selectedPoints )
00325 {
00326 KoPathPoint *n = pathShape->nextPoint( p );
00327 if( m_pointSelection.contains( n ) )
00328 segments << qMakePair( p, n );
00329 }
00330 if( segments.size() )
00331 {
00332 KoSegmentTypeCommand *cmd = new KoSegmentTypeCommand( pathShape, segments, true );
00333 m_canvas->addCommand( cmd, true );
00334 }
00335 }
00336 break;
00337 case Qt::Key_C:
00338 if ( pathShape )
00339 {
00340 QList<KoPathSegment> segments;
00341 foreach( KoPathPoint* p, selectedPoints )
00342 {
00343 KoPathPoint *n = pathShape->nextPoint( p );
00344 if( m_pointSelection.contains( n ) )
00345 segments << qMakePair( p, n );
00346 }
00347 if( segments.size() )
00348 {
00349 KoSegmentTypeCommand *cmd = new KoSegmentTypeCommand( pathShape, segments, false );
00350 m_canvas->addCommand( cmd, true );
00351 }
00352 }
00353 break;
00354 case Qt::Key_P:
00355 {
00356 KMacroCommand *cmd = 0;
00357 foreach( KoShape *shape, m_canvas->shapeManager()->selection()->selectedShapes( KoFlake::TopLevelSelection ) )
00358 {
00359 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>( shape );
00360 qDebug() << "Key_P" << parameterShape;
00361
00362 if ( parameterShape && parameterShape->isParametricShape() )
00363 {
00364 if ( !cmd )
00365 {
00366 cmd = new KMacroCommand( i18n( "Convert to Path" ) );
00367 }
00368 cmd->addCommand( new KoParameterToPathCommand( parameterShape ) );
00369 repaint( parameterShape->boundingRect() );
00370 }
00371 }
00372 if ( cmd )
00373 {
00374 m_canvas->addCommand( cmd, true );
00375 }
00376 }
00377 break;
00378 }
00379 }
00380 event->accept();
00381 }
00382
00383 void KoPathTool::keyReleaseEvent(QKeyEvent *event) {
00384 if ( m_currentStrategy )
00385 {
00386 switch ( event->key() )
00387 {
00388 case Qt::Key_Control:
00389 case Qt::Key_Alt:
00390 case Qt::Key_Shift:
00391 case Qt::Key_Meta:
00392 m_currentStrategy->handleMouseMove( m_lastPoint, Qt::NoModifier );
00393 break;
00394 default:
00395 break;
00396 }
00397 }
00398 event->accept();
00399 }
00400
00401 void KoPathTool::activate (bool temporary) {
00402 Q_UNUSED(temporary);
00403 bool found = false;
00404 foreach(KoShape *shape, m_canvas->shapeManager()->selection()->selectedShapes())
00405 {
00406 KoPathShape *pathShape;
00407 pathShape = dynamic_cast<KoPathShape*> (shape);
00408 if( pathShape )
00409 {
00410 found = true;
00411 repaint( pathShape->shapeToDocument( pathShape->outline().controlPointRect() ) );
00412 }
00413 }
00414 if( !found ) {
00415 emit sigDone();
00416 return;
00417 }
00418 useCursor(Qt::ArrowCursor, true);
00419 }
00420
00421 void KoPathTool::deactivate() {
00422 qDebug() << "KoPathTool::deactivate()";
00423 m_pointSelection.clear();
00424 delete m_activeHandle;
00425 m_activeHandle = 0;
00426 delete m_currentStrategy;
00427 m_currentStrategy = 0;
00428 }
00429
00430 void KoPathTool::selectPoints( const QRectF &rect, bool clearSelection )
00431 {
00432 if( clearSelection )
00433 {
00434 m_pointSelection.clear();
00435 }
00436
00437 KoSelectionSet selectedShapes = m_canvas->shapeManager()->selection()->selectedShapes( KoFlake::TopLevelSelection );
00438 foreach( KoShape *shape, selectedShapes )
00439 {
00440 KoPathShape *pathShape = dynamic_cast<KoPathShape*>( shape );
00441
00442 if ( !shape->isLocked() && pathShape )
00443 {
00444 KoParameterShape *parameterShape = dynamic_cast<KoParameterShape*>( shape );
00445
00446 if ( ! parameterShape || ! parameterShape->isParametricShape() )
00447 {
00448 QList<KoPathPoint*> selected = pathShape->pointsAt( pathShape->documentToShape( rect ) );
00449 foreach( KoPathPoint* point, selected )
00450 {
00451 m_pointSelection.add( point, false );
00452 }
00453 }
00454 }
00455 }
00456 }
00457
00458 void KoPathTool::repaint( const QRectF &repaintRect ) {
00459
00460 m_canvas->updateCanvas( repaintRect.adjusted( -m_handleRadius, -m_handleRadius, m_handleRadius, m_handleRadius ) );
00461 }
00462
00463 QRectF KoPathTool::handleRect( const QPointF &p ) {
00464 return QRectF( p.x()-m_handleRadius, p.y()-m_handleRadius, 2*m_handleRadius, 2*m_handleRadius );
00465 }
00466
00467 QPointF KoPathTool::snapToGrid( const QPointF &p, Qt::KeyboardModifiers modifiers ) {
00468 if( ! m_canvas->snapToGrid() || modifiers & Qt::ShiftModifier )
00469 return p;
00470
00471 double gridX, gridY;
00472 m_canvas->gridSize( &gridX, &gridY );
00473 return QPointF( static_cast<int>( p.x() / gridX + 1e-10 ) * gridX,
00474 static_cast<int>( p.y() / gridY + 1e-10 ) * gridY );
00475 }
00476
00477 void KoPathTool::ActivePointHandle::paint( QPainter &painter, KoViewConverter &converter )
00478 {
00479 painter.save();
00480 painter.setMatrix( m_activePoint->parent()->transformationMatrix(&converter) * painter.matrix() );
00481 KoShape::applyConversion( painter, converter );
00482
00483 QRectF handle = converter.viewToDocument( m_tool->handleRect( QPoint(0,0) ) );
00484 m_activePoint->paint( painter, handle.size(), m_tool->m_pointSelection.contains( m_activePoint ) );
00485 painter.restore();
00486 }
00487
00488 void KoPathTool::ActivePointHandle::repaint() const
00489 {
00490 m_tool->repaint( m_activePoint->boundingRect() );
00491 }
00492
00493 void KoPathTool::ActivePointHandle::mousePressEvent( KoPointerEvent *event )
00494 {
00495 if( event->button() & Qt::LeftButton )
00496 {
00497
00498 if( event->modifiers() & Qt::ControlModifier )
00499 {
00500 if ( m_tool->m_pointSelection.contains( m_activePoint ) )
00501 {
00502 m_tool->m_pointSelection.remove( m_activePoint );
00503 }
00504 else
00505 {
00506 m_tool->m_pointSelection.add( m_activePoint, false );
00507 }
00508 }
00509 else
00510 {
00511
00512 if ( !m_tool->m_pointSelection.contains( m_activePoint ) )
00513 {
00514 m_tool->m_pointSelection.add( m_activePoint, true );
00515 }
00516 }
00517
00518 if ( m_activePointType == KoPathPoint::Node )
00519 {
00520 m_tool->m_currentStrategy = new KoPathPointMoveStrategy( m_tool, m_tool->m_canvas, event->point );
00521 }
00522 else
00523 {
00524 m_tool->m_currentStrategy = new KoPathControlPointMoveStrategy( m_tool, m_tool->m_canvas, m_activePoint, m_activePointType, event->point );
00525 }
00526
00527 }
00528 else if( event->button() & Qt::RightButton )
00529 {
00530 KoPathPoint::KoPointProperties props = m_activePoint->properties();
00531 if( (props & KoPathPoint::HasControlPoint1) == 0 || (props & KoPathPoint::HasControlPoint2) == 0 )
00532 return;
00533
00534
00535 if( props & KoPathPoint::IsSmooth )
00536 {
00537 props &= ~KoPathPoint::IsSmooth;
00538 props |= KoPathPoint::IsSymmetric;
00539 }
00540 else if( props & KoPathPoint::IsSymmetric )
00541 props &= ~KoPathPoint::IsSymmetric;
00542 else
00543 props |= KoPathPoint::IsSmooth;
00544
00545 KoPointPropertyCommand *cmd = new KoPointPropertyCommand( m_activePoint->parent(), m_activePoint, props );
00546 m_tool->m_canvas->addCommand( cmd, true );
00547 }
00548 }
00549
00550 void KoPathTool::ActiveParameterHandle::paint( QPainter &painter, KoViewConverter &converter )
00551 {
00552 painter.save();
00553 painter.setMatrix( m_parameterShape->transformationMatrix(&converter) * painter.matrix() );
00554
00555 QRectF handle = converter.viewToDocument( m_tool->handleRect( QPoint(0,0) ) );
00556 m_parameterShape->paintHandle( painter, converter, m_handleId );
00557 painter.restore();
00558 }
00559
00560 void KoPathTool::ActiveParameterHandle::repaint() const
00561 {
00562 m_tool->repaint( m_parameterShape->shapeToDocument( QRectF( m_parameterShape->handlePosition( m_handleId ), QSize( 1, 1 ) ) ) );
00563 }
00564
00565 void KoPathTool::ActiveParameterHandle::mousePressEvent( KoPointerEvent *event )
00566 {
00567 if( event->button() & Qt::LeftButton )
00568 {
00569 m_tool->m_pointSelection.clear();
00570 m_tool->m_currentStrategy = new KoParameterChangeStrategy( m_tool, m_tool->m_canvas, m_parameterShape, m_handleId );
00571 }
00572 }
00573
00574 void KoPathTool::KoPathPointSelection::paint( QPainter &painter, KoViewConverter &converter )
00575 {
00576 KoPathShapePointMap::iterator it( m_shapePointMap.begin() );
00577 for ( ; it != m_shapePointMap.end(); ++it )
00578 {
00579 painter.save();
00580
00581 painter.setMatrix( it.key()->transformationMatrix(&converter) * painter.matrix() );
00582 KoShape::applyConversion( painter, converter );
00583
00584 QRectF handle = converter.viewToDocument( m_tool->handleRect( QPoint(0,0) ) );
00585
00586 foreach( KoPathPoint *p, it.value() )
00587 p->paint( painter, handle.size(), true );
00588
00589 painter.restore();
00590 }
00591 }
00592
00593 void KoPathTool::KoPathPointSelection::add( KoPathPoint * point, bool clear )
00594 {
00595 bool allreadyIn = false;
00596 if ( clear )
00597 {
00598 if ( size() == 1 && m_selectedPoints.contains( point ) )
00599 {
00600 allreadyIn = true;
00601 }
00602 else
00603 {
00604 this->clear();
00605 }
00606 }
00607 else
00608 {
00609 allreadyIn = m_selectedPoints.contains( point );
00610 }
00611
00612 if ( !allreadyIn )
00613 {
00614 m_selectedPoints.insert( point );
00615 KoPathShape * pathShape = point->parent();
00616 KoPathShapePointMap::iterator it( m_shapePointMap.find( pathShape ) );
00617 if ( it == m_shapePointMap.end() )
00618 {
00619 it = m_shapePointMap.insert( pathShape, QSet<KoPathPoint *>() );
00620 }
00621 it.value().insert( point );
00622 m_tool->repaint( point->boundingRect() );
00623 }
00624 }
00625
00626 void KoPathTool::KoPathPointSelection::remove( KoPathPoint * point )
00627 {
00628 if ( m_selectedPoints.remove( point ) )
00629 {
00630 KoPathShape * pathShape = point->parent();
00631 m_shapePointMap[pathShape].remove( point );
00632 if ( m_shapePointMap[pathShape].size() == 0 )
00633 {
00634 m_shapePointMap.remove( pathShape );
00635 }
00636 }
00637 m_tool->repaint( point->boundingRect() );
00638 }
00639
00640 void KoPathTool::KoPathPointSelection::clear()
00641 {
00642 repaint();
00643 m_selectedPoints.clear();
00644 m_shapePointMap.clear();
00645 }
00646
00647 void KoPathTool::KoPathPointSelection::repaint()
00648 {
00649 foreach ( KoPathPoint *p, m_selectedPoints )
00650 {
00651 m_tool->repaint( p->boundingRect() );
00652 }
00653 }