00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "KoPathTool.h"
00024 #include "KoPathToolHandle.h"
00025 #include "KoCanvasBase.h"
00026 #include "KoShapeManager.h"
00027 #include "KoCanvasResourceProvider.h"
00028 #include "KoPointerEvent.h"
00029 #include "commands/KoPathPointTypeCommand.h"
00030 #include "commands/KoPathPointInsertCommand.h"
00031 #include "commands/KoPathPointRemoveCommand.h"
00032 #include "commands/KoPathSegmentTypeCommand.h"
00033 #include "commands/KoPathBreakAtPointCommand.h"
00034 #include "commands/KoPathSegmentBreakCommand.h"
00035 #include "commands/KoParameterToPathCommand.h"
00036 #include "commands/KoSubpathJoinCommand.h"
00037 #include "commands/KoPathPointMergeCommand.h"
00038 #include "KoParameterShape.h"
00039 #include "KoPathPoint.h"
00040 #include "KoPathPointRubberSelectStrategy.h"
00041 #include "KoPathSegmentChangeStrategy.h"
00042 #include "PathToolOptionWidget.h"
00043 #include "KoConnectionShape.h"
00044 #include "KoSnapGuide.h"
00045 #include "SnapGuideConfigWidget.h"
00046
00047 #include <KAction>
00048 #include <KIcon>
00049 #include <KDebug>
00050 #include <KLocale>
00051 #include <QtGui/QPainter>
00052 #include <QtGui/QBitmap>
00053 #include <QtGui/QTabWidget>
00054
00055 static unsigned char needle_bits[] = {
00056 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x01,
00057 0x80, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3e, 0x00, 0x7e,
00058 0x00, 0x7c, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x00
00059 };
00060
00061 static unsigned char needle_move_bits[] = {
00062 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x01,
00063 0x80, 0x03, 0x80, 0x07, 0x10, 0x0f, 0x38, 0x1f, 0x54, 0x3e, 0xfe, 0x7e,
00064 0x54, 0x7c, 0x38, 0x1c, 0x10, 0x18, 0x00, 0x00
00065 };
00066
00067
00068 qreal squaredDistance( const QPointF p1, const QPointF &p2 )
00069 {
00070 qreal dx = p1.x()-p2.x();
00071 qreal dy = p1.y()-p2.y();
00072 return dx*dx + dy*dy;
00073 }
00074
00075 KoPathTool::KoPathTool(KoCanvasBase *canvas)
00076 : KoTool(canvas)
00077 , m_activeHandle(0)
00078 , m_handleRadius(3)
00079 , m_pointSelection(this)
00080 , m_currentStrategy(0)
00081 {
00082 QActionGroup *points = new QActionGroup(this);
00083
00084 m_actionPathPointCorner = new KAction(KIcon("pathpoint-corner"), i18n("Corner point"), this);
00085 addAction("pathpoint-corner", m_actionPathPointCorner);
00086 m_actionPathPointCorner->setData(KoPathPointTypeCommand::Corner);
00087 points->addAction(m_actionPathPointCorner);
00088
00089 m_actionPathPointSmooth = new KAction(KIcon("pathpoint-smooth"), i18n("Smooth point"), this);
00090 addAction("pathpoint-smooth", m_actionPathPointSmooth);
00091 m_actionPathPointSmooth->setData(KoPathPointTypeCommand::Smooth);
00092 points->addAction(m_actionPathPointSmooth);
00093
00094 m_actionPathPointSymmetric = new KAction(KIcon("pathpoint-symmetric"), i18n("Symmetric Point"), this);
00095 addAction("pathpoint-symmetric", m_actionPathPointSymmetric);
00096 m_actionPathPointSymmetric->setData(KoPathPointTypeCommand::Symmetric);
00097 points->addAction(m_actionPathPointSymmetric);
00098
00099 m_actionCurvePoint = new KAction(KIcon("pathpoint-curve"), i18n("Make curve point"), this);
00100 addAction("pathpoint-curve", m_actionCurvePoint);
00101 connect(m_actionCurvePoint, SIGNAL(triggered()), this, SLOT(pointToCurve()));
00102
00103 m_actionLinePoint = new KAction(KIcon("pathpoint-line"), i18n("Make line point"), this);
00104 addAction("pathpoint-line", m_actionLinePoint);
00105 connect(m_actionLinePoint, SIGNAL(triggered()), this, SLOT(pointToLine()));
00106
00107 m_actionLineSegment = new KAction(KIcon("pathsegment-line"), i18n("Segment to Line"), this);
00108 m_actionLineSegment->setShortcut(Qt::Key_F);
00109 addAction("pathsegment-line", m_actionLineSegment);
00110 connect(m_actionLineSegment, SIGNAL(triggered()), this, SLOT(segmentToLine()));
00111
00112 m_actionCurveSegment = new KAction(KIcon("pathsegment-curve"), i18n("Segment to Curve"), this);
00113 m_actionCurveSegment->setShortcut(Qt::Key_C);
00114 addAction("pathsegment-curve", m_actionCurveSegment);
00115 connect(m_actionCurveSegment, SIGNAL(triggered()), this, SLOT(segmentToCurve()));
00116
00117 m_actionAddPoint = new KAction(KIcon("pathpoint-insert"), i18n("Insert point"), this);
00118 addAction("pathpoint-insert", m_actionAddPoint);
00119 m_actionAddPoint->setShortcut(Qt::Key_Insert);
00120 connect(m_actionAddPoint, SIGNAL(triggered()), this, SLOT(insertPoints()));
00121
00122 m_actionRemovePoint = new KAction(KIcon("pathpoint-remove"), i18n("Remove point"), this);
00123 m_actionRemovePoint->setShortcut(Qt::Key_Backspace);
00124 addAction("pathpoint-remove", m_actionRemovePoint);
00125 connect(m_actionRemovePoint, SIGNAL(triggered()), this, SLOT(removePoints()));
00126
00127 m_actionBreakPoint = new KAction(KIcon("path-break-point"), i18n("Break at point"), this);
00128 addAction("path-break-point", m_actionBreakPoint);
00129 connect(m_actionBreakPoint, SIGNAL(triggered()), this, SLOT(breakAtPoint()));
00130
00131 m_actionBreakSegment = new KAction(KIcon("path-break-segment"), i18n("Break at segment"), this);
00132 addAction("path-break-segment", m_actionBreakSegment);
00133 connect(m_actionBreakSegment, SIGNAL(triggered()), this, SLOT(breakAtSegment()));
00134
00135 m_actionJoinSegment = new KAction(KIcon("pathpoint-join"), i18n("Join with segment"), this);
00136 m_actionJoinSegment->setShortcut(Qt::Key_J);
00137 addAction("pathpoint-join", m_actionJoinSegment);
00138 connect(m_actionJoinSegment, SIGNAL(triggered()), this, SLOT(joinPoints()));
00139
00140 m_actionMergePoints = new KAction(KIcon("pathpoint-merge"), i18n("Merge points"), this);
00141 addAction("pathpoint-merge", m_actionMergePoints);
00142 connect(m_actionMergePoints, SIGNAL(triggered()), this, SLOT(mergePoints()));
00143
00144 m_actionConvertToPath = new KAction(KIcon("convert-to-path"), i18n("To Path"), this);
00145 m_actionConvertToPath->setShortcut(Qt::Key_P);
00146 addAction("convert-to-path", m_actionConvertToPath);
00147 connect(m_actionConvertToPath, SIGNAL(triggered()), this, SLOT(convertToPath()));
00148
00149 connect(points, SIGNAL(triggered(QAction*)), this, SLOT(pointTypeChanged(QAction*)));
00150 connect(&m_pointSelection, SIGNAL(selectionChanged()), this, SLOT(pointSelectionChanged()));
00151
00152 QBitmap b = QBitmap::fromData(QSize(16, 16), needle_bits);
00153 QBitmap m = b.createHeuristicMask(false);
00154
00155 m_selectCursor = QCursor(b, m, 2, 0);
00156
00157 b = QBitmap::fromData(QSize(16, 16), needle_move_bits);
00158 m = b.createHeuristicMask(false);
00159
00160 m_moveCursor = QCursor(b, m, 2, 0);
00161 }
00162
00163 KoPathTool::~KoPathTool()
00164 {
00165 }
00166
00167 QMap<QString, QWidget *> KoPathTool::createOptionWidgets()
00168 {
00169 QMap<QString, QWidget *> map;
00170
00171 PathToolOptionWidget * toolOptions = new PathToolOptionWidget(this);
00172 connect(this, SIGNAL(typeChanged(int)), toolOptions, SLOT(setSelectionType(int)));
00173
00174 updateOptionsWidget();
00175
00176 SnapGuideConfigWidget * snapOptions = new SnapGuideConfigWidget(m_canvas->snapGuide());
00177
00178 map.insert(i18n("Line/Curve"), toolOptions);
00179 map.insert(i18n("Snapping"), snapOptions);
00180
00181 return map;
00182 }
00183
00184 void KoPathTool::pointTypeChanged(QAction *type)
00185 {
00186 if (m_pointSelection.hasSelection()) {
00187 QList<KoPathPointData> selectedPoints = m_pointSelection.selectedPointsData();
00188 QList<KoPathPointData> pointToChange;
00189
00190 QList<KoPathPointData>::const_iterator it(selectedPoints.constBegin());
00191 for (; it != selectedPoints.constEnd(); ++it) {
00192 KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
00193 if (point) {
00194 if (point->activeControlPoint1() && point->activeControlPoint2()) {
00195 pointToChange.append(*it);
00196 }
00197 }
00198 }
00199
00200 if (!pointToChange.isEmpty()) {
00201 KoPathPointTypeCommand *cmd = new KoPathPointTypeCommand(pointToChange,
00202 static_cast<KoPathPointTypeCommand::PointType>(type->data().toInt()));
00203 m_canvas->addCommand(cmd);
00204 updateActions();
00205 }
00206 }
00207 }
00208
00209 void KoPathTool::insertPoints()
00210 {
00211 if (m_pointSelection.size() > 1) {
00212 QList<KoPathPointData> segments(m_pointSelection.selectedSegmentsData());
00213 if (!segments.isEmpty()) {
00214 KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, 0.5);
00215 m_canvas->addCommand(cmd);
00216
00217 foreach( KoPathPoint * p, cmd->insertedPoints() ) {
00218 m_pointSelection.add( p, false );
00219 }
00220 updateActions();
00221 }
00222 }
00223 }
00224
00225 void KoPathTool::removePoints()
00226 {
00227
00228 if (m_pointSelection.size() > 0) {
00229 QUndoCommand *cmd = KoPathPointRemoveCommand::createCommand(m_pointSelection.selectedPointsData(), m_canvas->shapeController());
00230 PointHandle *pointHandle = dynamic_cast<PointHandle*>(m_activeHandle);
00231 if (pointHandle && m_pointSelection.contains(pointHandle->activePoint())) {
00232 delete m_activeHandle;
00233 m_activeHandle = 0;
00234 }
00235 m_pointSelection.clear();
00236 m_canvas->addCommand(cmd);
00237 }
00238 }
00239
00240 void KoPathTool::pointToLine()
00241 {
00242 if (m_pointSelection.hasSelection()) {
00243 QList<KoPathPointData> selectedPoints = m_pointSelection.selectedPointsData();
00244 QList<KoPathPointData> pointToChange;
00245
00246 QList<KoPathPointData>::const_iterator it(selectedPoints.constBegin());
00247 for (; it != selectedPoints.constEnd(); ++it) {
00248 KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
00249 if (point && (point->activeControlPoint1() || point->activeControlPoint2()))
00250 pointToChange.append(*it);
00251 }
00252
00253 if (! pointToChange.isEmpty()) {
00254 m_canvas->addCommand(new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Line));
00255 updateActions();
00256 }
00257 }
00258 }
00259
00260 void KoPathTool::pointToCurve()
00261 {
00262 if (m_pointSelection.hasSelection()) {
00263 QList<KoPathPointData> selectedPoints = m_pointSelection.selectedPointsData();
00264 QList<KoPathPointData> pointToChange;
00265
00266 QList<KoPathPointData>::const_iterator it(selectedPoints.constBegin());
00267 for (; it != selectedPoints.constEnd(); ++it) {
00268 KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
00269 if (point && (! point->activeControlPoint1() || ! point->activeControlPoint2()))
00270 pointToChange.append(*it);
00271 }
00272
00273 if (! pointToChange.isEmpty()) {
00274 m_canvas->addCommand(new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Curve));
00275 updateActions();
00276 }
00277 }
00278 }
00279
00280 void KoPathTool::segmentToLine()
00281 {
00282 if (m_pointSelection.size() > 1) {
00283 QList<KoPathPointData> segments(m_pointSelection.selectedSegmentsData());
00284 if (segments.size() > 0) {
00285 m_canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Line));
00286 updateActions();
00287 }
00288 }
00289 }
00290
00291 void KoPathTool::segmentToCurve()
00292 {
00293 if (m_pointSelection.size() > 1) {
00294 QList<KoPathPointData> segments(m_pointSelection.selectedSegmentsData());
00295 if (segments.size() > 0) {
00296 m_canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Curve));
00297 updateActions();
00298 }
00299 }
00300 }
00301
00302 void KoPathTool::convertToPath()
00303 {
00304 QList<KoParameterShape*> shapesToConvert;
00305 foreach(KoShape *shape, m_pointSelection.selectedShapes()) {
00306 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
00307 if (parameterShape && parameterShape->isParametricShape())
00308 shapesToConvert.append(parameterShape);
00309 }
00310 if (shapesToConvert.count())
00311 m_canvas->addCommand(new KoParameterToPathCommand(shapesToConvert));
00312 updateOptionsWidget();
00313 }
00314
00315 void KoPathTool::joinPoints()
00316 {
00317 if (m_pointSelection.objectCount() == 1 && m_pointSelection.size() == 2) {
00318 QList<KoPathPointData> pd(m_pointSelection.selectedPointsData());
00319 const KoPathPointData & pd1 = pd.at(0);
00320 const KoPathPointData & pd2 = pd.at(1);
00321 KoPathShape * pathShape = pd1.pathShape;
00322 if (!pathShape->isClosedSubpath(pd1.pointIndex.first) &&
00323 (pd1.pointIndex.second == 0 ||
00324 pd1.pointIndex.second == pathShape->pointCountSubpath(pd1.pointIndex.first) - 1) &&
00325 !pathShape->isClosedSubpath(pd2.pointIndex.first) &&
00326 (pd2.pointIndex.second == 0 ||
00327 pd2.pointIndex.second == pathShape->pointCountSubpath(pd2.pointIndex.first) - 1)) {
00328 KoSubpathJoinCommand *cmd = new KoSubpathJoinCommand(pd1, pd2);
00329 m_canvas->addCommand(cmd);
00330 }
00331 updateActions();
00332 }
00333 }
00334
00335 void KoPathTool::mergePoints()
00336 {
00337 if (m_pointSelection.objectCount() != 1 || m_pointSelection.size() != 2)
00338 return;
00339
00340 QList<KoPathPointData> pointData = m_pointSelection.selectedPointsData();
00341 const KoPathPointData & pd1 = pointData.at(0);
00342 const KoPathPointData & pd2 = pointData.at(1);
00343 const KoPathPointIndex & index1 = pd1.pointIndex;
00344 const KoPathPointIndex & index2 = pd2.pointIndex;
00345
00346 KoPathShape * path = pd1.pathShape;
00347
00348
00349 if (path->isClosedSubpath(index1.first) || path->isClosedSubpath(index2.first))
00350 return;
00351
00352 if (index1.second != 0 && index1.second != path->pointCountSubpath(index1.first)-1)
00353 return;
00354
00355 if (index2.second != 0 && index2.second != path->pointCountSubpath(index2.first)-1)
00356 return;
00357
00358
00359 KoPathPointMergeCommand *cmd = new KoPathPointMergeCommand(pd1, pd2);
00360 m_canvas->addCommand(cmd);
00361 updateActions();
00362 }
00363
00364 void KoPathTool::breakAtPoint()
00365 {
00366 if (m_pointSelection.hasSelection()) {
00367 m_canvas->addCommand(new KoPathBreakAtPointCommand(m_pointSelection.selectedPointsData()));
00368 updateActions();
00369 }
00370 }
00371
00372 void KoPathTool::breakAtSegment()
00373 {
00374
00375 if (m_pointSelection.objectCount() == 1 && m_pointSelection.size() == 2) {
00376 QList<KoPathPointData> segments(m_pointSelection.selectedSegmentsData());
00377 if (segments.size() == 1) {
00378 m_canvas->addCommand(new KoPathSegmentBreakCommand(segments.at(0)));
00379 updateActions();
00380 }
00381 }
00382 }
00383
00384 void KoPathTool::paint(QPainter &painter, const KoViewConverter &converter)
00385 {
00386 painter.setRenderHint(QPainter::Antialiasing, true);
00387
00388 painter.setBrush(Qt::white);
00389 painter.setPen(Qt::blue);
00390
00391 foreach(KoPathShape *shape, m_pointSelection.selectedShapes()) {
00392 painter.save();
00393 painter.setMatrix(shape->absoluteTransformation(&converter) * painter.matrix());
00394
00395 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
00396 if (parameterShape && parameterShape->isParametricShape()) {
00397 parameterShape->paintHandles(painter, converter, m_handleRadius);
00398 } else {
00399 shape->paintPoints(painter, converter, m_handleRadius);
00400 }
00401
00402 painter.restore();
00403 }
00404
00405 if (m_currentStrategy) {
00406 painter.save();
00407 m_currentStrategy->paint(painter, converter);
00408 painter.restore();
00409 }
00410
00411 painter.setBrush(Qt::green);
00412 painter.setPen(Qt::blue);
00413
00414 m_pointSelection.paint(painter, converter);
00415
00416 painter.setBrush(Qt::red);
00417 painter.setPen(Qt::blue);
00418
00419 if (m_activeHandle) {
00420 if (m_activeHandle->check()) {
00421 m_activeHandle->paint(painter, converter);
00422 } else {
00423 delete m_activeHandle;
00424 m_activeHandle = 0;
00425 }
00426 }
00427
00428 if (m_currentStrategy) {
00429 painter.save();
00430 KoShape::applyConversion(painter, converter);
00431 m_canvas->snapGuide()->paint(painter, converter);
00432 painter.restore();
00433 }
00434 }
00435
00436 void KoPathTool::repaintDecorations()
00437 {
00438 foreach(KoShape *shape, m_pointSelection.selectedShapes()) {
00439 repaint(shape->boundingRect());
00440 }
00441
00442 m_pointSelection.repaint();
00443 updateOptionsWidget();
00444 }
00445
00446 void KoPathTool::mousePressEvent(KoPointerEvent *event)
00447 {
00448
00449 event->ignore();
00450 if (m_activeHandle) {
00451 m_currentStrategy = m_activeHandle->handleMousePress(event);
00452 event->accept();
00453 } else {
00454 if (event->button() & Qt::LeftButton) {
00455
00456
00457 KoPathShape * clickedShape = 0;
00458 KoPathPoint * clickedPoint = 0;
00459 qreal clickedPointParam = 0.0;
00460 if (segmentAtPoint(event->point, clickedShape, clickedPoint, clickedPointParam)) {
00461 KoPathPointIndex index = clickedShape->pathPointIndex(clickedPoint);
00462 KoPathPointData data(clickedShape, index);
00463 m_currentStrategy = new KoPathSegmentChangeStrategy(this, m_canvas, event->point, data, clickedPointParam);
00464 event->accept();
00465 } else {
00466 if ((event->modifiers() & Qt::ControlModifier) == 0) {
00467 m_pointSelection.clear();
00468 }
00469
00470 Q_ASSERT(m_currentStrategy == 0);
00471 m_currentStrategy = new KoPathPointRubberSelectStrategy(this, m_canvas, event->point);
00472 }
00473 }
00474 }
00475 }
00476
00477 void KoPathTool::mouseMoveEvent(KoPointerEvent *event)
00478 {
00479 if (event->button() & Qt::RightButton)
00480 return;
00481
00482 if (m_currentStrategy) {
00483 m_lastPoint = event->point;
00484 m_currentStrategy->handleMouseMove(event->point, event->modifiers());
00485
00486
00487 m_pointSelection.repaint();
00488 if (m_activeHandle)
00489 m_activeHandle->repaint();
00490 return;
00491 }
00492
00493 foreach(KoPathShape *shape, m_pointSelection.selectedShapes()) {
00494 QRectF roi = handleGrabRect(shape->documentToShape(event->point));
00495 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
00496 if (parameterShape && parameterShape->isParametricShape()) {
00497 int handleId = parameterShape->handleIdAt(roi);
00498 if (handleId != -1) {
00499 useCursor(m_moveCursor);
00500 emit statusTextChanged(i18n("Drag to move handle."));
00501 if (m_activeHandle)
00502 m_activeHandle->repaint();
00503 delete m_activeHandle;
00504
00505 if (KoConnectionShape * connectionShape = dynamic_cast<KoConnectionShape*>(parameterShape)) {
00506
00507 m_activeHandle = new ConnectionHandle(this, connectionShape, handleId);
00508 m_activeHandle->repaint();
00509 return;
00510 } else {
00511
00512 m_activeHandle = new ParameterHandle(this, parameterShape, handleId);
00513 m_activeHandle->repaint();
00514 return;
00515 }
00516 }
00517
00518 } else {
00519 QList<KoPathPoint*> points = shape->pointsAt(roi);
00520 if (! points.empty()) {
00521
00522 KoPathPoint * bestPoint = 0;
00523 KoPathPoint::KoPointType bestPointType = KoPathPoint::Node;
00524 qreal minDistance = HUGE_VAL;
00525 foreach(KoPathPoint *p, points) {
00526
00527 if (! m_pointSelection.contains(p) && ! roi.contains(p->point()))
00528 continue;
00529
00530
00531
00532 if (p->activeControlPoint1() && roi.contains(p->controlPoint1())) {
00533 qreal dist = squaredDistance(roi.center(), p->controlPoint1());
00534 if (dist < minDistance) {
00535 bestPoint = p;
00536 bestPointType = KoPathPoint::ControlPoint1;
00537 minDistance = dist;
00538 }
00539 }
00540
00541 if (p->activeControlPoint2() && roi.contains(p->controlPoint2())) {
00542 qreal dist = squaredDistance(roi.center(), p->controlPoint2());
00543 if (dist < minDistance) {
00544 bestPoint = p;
00545 bestPointType = KoPathPoint::ControlPoint2;
00546 minDistance = dist;
00547 }
00548 }
00549
00550
00551 qreal dist = squaredDistance(roi.center(), p->point());
00552 if (dist < minDistance) {
00553 bestPoint = p;
00554 bestPointType = KoPathPoint::Node;
00555 minDistance = dist;
00556 }
00557 }
00558
00559 if (! bestPoint)
00560 return;
00561
00562 useCursor(m_moveCursor);
00563 if (bestPointType == KoPathPoint::Node)
00564 emit statusTextChanged(i18n("Drag to move point. Shift click to change point type."));
00565 else
00566 emit statusTextChanged(i18n("Drag to move control point."));
00567
00568 PointHandle *prev = dynamic_cast<PointHandle*>(m_activeHandle);
00569 if (prev && prev->activePoint() == bestPoint && prev->activePointType() == bestPointType)
00570 return;
00571
00572 if (m_activeHandle)
00573 m_activeHandle->repaint();
00574 delete m_activeHandle;
00575 m_activeHandle = new PointHandle(this, bestPoint, bestPointType);
00576 m_activeHandle->repaint();
00577 return;
00578 }
00579 }
00580 }
00581
00582 useCursor(m_selectCursor);
00583 if (m_activeHandle)
00584 m_activeHandle->repaint();
00585 delete m_activeHandle;
00586 m_activeHandle = 0;
00587 uint selectedPointCount = m_pointSelection.size();
00588 if (selectedPointCount == 0)
00589 emit statusTextChanged("");
00590 else if (selectedPointCount == 1)
00591 emit statusTextChanged(i18n("Press B to break path at selected point."));
00592 else
00593 emit statusTextChanged(i18n("Press B to break path at selected segments."));
00594 }
00595
00596 void KoPathTool::mouseReleaseEvent(KoPointerEvent *event)
00597 {
00598 if (m_currentStrategy) {
00599 const bool hadNoSelection = !m_pointSelection.hasSelection();
00600 m_currentStrategy->finishInteraction(event->modifiers());
00601 QUndoCommand *command = m_currentStrategy->createCommand();
00602 if (command)
00603 m_canvas->addCommand(command);
00604 if (hadNoSelection && dynamic_cast<KoPathPointRubberSelectStrategy*>(m_currentStrategy)
00605 && !m_pointSelection.hasSelection()) {
00606
00607 event->ignore();
00608 }
00609 delete m_currentStrategy;
00610 m_currentStrategy = 0;
00611
00612 if (m_pointSelection.selectedShapes().count() == 1)
00613 emit pathChanged(m_pointSelection.selectedShapes().first());
00614 else
00615 emit pathChanged(0);
00616 }
00617 }
00618
00619 void KoPathTool::keyPressEvent(QKeyEvent *event)
00620 {
00621 if (m_currentStrategy) {
00622 switch (event->key()) {
00623 case Qt::Key_Control:
00624 case Qt::Key_Alt:
00625 case Qt::Key_Shift:
00626 case Qt::Key_Meta:
00627 m_currentStrategy->handleMouseMove(m_lastPoint, event->modifiers());
00628 break;
00629 case Qt::Key_Escape:
00630 m_currentStrategy->cancelInteraction();
00631 delete m_currentStrategy;
00632 m_currentStrategy = 0;
00633 break;
00634 default:
00635 event->ignore();
00636 return;
00637 }
00638 } else {
00639 switch (event->key()) {
00640
00641 case Qt::Key_I: {
00642 int handleRadius = m_canvas->resourceProvider()->handleRadius();
00643 if (event->modifiers() & Qt::ControlModifier)
00644 handleRadius--;
00645 else
00646 handleRadius++;
00647 m_canvas->resourceProvider()->setHandleRadius(handleRadius);
00648 break;
00649 }
00650 #ifndef NDEBUG
00651 case Qt::Key_D:
00652 if (m_pointSelection.objectCount() == 1) {
00653 QList<KoPathPointData> selectedPoints = m_pointSelection.selectedPointsData();
00654 selectedPoints[0].pathShape->debugPath();
00655 }
00656 break;
00657 #endif
00658 case Qt::Key_B:
00659 if (m_pointSelection.size() == 1)
00660 breakAtPoint();
00661 else if (m_pointSelection.size() >= 2)
00662 breakAtSegment();
00663 break;
00664 default:
00665 event->ignore();
00666 return;
00667 }
00668 }
00669 event->accept();
00670 }
00671
00672 void KoPathTool::keyReleaseEvent(QKeyEvent *event)
00673 {
00674 if (m_currentStrategy) {
00675 switch (event->key()) {
00676 case Qt::Key_Control:
00677 case Qt::Key_Alt:
00678 case Qt::Key_Shift:
00679 case Qt::Key_Meta:
00680 m_currentStrategy->handleMouseMove(m_lastPoint, Qt::NoModifier);
00681 break;
00682 default:
00683 break;
00684 }
00685 }
00686 event->accept();
00687 }
00688
00689 void KoPathTool::mouseDoubleClickEvent(KoPointerEvent *event)
00690 {
00691 event->ignore();
00692
00693
00694 if (m_currentStrategy)
00695 return;
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740 KoPathShape * clickedShape = 0;
00741 KoPathPoint * clickedSegmentStart = 0;
00742 qreal clickedPointParam = 0.0;
00743 if (! segmentAtPoint(event->point, clickedShape, clickedSegmentStart, clickedPointParam))
00744 return;
00745
00746 if (clickedShape && clickedSegmentStart) {
00747 QList<KoPathPointData> segments;
00748 segments.append(KoPathPointData(clickedShape, clickedShape->pathPointIndex(clickedSegmentStart)));
00749 KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, clickedPointParam);
00750 m_canvas->addCommand(cmd);
00751
00752 foreach( KoPathPoint * p, cmd->insertedPoints() ) {
00753 m_pointSelection.add( p, false );
00754 }
00755 updateActions();
00756 event->accept();
00757 }
00758 }
00759
00760 bool KoPathTool::segmentAtPoint( const QPointF &point, KoPathShape* &shape, KoPathPoint* &segmentStart, qreal &pointParam )
00761 {
00762
00763 const int clickProximity = 5;
00764
00765
00766 QPointF clickOffset = m_canvas->viewConverter()->viewToDocument( QPointF(clickProximity, clickProximity) );
00767
00768 const qreal maxSquaredDistance = clickOffset.x()*clickOffset.x();
00769
00770 KoPathShape * clickedShape = 0;
00771 KoPathPoint * clickedSegmentStart = 0;
00772 qreal clickedPointParam = 0.0;
00773
00774 foreach(KoPathShape *shape, m_pointSelection.selectedShapes()) {
00775 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>( shape );
00776 if (parameterShape && parameterShape->isParametricShape())
00777 continue;
00778
00779
00780 QPointF p = shape->documentToShape( point );
00781
00782 QRectF roi( p - clickOffset, p + clickOffset );
00783
00784 qreal minSqaredDistance = HUGE_VAL;
00785
00786 QList<KoPathSegment> segments = shape->segmentsAt( roi );
00787 foreach( const KoPathSegment &s, segments ) {
00788 qreal nearestPointParam = s.nearestPoint( p );
00789 QPointF nearestPoint = s.pointAt( nearestPointParam );
00790 QPointF diff = p - nearestPoint;
00791 qreal squaredDistance = diff.x()*diff.x() + diff.y()*diff.y();
00792
00793 if (squaredDistance > maxSquaredDistance)
00794 continue;
00795
00796 if (squaredDistance < minSqaredDistance) {
00797 clickedShape = shape;
00798 clickedSegmentStart = s.first();
00799 clickedPointParam = nearestPointParam;
00800 }
00801 }
00802 }
00803
00804 shape = clickedShape;
00805 segmentStart = clickedSegmentStart;
00806 pointParam = clickedPointParam;
00807
00808 return (shape && segmentStart);
00809 }
00810
00811 void KoPathTool::activate(bool temporary)
00812 {
00813 Q_UNUSED(temporary);
00814
00815 m_handleRadius = m_canvas->resourceProvider()->handleRadius();
00816 m_canvas->snapGuide()->reset();
00817
00818 repaintDecorations();
00819 QList<KoPathShape*> selectedShapes;
00820 foreach(KoShape *shape, m_canvas->shapeManager()->selection()->selectedShapes()) {
00821 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
00822
00823 if (shape->isEditable() && pathShape) {
00824
00825
00826
00827 repaint(pathShape->boundingRect());
00828 selectedShapes.append(pathShape);
00829 }
00830 }
00831 if (selectedShapes.isEmpty()) {
00832 emit done();
00833 return;
00834 }
00835 m_pointSelection.setSelectedShapes(selectedShapes);
00836 useCursor(m_selectCursor, true);
00837 connect(m_canvas->shapeManager()->selection(), SIGNAL(selectionChanged()), this, SLOT(activate()));
00838 updateOptionsWidget();
00839 updateActions();
00840 }
00841
00842 void KoPathTool::updateOptionsWidget()
00843 {
00844 PathToolOptionWidget::Types type;
00845 QList<KoPathShape*> selectedShapes = m_pointSelection.selectedShapes();
00846 foreach(KoPathShape *shape, selectedShapes) {
00847 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
00848 type |= parameterShape && parameterShape->isParametricShape() ?
00849 PathToolOptionWidget::ParametricShape : PathToolOptionWidget::PlainPath;
00850 }
00851 if (selectedShapes.count() == 1)
00852 emit pathChanged(selectedShapes.first());
00853 else
00854 emit pathChanged(0);
00855 emit typeChanged(type);
00856 }
00857
00858 void KoPathTool::updateActions()
00859 {
00860 const bool hasPointsSelected = m_pointSelection.hasSelection();
00861 m_actionPathPointCorner->setEnabled(hasPointsSelected);
00862 m_actionPathPointSmooth->setEnabled(hasPointsSelected);
00863 m_actionPathPointSymmetric->setEnabled(hasPointsSelected);
00864 m_actionRemovePoint->setEnabled(hasPointsSelected);
00865 m_actionBreakPoint->setEnabled(hasPointsSelected);
00866 m_actionCurvePoint->setEnabled(hasPointsSelected);
00867 m_actionLinePoint->setEnabled(hasPointsSelected);
00868
00869 bool hasSegmentsSelected = false;
00870 if (hasPointsSelected && m_pointSelection.size() > 1)
00871 hasSegmentsSelected = !m_pointSelection.selectedSegmentsData().isEmpty();
00872 m_actionAddPoint->setEnabled(hasSegmentsSelected);
00873 m_actionLineSegment->setEnabled(hasSegmentsSelected);
00874 m_actionCurveSegment->setEnabled(hasSegmentsSelected);
00875
00876 const uint objectCount = m_pointSelection.objectCount();
00877 const uint pointCount = m_pointSelection.size();
00878 m_actionBreakSegment->setEnabled(objectCount == 1 && pointCount == 2);
00879 m_actionJoinSegment->setEnabled(objectCount == 1 && pointCount == 2);
00880 m_actionMergePoints->setEnabled(objectCount == 1 && pointCount == 2);
00881 }
00882
00883 void KoPathTool::deactivate()
00884 {
00885 disconnect(m_canvas->shapeManager()->selection(), SIGNAL(selectionChanged()), this, SLOT(activate()));
00886 m_pointSelection.clear();
00887 m_pointSelection.setSelectedShapes(QList<KoPathShape*>());
00888 delete m_activeHandle;
00889 m_activeHandle = 0;
00890 delete m_currentStrategy;
00891 m_currentStrategy = 0;
00892 m_canvas->snapGuide()->reset();
00893 }
00894
00895 void KoPathTool::resourceChanged(int key, const QVariant & res)
00896 {
00897 if (key == KoCanvasResource::HandleRadius) {
00898 int oldHandleRadius = m_handleRadius;
00899
00900 m_handleRadius = res.toUInt();
00901
00902
00903 int maxRadius = qMax(m_handleRadius, oldHandleRadius);
00904 foreach(KoPathShape *shape, m_pointSelection.selectedShapes()) {
00905 QRectF controlPointRect = shape->absoluteTransformation(0).map(shape->outline()).controlPointRect();
00906 repaint(controlPointRect.adjusted(-maxRadius, -maxRadius, maxRadius, maxRadius));
00907 }
00908 }
00909 }
00910
00911 void KoPathTool::pointSelectionChanged()
00912 {
00913 updateActions();
00914 m_canvas->snapGuide()->setIgnoredPathPoints(m_pointSelection.selectedPoints().toList());
00915 emit selectionChanged(m_pointSelection.hasSelection());
00916 }
00917
00918 void KoPathTool::repaint(const QRectF &repaintRect)
00919 {
00920
00921
00922 qreal radius = m_handleRadius + 1;
00923 m_canvas->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius));
00924 }
00925
00926 void KoPathTool::deleteSelection()
00927 {
00928 removePoints();
00929 }
00930
00931 KoToolSelection * KoPathTool::selection()
00932 {
00933 return &m_pointSelection;
00934 }
00935
00936 #include "KoPathTool.moc"