File indexing completed on 2024-04-28 09:39:02

0001 /***************************************************************************
0002  *   Copyright (C) 2004-2006 by David Saxton                               *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "canvasmanipulator.h"
0012 #include "canvasitemparts.h"
0013 #include "cnitemgroup.h"
0014 #include "connector.h"
0015 #include "dptext.h"
0016 #include "flowcontainer.h"
0017 #include "icndocument.h"
0018 #include "itemview.h"
0019 #include "mechanicsdocument.h"
0020 #include "mechanicsgroup.h"
0021 #include "mechanicsitem.h"
0022 #include "node.h"
0023 #include "nodegroup.h"
0024 #include "picitem.h"
0025 #include "resizeoverlay.h"
0026 
0027 #include "utils.h"
0028 #include <cmath>
0029 #include <cstdlib>
0030 
0031 #include <KConfigGroup>
0032 #include <KSharedConfig>
0033 
0034 #include <QCursor>
0035 #include <QDebug>
0036 #include <QPainter>
0037 #include <QTimer>
0038 
0039 // FIXME: This source file is HUUUGE!!!, contains numerous clases, should be broken down.
0040 
0041 // BEGIN class CMManager
0042 CMManager::CMManager(ItemDocument *itemDocument)
0043     : QObject()
0044 {
0045     b_allowItemScroll = true;
0046     p_lastMouseOverResizeHandle = nullptr;
0047     m_canvasManipulator = nullptr;
0048     p_itemDocument = itemDocument;
0049     m_cmState = 0;
0050     p_lastMouseOverItem = nullptr;
0051     p_lastItemClicked = nullptr;
0052     m_drawAction = -1;
0053     m_allowItemScrollTmr = new QTimer(this);
0054     connect(m_allowItemScrollTmr, &QTimer::timeout, this, &CMManager::slotAllowItemScroll);
0055 
0056     KConfigGroup grGen = KSharedConfig::openConfig()->group("General");
0057     slotSetManualRoute(grGen.readEntry("ManualRouting", false));
0058 }
0059 
0060 CMManager::~CMManager()
0061 {
0062     delete m_allowItemScrollTmr;
0063     delete m_canvasManipulator;
0064 
0065     qDeleteAll(m_manipulatorInfoList);
0066     m_manipulatorInfoList.clear();
0067 }
0068 
0069 void CMManager::addManipulatorInfo(ManipulatorInfo *eventInfo)
0070 {
0071     if (eventInfo && !m_manipulatorInfoList.contains(eventInfo)) {
0072         m_manipulatorInfoList.prepend(eventInfo);
0073     }
0074 }
0075 
0076 void CMManager::cancelCurrentManipulation()
0077 {
0078     delete m_canvasManipulator;
0079     m_canvasManipulator = nullptr;
0080     setRepeatedAddId();
0081 }
0082 
0083 void CMManager::mousePressEvent(EventInfo eventInfo)
0084 {
0085     if (m_canvasManipulator) {
0086         if (m_canvasManipulator->mousePressedRepeat(eventInfo)) {
0087             delete m_canvasManipulator;
0088             m_canvasManipulator = nullptr;
0089         }
0090         return;
0091     }
0092 
0093     uint eventState = 0;
0094     if (eventInfo.isRightClick)
0095         eventState |= CMManager::es_right_click;
0096 
0097     if (eventInfo.ctrlPressed)
0098         eventState |= CMManager::es_ctrl_pressed;
0099 
0100     uint itemType = 0;
0101     uint cnItemType = 0;
0102 
0103     KtlQCanvasItem *qcanvasItem = eventInfo.qcanvasItemClickedOn;
0104 
0105     if (!qcanvasItem)
0106         itemType = it_none;
0107     else if (dynamic_cast<Node *>(qcanvasItem))
0108         itemType = it_node;
0109     else if (dynamic_cast<ConnectorLine *>(qcanvasItem) || dynamic_cast<Connector *>(qcanvasItem))
0110         itemType = it_connector;
0111     else if (dynamic_cast<PinItem *>(qcanvasItem))
0112         itemType = it_pin;
0113     else if (dynamic_cast<ResizeHandle *>(qcanvasItem))
0114         itemType = it_resize_handle;
0115     else if (DrawPart *drawPartClickedOn = dynamic_cast<DrawPart *>(qcanvasItem)) {
0116         itemType = it_drawpart;
0117 
0118         if (drawPartClickedOn->mousePressEvent(eventInfo)) {
0119             p_lastItemClicked = drawPartClickedOn;
0120             return;
0121         }
0122 
0123         if (drawPartClickedOn->isMovable())
0124             cnItemType |= CMManager::isi_isMovable;
0125     } else if (MechanicsItem *p_mechanicsItemClickedOn = dynamic_cast<MechanicsItem *>(qcanvasItem)) {
0126         itemType = it_mechanics_item;
0127 
0128         if (p_mechanicsItemClickedOn->mousePressEvent(eventInfo)) {
0129             p_lastItemClicked = p_mechanicsItemClickedOn;
0130             return;
0131         }
0132     } else {
0133         if (Widget *widget = dynamic_cast<Widget *>(qcanvasItem))
0134             qcanvasItem = widget->parent();
0135 
0136         if (CNItem *cnItemClickedOn = dynamic_cast<CNItem *>(qcanvasItem)) {
0137             itemType = it_canvas_item;
0138 
0139             if (cnItemClickedOn->mousePressEvent(eventInfo)) {
0140                 p_lastItemClicked = cnItemClickedOn;
0141                 return;
0142             }
0143 
0144             if (cnItemClickedOn->isMovable())
0145                 cnItemType |= CMManager::isi_isMovable;
0146         }
0147     }
0148 
0149     //  uint highestScore=0;
0150     //  ManipulatorInfo *best = nullptr;
0151     const ManipulatorInfoList::iterator end = m_manipulatorInfoList.end();
0152     for (ManipulatorInfoList::iterator it = m_manipulatorInfoList.begin(); it != end && !m_canvasManipulator; ++it) {
0153         if ((*it)->m_acceptManipulationPtr(eventState, m_cmState, itemType, cnItemType)) {
0154             m_canvasManipulator = (*it)->m_createManipulatorPtr(p_itemDocument, this);
0155         }
0156     }
0157 
0158     if (m_canvasManipulator) {
0159         if (m_canvasManipulator->mousePressedInitial(eventInfo)) {
0160             delete m_canvasManipulator;
0161             m_canvasManipulator = nullptr;
0162         }
0163     }
0164 }
0165 
0166 void CMManager::mouseDoubleClickEvent(const EventInfo &eventInfo)
0167 {
0168     if (m_canvasManipulator) {
0169         // Translate this into a repeat-click event
0170         if (m_canvasManipulator->mousePressedRepeat(eventInfo)) {
0171             delete m_canvasManipulator;
0172             m_canvasManipulator = nullptr;
0173         }
0174         return;
0175     }
0176 
0177     Item *item = dynamic_cast<Item *>(eventInfo.qcanvasItemClickedOn);
0178     if (item) {
0179         item->mouseDoubleClickEvent(eventInfo);
0180         return;
0181     }
0182 
0183     Widget *widget = dynamic_cast<Widget *>(eventInfo.qcanvasItemClickedOn);
0184     if (widget) {
0185         widget->parent()->mouseDoubleClickEvent(eventInfo);
0186         return;
0187     }
0188 }
0189 
0190 void CMManager::mouseMoveEvent(const EventInfo &eventInfo)
0191 {
0192     if (m_canvasManipulator) {
0193         if (m_canvasManipulator->mouseMoved(eventInfo)) {
0194             delete m_canvasManipulator;
0195             m_canvasManipulator = nullptr;
0196         }
0197         ItemView *itemView = dynamic_cast<ItemView *>(p_itemDocument->activeView());
0198         if (itemView)
0199             itemView->scrollToMouse(eventInfo.pos);
0200         return;
0201     }
0202 
0203     // BEGIN
0204     KtlQCanvasItem *qcnItem = p_itemDocument->itemAtTop(eventInfo.pos);
0205     Item *item;
0206     Widget *widget = dynamic_cast<Widget *>(qcnItem);
0207     if (widget)
0208         item = widget->parent();
0209     else
0210         item = dynamic_cast<Item *>(qcnItem);
0211 
0212     if (p_lastMouseOverItem != static_cast<QPointer<Item> >(item)) {
0213         QEvent event(QEvent::Leave);
0214 
0215         if (p_lastMouseOverItem)
0216             p_lastMouseOverItem->leaveEvent(nullptr);
0217 
0218         if (item)
0219             item->enterEvent(nullptr);
0220 
0221         p_lastMouseOverItem = item;
0222     }
0223 
0224     // If we clicked on an item, then continue to pass mouse events to that item until we release the mouse...
0225     if (p_lastItemClicked) {
0226         p_lastItemClicked->mouseMoveEvent(eventInfo);
0227     } else if (item) {
0228         item->mouseMoveEvent(eventInfo);
0229     }
0230     // END
0231 
0232     updateCurrentResizeHandle(dynamic_cast<ResizeHandle *>(qcnItem));
0233 }
0234 
0235 void CMManager::updateCurrentResizeHandle(ResizeHandle *resizeHandle)
0236 {
0237     if (p_lastMouseOverResizeHandle != static_cast<QPointer<ResizeHandle> >(resizeHandle)) {
0238         if (p_lastMouseOverResizeHandle)
0239             p_lastMouseOverResizeHandle->setHover(false);
0240         p_lastMouseOverResizeHandle = resizeHandle;
0241         if (resizeHandle)
0242             resizeHandle->setHover(true);
0243     }
0244 }
0245 
0246 void CMManager::mouseReleaseEvent(const EventInfo &eventInfo)
0247 {
0248     // If it returns true, then it has finished its editing operation
0249     if (m_canvasManipulator && m_canvasManipulator->mouseReleased(eventInfo)) {
0250         delete m_canvasManipulator;
0251         m_canvasManipulator = nullptr;
0252     }
0253 
0254     if (p_lastItemClicked) {
0255         p_lastItemClicked->mouseReleaseEvent(eventInfo);
0256         p_lastItemClicked = nullptr;
0257     }
0258 
0259     updateCurrentResizeHandle(dynamic_cast<ResizeHandle *>(p_itemDocument->itemAtTop(eventInfo.pos)));
0260 }
0261 
0262 void CMManager::wheelEvent(const EventInfo &eventInfo)
0263 {
0264     bool accepted = false;
0265     if (b_allowItemScroll) {
0266         KtlQCanvasItem *qcnItem = p_itemDocument->itemAtTop(eventInfo.pos);
0267         Item *item;
0268         Widget *widget = dynamic_cast<Widget *>(qcnItem);
0269         if (widget)
0270             item = widget->parent();
0271         else
0272             item = dynamic_cast<Item *>(qcnItem);
0273         if (item)
0274             accepted = item->wheelEvent(eventInfo);
0275     }
0276     if (!accepted) {
0277         // Only allow scrolling of items if we have not just been scrolling the canvas
0278         b_allowItemScroll = false;
0279         m_allowItemScrollTmr->stop();
0280         m_allowItemScrollTmr->setSingleShot(true);
0281         m_allowItemScrollTmr->start(500 /*,true */);
0282 
0283         ItemView *itemView = dynamic_cast<ItemView *>(p_itemDocument->activeView());
0284         if (itemView) {
0285             itemView->cvbEditor()->setPassEventsToView(false);
0286             itemView->cvbEditor()->contentsWheelEvent(eventInfo.wheelEvent(0, 0));
0287             itemView->cvbEditor()->setPassEventsToView(true);
0288         }
0289     }
0290 }
0291 
0292 void CMManager::setDrawAction(int drawAction)
0293 {
0294     if (m_drawAction == drawAction)
0295         return;
0296 
0297     m_drawAction = drawAction;
0298     setCMState(cms_draw, (m_drawAction != -1));
0299 }
0300 
0301 void CMManager::slotSetManualRoute(bool manualRoute)
0302 {
0303     KConfigGroup grGen = KSharedConfig::openConfig()->group("General");
0304     grGen.writeEntry("ManualRouting", manualRoute);
0305 
0306     setCMState(cms_manual_route, manualRoute);
0307 }
0308 
0309 void CMManager::setCMState(CMState type, bool state)
0310 {
0311     // Set or clear the correct bit
0312     state ? (m_cmState |= type) : (m_cmState &= (~type));
0313 
0314     if (type == CMManager::cms_manual_route)
0315         emit manualRoutingChanged(state);
0316 }
0317 
0318 void CMManager::setRepeatedAddId(const QString &repeatedId)
0319 {
0320     m_repeatedItemId = repeatedId;
0321 }
0322 // END class CMManager
0323 
0324 // BEGIN class CanvasManipulator
0325 CanvasManipulator::CanvasManipulator(ItemDocument *itemDocument, CMManager *cmManager)
0326 {
0327     p_itemDocument = itemDocument;
0328     p_icnDocument = dynamic_cast<ICNDocument *>(itemDocument);
0329     p_mechanicsDocument = dynamic_cast<MechanicsDocument *>(itemDocument);
0330     p_canvas = p_itemDocument->canvas();
0331     //  b_connectorsAllowedRouting = true;
0332     p_selectList = p_itemDocument->selectList();
0333     p_cnItemSelectList = dynamic_cast<CNItemGroup *>(p_selectList);
0334     p_mechItemSelectList = dynamic_cast<MechanicsGroup *>(p_selectList);
0335     p_cnItemClickedOn = nullptr;
0336     p_cmManager = cmManager;
0337 
0338     // connect(itemDocument->canvas(), SIGNAL(resized(const QRect &, const QRect &)), this, SLOT(canvasResized(const QRect &, const QRect &)));
0339     connect(itemDocument->canvas(), &Canvas::resized, this, &CanvasManipulator::canvasResized);
0340 }
0341 
0342 CanvasManipulator::~CanvasManipulator()
0343 {
0344 }
0345 
0346 QPoint CanvasManipulator::snapPoint(QPoint point)
0347 {
0348     point /= 8;
0349     point *= 8;
0350     point += QPoint(4, 4);
0351     return point;
0352 }
0353 // END class CanvasManipulator
0354 
0355 CMRepeatedItemAdd::CMRepeatedItemAdd(ItemDocument *itemDocument, CMManager *cmManager)
0356     : CanvasManipulator(itemDocument, cmManager)
0357 {
0358 }
0359 
0360 CMRepeatedItemAdd::~CMRepeatedItemAdd()
0361 {
0362 }
0363 
0364 CanvasManipulator *CMRepeatedItemAdd::construct(ItemDocument *itemDocument, CMManager *cmManager)
0365 {
0366     return new CMRepeatedItemAdd(itemDocument, cmManager);
0367 }
0368 
0369 ManipulatorInfo *CMRepeatedItemAdd::manipulatorInfo()
0370 {
0371     ManipulatorInfo *eventInfo = new ManipulatorInfo();
0372     eventInfo->m_acceptManipulationPtr = CMRepeatedItemAdd::acceptManipulation;
0373     eventInfo->m_createManipulatorPtr = CMRepeatedItemAdd::construct;
0374     return eventInfo;
0375 }
0376 
0377 bool CMRepeatedItemAdd::acceptManipulation(uint /*eventState*/, uint cmState, uint /*itemType*/, uint /*cnItemType*/)
0378 {
0379     return (cmState & CMManager::cms_repeated_add);
0380 }
0381 
0382 bool CMRepeatedItemAdd::mousePressedRepeat(const EventInfo &eventInfo)
0383 {
0384     return mousePressedInitial(eventInfo);
0385 }
0386 
0387 bool CMRepeatedItemAdd::mousePressedInitial(const EventInfo &eventInfo)
0388 {
0389     m_eventInfo = eventInfo;
0390     if (eventInfo.isRightClick) {
0391         p_cmManager->setCMState(CMManager::cms_repeated_add, false);
0392         return true;
0393     }
0394 
0395     p_icnDocument->addItem(p_cmManager->repeatedItemId(), eventInfo.pos, true);
0396     p_itemDocument->requestStateSave();
0397     return false;
0398 }
0399 
0400 bool CMRepeatedItemAdd::mouseMoved(const EventInfo & /*eventInfo*/)
0401 {
0402     return false;
0403 }
0404 
0405 bool CMRepeatedItemAdd::mouseReleased(const EventInfo & /*eventInfo*/)
0406 {
0407     return false;
0408 }
0409 
0410 CMRightClick::CMRightClick(ItemDocument *itemDocument, CMManager *cmManager)
0411     : CanvasManipulator(itemDocument, cmManager)
0412 {
0413 }
0414 
0415 CMRightClick::~CMRightClick()
0416 {
0417 }
0418 
0419 CanvasManipulator *CMRightClick::construct(ItemDocument *itemDocument, CMManager *cmManager)
0420 {
0421     return new CMRightClick(itemDocument, cmManager);
0422 }
0423 
0424 ManipulatorInfo *CMRightClick::manipulatorInfo()
0425 {
0426     ManipulatorInfo *eventInfo = new ManipulatorInfo();
0427     //  eventInfo->m_eventState.m_activate = CMManager::es_right_click;
0428     eventInfo->m_acceptManipulationPtr = CMRightClick::acceptManipulation;
0429     eventInfo->m_createManipulatorPtr = CMRightClick::construct;
0430     return eventInfo;
0431 }
0432 
0433 bool CMRightClick::acceptManipulation(uint eventState, uint /*cmState*/, uint /*itemType*/, uint /*cnItemType*/)
0434 {
0435     return eventState & CMManager::es_right_click;
0436 }
0437 
0438 bool CMRightClick::mousePressedInitial(const EventInfo &eventInfo)
0439 {
0440     m_eventInfo = eventInfo;
0441     p_itemDocument->canvasRightClick(eventInfo.globalPos, eventInfo.qcanvasItemClickedOn);
0442     return true;
0443 }
0444 
0445 bool CMRightClick::mouseMoved(const EventInfo & /*eventInfo*/)
0446 {
0447     return true;
0448 }
0449 
0450 bool CMRightClick::mouseReleased(const EventInfo & /*eventInfo*/)
0451 {
0452     return true;
0453 }
0454 
0455 // BEGIN class ConnectorDraw
0456 ConnectorDraw::ConnectorDraw(ItemDocument *itemDocument, CMManager *cmManager)
0457     : CanvasManipulator(itemDocument, cmManager)
0458 {
0459     p_startNode = nullptr;
0460     p_startConnector = nullptr;
0461     p_endNode = nullptr;
0462     p_endConnector = nullptr;
0463 }
0464 
0465 ConnectorDraw::~ConnectorDraw()
0466 {
0467 }
0468 
0469 QColor ConnectorDraw::validConnectionColor()
0470 {
0471     return QColor(255, 166, 0);
0472 }
0473 
0474 QPoint ConnectorDraw::toValidPos(const QPoint &clickPos, Connector *clickedConnector) const
0475 {
0476     if (!clickedConnector)
0477         return clickPos;
0478 
0479     const QPointList pointList = clickedConnector->connectorPoints();
0480 
0481     QPointList::const_iterator end = pointList.end();
0482 
0483     double dl[] = {0.5, 8.5, 11.5, 18.0, 23.0}; // various distances rounded up of (0,0) cells, (0,1), etc
0484     for (unsigned i = 0; i < 5; ++i) {
0485         for (QPointList::const_iterator it = pointList.begin(); it != end; ++it) {
0486             if (qpoint_distance(*it, clickPos) <= dl[i])
0487                 return *it;
0488         }
0489     }
0490 
0491     return clickPos;
0492 }
0493 
0494 Connector *ConnectorDraw::toConnector(Node *node)
0495 {
0496     if (!node || node->numCon(true, false) < 3)
0497         return nullptr;
0498 
0499     return node->getAConnector();
0500 }
0501 
0502 void ConnectorDraw::grabEndStuff(KtlQCanvasItem *endItem, const QPoint &pos, bool posIsExact)
0503 {
0504     if (!endItem)
0505         return;
0506 
0507     CNItem *cnItem = dynamic_cast<CNItem *>(endItem);
0508     if (cnItem && !posIsExact)
0509         p_endNode = cnItem->getClosestNode(pos);
0510     else
0511         p_endNode = dynamic_cast<Node *>(endItem);
0512 
0513     if (p_endNode && p_endNode->numCon(true, false) > 2) {
0514         p_endConnector = toConnector(p_endNode);
0515         p_endNode = nullptr;
0516     }
0517 
0518     // If the endItem is a node, we have to finish exactly on the end when posIsExact is true
0519     if (posIsExact && p_endNode && (p_endNode->x() != pos.x() || p_endNode->y() != pos.y()))
0520         p_endNode = nullptr;
0521 
0522     if (!p_endConnector)
0523         p_endConnector = dynamic_cast<Connector *>(endItem);
0524 }
0525 // END class ConnectorDraw
0526 
0527 // BEGIN class CMAutoConnector
0528 CMAutoConnector::CMAutoConnector(ItemDocument *itemDocument, CMManager *cmManager)
0529     : ConnectorDraw(itemDocument, cmManager)
0530 {
0531     m_connectorLine = nullptr;
0532     p_startNode = nullptr;
0533     p_startConnector = nullptr;
0534 }
0535 
0536 CMAutoConnector::~CMAutoConnector()
0537 {
0538     delete m_connectorLine;
0539 }
0540 
0541 CanvasManipulator *CMAutoConnector::construct(ItemDocument *itemDocument, CMManager *cmManager)
0542 {
0543     return new CMAutoConnector(itemDocument, cmManager);
0544 }
0545 
0546 ManipulatorInfo *CMAutoConnector::manipulatorInfo()
0547 {
0548     ManipulatorInfo *eventInfo = new ManipulatorInfo();
0549     eventInfo->m_acceptManipulationPtr = CMAutoConnector::acceptManipulation;
0550     eventInfo->m_createManipulatorPtr = CMAutoConnector::construct;
0551     return eventInfo;
0552 }
0553 
0554 bool CMAutoConnector::acceptManipulation(uint /*eventState*/, uint cmState, uint itemType, uint /*cnItemType*/)
0555 {
0556     return (itemType & (CMManager::it_node | CMManager::it_connector)) && !(cmState & CMManager::cms_manual_route);
0557 }
0558 
0559 bool CMAutoConnector::mousePressedInitial(const EventInfo &eventInfo)
0560 {
0561     m_eventInfo = eventInfo;
0562 
0563     p_startNode = dynamic_cast<Node *>(eventInfo.qcanvasItemClickedOn);
0564 
0565     if (p_startNode) {
0566         m_eventInfo.pos = m_prevPos = p_icnDocument->gridSnap(QPoint(int(p_startNode->x()), int(p_startNode->y())));
0567         if (p_startNode->numCon(true, false) > 2) {
0568             p_startConnector = toConnector(p_startNode);
0569             p_startNode = nullptr;
0570         }
0571     } else if ((p_startConnector = dynamic_cast<Connector *>(eventInfo.qcanvasItemClickedOn))) {
0572         //      startConnectorPoint = m_eventInfo.pos = m_prevPos = p_icnDocument->gridSnap(m_eventInfo.pos);
0573         startConnectorPoint = m_eventInfo.pos = m_prevPos = toValidPos(m_eventInfo.pos, p_startConnector);
0574     } else
0575         return true;
0576 
0577     p_icnDocument->unselectAll();
0578 
0579     delete m_connectorLine;
0580     m_connectorLine = new KtlQCanvasLine(p_canvas);
0581     m_connectorLine->setPen(QColor(0, 0, 0));
0582     m_connectorLine->setZ(ItemDocument::Z::ConnectorCreateLine);
0583     m_connectorLine->show();
0584     return false;
0585 }
0586 
0587 bool CMAutoConnector::mouseMoved(const EventInfo &eventInfo)
0588 {
0589     const QPoint pos = eventInfo.pos;
0590 
0591     int newX = p_icnDocument->gridSnap(pos.x());
0592     int newY = p_icnDocument->gridSnap(pos.y());
0593 
0594     bool movedFlag = false;
0595 
0596     if (newX != m_prevPos.x()) {
0597         m_prevPos.setX(newX);
0598         movedFlag = true;
0599     }
0600 
0601     if (newY != m_prevPos.y()) {
0602         m_prevPos.setY(newY);
0603         movedFlag = true;
0604     }
0605 
0606     m_connectorLine->setPoints(m_eventInfo.pos.x(), m_eventInfo.pos.y(), newX, newY);
0607 
0608     if (movedFlag) {
0609         KtlQCanvasItem *startItem = nullptr;
0610         if (p_startNode)
0611             startItem = p_startNode;
0612         else if (p_startConnector)
0613             startItem = p_startConnector;
0614 
0615         KtlQCanvasItem *endItem = p_icnDocument->itemAtTop(QPoint(newX, newY));
0616         if (CNItem *cni = dynamic_cast<CNItem *>(endItem))
0617             endItem = cni->getClosestNode(QPoint(newX, newY));
0618 
0619         bool validLine = p_icnDocument->canConnect(startItem, endItem);
0620         m_connectorLine->setPen(validLine ? validConnectionColor() : Qt::black);
0621     }
0622     return false;
0623 }
0624 
0625 bool CMAutoConnector::mouseReleased(const EventInfo &eventInfo)
0626 {
0627     const QPoint pos = eventInfo.pos;
0628 
0629     QPoint end = m_connectorLine->endPoint();
0630     delete m_connectorLine;
0631     m_connectorLine = nullptr;
0632 
0633     KtlQCanvasItem *qcanvasItem = p_icnDocument->itemAtTop(end);
0634     if (!qcanvasItem)
0635         return true;
0636 
0637     grabEndStuff(qcanvasItem, pos, false);
0638 
0639     if (p_startConnector) {
0640         if (p_endConnector) {
0641             if (!p_icnDocument->createConnector(p_endConnector, p_startConnector, p_icnDocument->gridSnap(pos), startConnectorPoint))
0642                 return true;
0643         } else if (p_endNode) {
0644             if (!p_icnDocument->createConnector(p_endNode, p_startConnector, startConnectorPoint))
0645                 return true;
0646         } else
0647             return true;
0648     } else if (p_startNode) {
0649         if (p_endConnector) {
0650             if (!p_icnDocument->createConnector(p_startNode, p_endConnector, p_icnDocument->gridSnap(pos)))
0651                 return true;
0652         } else if (p_endNode) {
0653             if (!p_icnDocument->createConnector(p_startNode, p_endNode))
0654                 return true;
0655         } else
0656             return true;
0657     } else
0658         return true;
0659 
0660     p_itemDocument->requestStateSave();
0661     return true;
0662 }
0663 // END class CMAutoConnector
0664 
0665 // BEGIN class CMManualConnector
0666 CMManualConnector::CMManualConnector(ItemDocument *itemDocument, CMManager *cmManager)
0667     : ConnectorDraw(itemDocument, cmManager)
0668 {
0669     m_manualConnectorDraw = nullptr;
0670 }
0671 
0672 CMManualConnector::~CMManualConnector()
0673 {
0674     delete m_manualConnectorDraw;
0675 }
0676 
0677 CanvasManipulator *CMManualConnector::construct(ItemDocument *itemDocument, CMManager *cmManager)
0678 {
0679     return new CMManualConnector(itemDocument, cmManager);
0680 }
0681 
0682 ManipulatorInfo *CMManualConnector::manipulatorInfo()
0683 {
0684     ManipulatorInfo *eventInfo = new ManipulatorInfo();
0685     eventInfo->m_acceptManipulationPtr = CMManualConnector::acceptManipulation;
0686     eventInfo->m_createManipulatorPtr = CMManualConnector::construct;
0687     return eventInfo;
0688 }
0689 
0690 bool CMManualConnector::acceptManipulation(uint /*eventState*/, uint cmState, uint itemType, uint /*cnItemType*/)
0691 {
0692     return (itemType & (CMManager::it_node | CMManager::it_connector)) && (cmState & CMManager::cms_manual_route);
0693 }
0694 
0695 bool CMManualConnector::mousePressedInitial(const EventInfo &eventInfo)
0696 {
0697     if (eventInfo.isRightClick)
0698         return true;
0699 
0700     m_eventInfo = eventInfo;
0701 
0702     p_icnDocument->unselectAll();
0703 
0704     QPoint sp;
0705 
0706     if ((p_startNode = dynamic_cast<Node *>(eventInfo.qcanvasItemClickedOn))) {
0707         sp.setX(int(p_startNode->x()));
0708         sp.setY(int(p_startNode->y()));
0709         if (p_startNode->numCon(true, false) > 2) {
0710             p_startConnector = toConnector(p_startNode);
0711             p_startNode = nullptr;
0712         }
0713     } else {
0714         p_startConnector = dynamic_cast<Connector *>(eventInfo.qcanvasItemClickedOn);
0715         sp = toValidPos(eventInfo.pos, p_startConnector);
0716     }
0717     startConnectorPoint = sp;
0718 
0719     if (m_manualConnectorDraw)
0720         delete m_manualConnectorDraw;
0721     m_manualConnectorDraw = new ManualConnectorDraw(p_icnDocument, sp);
0722     return false;
0723 }
0724 
0725 bool CMManualConnector::mousePressedRepeat(const EventInfo &eventInfo)
0726 {
0727     m_eventInfo = eventInfo;
0728     if (eventInfo.isRightClick) {
0729         return true;
0730     }
0731     m_manualConnectorDraw->mouseClicked(p_icnDocument->gridSnap(m_eventInfo.pos));
0732     return false;
0733 }
0734 
0735 bool CMManualConnector::mouseMoved(const EventInfo &eventInfo)
0736 {
0737     if (!m_manualConnectorDraw)
0738         return true;
0739 
0740     const QPoint pos = eventInfo.pos;
0741 
0742     int newX = p_icnDocument->gridSnap(pos.x());
0743     int newY = p_icnDocument->gridSnap(pos.y());
0744 
0745     bool movedFlag = false;
0746 
0747     if (newX != m_prevPos.x()) {
0748         m_prevPos.setX(newX);
0749         movedFlag = true;
0750     }
0751 
0752     if (newY != m_prevPos.y()) {
0753         m_prevPos.setY(newY);
0754         movedFlag = true;
0755     }
0756 
0757     if (movedFlag) {
0758         KtlQCanvasItem *startItem = nullptr;
0759         if (p_startNode)
0760             startItem = p_startNode;
0761         else if (p_startConnector)
0762             startItem = p_startConnector;
0763 
0764         KtlQCanvasItem *endItem = p_icnDocument->itemAtTop(QPoint(newX, newY));
0765 
0766         // If the endItem is a node, we have to finish exactly on the end.
0767         if (Node *node = dynamic_cast<Node *>(endItem)) {
0768             if (node->x() != newX || node->y() != newY)
0769                 endItem = nullptr;
0770         }
0771 
0772         bool validLine = p_icnDocument->canConnect(startItem, endItem);
0773 
0774         m_manualConnectorDraw->setColor(validLine ? validConnectionColor() : Qt::black);
0775         m_manualConnectorDraw->mouseMoved(QPoint(newX, newY));
0776     }
0777 
0778     return false;
0779 }
0780 
0781 bool CMManualConnector::mouseReleased(const EventInfo &eventInfo)
0782 {
0783     if (!m_manualConnectorDraw)
0784         return true;
0785 
0786     QPoint pos = p_icnDocument->gridSnap(eventInfo.pos);
0787 
0788     grabEndStuff(m_manualConnectorDraw->mouseClicked(pos), pos, true);
0789 
0790     if (!p_endNode && !p_endConnector)
0791         return false;
0792 
0793     // Create the points that define the manual route
0794     QPointList list = m_manualConnectorDraw->pointList();
0795     delete m_manualConnectorDraw;
0796     m_manualConnectorDraw = nullptr;
0797 
0798     if (p_startConnector) {
0799         if (p_endConnector) {
0800             if (!p_icnDocument->createConnector(p_endConnector, p_startConnector, p_icnDocument->gridSnap(pos), startConnectorPoint, &list))
0801                 return true;
0802         } else // if (p_endNode)
0803         {
0804             if (!p_icnDocument->createConnector(p_endNode, p_startConnector, startConnectorPoint, &list))
0805                 return true;
0806         }
0807     } else if (p_startNode) {
0808         if (p_endConnector) {
0809             if (!p_icnDocument->createConnector(p_startNode, p_endConnector, p_icnDocument->gridSnap(pos), &list))
0810                 return true;
0811         } else // if (p_endNode)
0812         {
0813             if (!p_icnDocument->createConnector(p_startNode, p_endNode, &list))
0814                 return true;
0815         }
0816     } else
0817         return true;
0818 
0819     p_itemDocument->requestStateSave();
0820     return true;
0821 }
0822 // END class CMManualConnector
0823 
0824 // BEGIN class CMItemMove
0825 CMItemMove::CMItemMove(ItemDocument *itemDocument, CMManager *cmManager)
0826     : CanvasManipulator(itemDocument, cmManager)
0827 {
0828     p_flowContainerCandidate = nullptr;
0829     m_bItemsSnapToGrid = false;
0830 }
0831 
0832 CMItemMove::~CMItemMove()
0833 {
0834 }
0835 
0836 CanvasManipulator *CMItemMove::construct(ItemDocument *itemDocument, CMManager *cmManager)
0837 {
0838     return new CMItemMove(itemDocument, cmManager);
0839 }
0840 
0841 ManipulatorInfo *CMItemMove::manipulatorInfo()
0842 {
0843     ManipulatorInfo *eventInfo = new ManipulatorInfo();
0844     eventInfo->m_acceptManipulationPtr = CMItemMove::acceptManipulation;
0845     eventInfo->m_createManipulatorPtr = CMItemMove::construct;
0846     return eventInfo;
0847 }
0848 
0849 bool CMItemMove::acceptManipulation(uint eventState, uint /*cmState*/, uint itemType, uint cnItemType)
0850 {
0851     return ((itemType & CMManager::it_canvas_item) || (itemType & CMManager::it_drawpart)) && (cnItemType & CMManager::isi_isMovable) && !(eventState & CMManager::es_right_click);
0852 }
0853 
0854 bool CMItemMove::mousePressedInitial(const EventInfo &eventInfo)
0855 {
0856     m_eventInfo = eventInfo;
0857     m_prevPos = eventInfo.pos;
0858 
0859     Item *item = dynamic_cast<Item *>(eventInfo.qcanvasItemClickedOn);
0860 
0861     if (!item)
0862         return true;
0863 
0864     if (!p_selectList->contains(item)) {
0865         if (!eventInfo.ctrlPressed)
0866             p_itemDocument->unselectAll();
0867 
0868         p_itemDocument->select(item);
0869     } else if (m_eventInfo.ctrlPressed)
0870         p_itemDocument->unselect(item);
0871 
0872     if (p_selectList->isEmpty())
0873         return true;
0874 
0875     // We want to allow dragging into FlowContainers if this is a FlowView
0876     p_flowContainerCandidate = nullptr;
0877     {
0878         const ItemList &itemList = p_icnDocument->itemList();
0879         const ItemList::const_iterator ciEnd = itemList.end();
0880         for (ItemList::const_iterator it = itemList.begin(); it != ciEnd; ++it) {
0881             if (FlowContainer *flowContainer = dynamic_cast<FlowContainer *>(static_cast<Item *>(*it)))
0882                 flowContainer->setFullBounds(true);
0883         }
0884     }
0885 
0886     ItemList itemList = p_cnItemSelectList->items(false);
0887     itemList.removeAll(static_cast<Item *>(nullptr));
0888 
0889     m_bItemsSnapToGrid = false;
0890     const ItemList::iterator itemListEnd = itemList.end();
0891     for (ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it) {
0892         CNItem *cnItem = dynamic_cast<CNItem *>(static_cast<Item *>(*it));
0893         if (!cnItem || !cnItem->canvas())
0894             continue;
0895 
0896         m_bItemsSnapToGrid = true;
0897     }
0898 
0899     if (m_bItemsSnapToGrid)
0900         m_prevSnapPoint = this->snapPoint(m_prevPos);
0901     else
0902         m_prevSnapPoint = m_prevPos;
0903 
0904     ConnectorList fixedConnectors;
0905     p_icnDocument->getTranslatable(itemList, &fixedConnectors, &m_translatableConnectors, &m_translatableNodeGroups);
0906 
0907     const ConnectorList::iterator fixedConnectorsEnd = fixedConnectors.end();
0908     for (ConnectorList::iterator it = fixedConnectors.begin(); it != fixedConnectorsEnd; ++it)
0909         (*it)->setSemiHidden(true);
0910 
0911     p_flowContainerCandidate = p_icnDocument->flowContainer(eventInfo.pos);
0912 
0913     return false;
0914 }
0915 
0916 void CMItemMove::canvasResized(const QRect & /*oldSize*/, const QRect & /*newSize*/)
0917 {
0918     // QPoint delta = oldSize.topLeft() - newSize.topLeft(); // 2017.10.01 - comment out unused variable
0919 
0920     //  scrollCanvasToSelection();
0921 
0922     //  QCursor::setPos( QCursor::pos() + delta );
0923     //  m_prevPos += delta;
0924     //  m_prevSnapPoint += delta;
0925 }
0926 
0927 void CMItemMove::scrollCanvasToSelection()
0928 {
0929     QRect bound;
0930     ItemList itemList = p_cnItemSelectList->items(false);
0931     itemList.removeAll(static_cast<Item *>(nullptr));
0932     const ItemList::iterator itemListEnd = itemList.end();
0933     for (ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it)
0934         bound |= (*it)->boundingRect();
0935 
0936     QPoint scrollToPos = m_prevPos;
0937     if (m_dx < 0) {
0938         // Scrolling left
0939         scrollToPos -= QPoint(bound.left(), 0);
0940     } else {
0941         // Scrolling right
0942         scrollToPos += QPoint(bound.right(), 0);
0943     }
0944 
0945     if (m_dy < 0) {
0946         // Scrolling up
0947         scrollToPos -= QPoint(0, bound.top());
0948     } else {
0949         // Scrolling right
0950         scrollToPos += QPoint(0, bound.bottom());
0951     }
0952 
0953     ItemView *itemView = dynamic_cast<ItemView *>(p_itemDocument->activeView());
0954     if (itemView)
0955         itemView->scrollToMouse(scrollToPos);
0956 }
0957 
0958 bool CMItemMove::mouseMoved(const EventInfo &eventInfo)
0959 {
0960     QPoint pos = eventInfo.pos;
0961 
0962     QPoint snapPoint = pos;
0963     if (m_bItemsSnapToGrid)
0964         snapPoint = this->snapPoint(snapPoint);
0965 
0966     int dx = snapPoint.x() - m_prevSnapPoint.x();
0967     int dy = snapPoint.y() - m_prevSnapPoint.y();
0968 
0969     m_dx = dx;
0970     m_dy = dy;
0971 
0972     const ItemList itemList = p_cnItemSelectList->items();
0973     const ItemList::const_iterator end = itemList.end();
0974 
0975     for (ItemList::const_iterator it = itemList.begin(); it != end; ++it) {
0976         if (!*it || !(*it)->isMovable())
0977             continue;
0978 
0979         // QRect oldRect = (*it)->boundingRect(); // 2017.10.01 - comment out unused variable
0980         (*it)->moveBy(dx, dy);
0981         // QRect newRect = (*it)->boundingRect();
0982         // QRect merged = oldRect | newRect; // 2017.10.01 - comment out unused variable
0983     }
0984 
0985     if ((dx != 0) || (dy != 0)) {
0986         const ConnectorList::iterator frEnd = m_translatableConnectors.end();
0987         for (ConnectorList::iterator it = m_translatableConnectors.begin(); it != frEnd; ++it)
0988             (*it)->translateRoute(dx, dy);
0989 
0990         const NodeGroupList::iterator end = m_translatableNodeGroups.end();
0991         for (NodeGroupList::iterator it = m_translatableNodeGroups.begin(); it != end; ++it)
0992             (*it)->translate(dx, dy);
0993     }
0994 
0995     FlowContainer *fc = p_icnDocument->flowContainer(pos);
0996     if (fc != p_flowContainerCandidate) {
0997         if (p_flowContainerCandidate) {
0998             p_flowContainerCandidate->setSelected(false);
0999             p_flowContainerCandidate = nullptr;
1000         }
1001     }
1002 
1003     if (fc) {
1004         p_flowContainerCandidate = fc;
1005         p_flowContainerCandidate->setSelected(true);
1006     }
1007 
1008     p_itemDocument->requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
1009     p_canvas->update();
1010     m_prevPos = pos;
1011     m_prevSnapPoint = snapPoint;
1012 
1013     //  scrollCanvasToSelection();
1014 
1015     return false;
1016 }
1017 
1018 bool CMItemMove::mouseReleased(const EventInfo &eventInfo)
1019 {
1020     // Is the release event from a right click (which rotates items)?
1021     if (eventInfo.isRightClick || eventInfo.isMiddleClick)
1022         return false;
1023 
1024     QStringList itemIDs;
1025 
1026     const ItemList itemList = p_cnItemSelectList->items();
1027     const ItemList::const_iterator ilEnd = itemList.end();
1028     for (ItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it) {
1029         if (*it)
1030             itemIDs.append((*it)->id());
1031     }
1032 
1033     // const QPoint pos = eventInfo.pos; // 2017.10.10 - comment out unused variable
1034 
1035     // And make sure all connectors are properly shown
1036     const ConnectorList &connectorList = p_icnDocument->connectorList();
1037     const ConnectorList::const_iterator conEnd = connectorList.end();
1038     for (ConnectorList::const_iterator it = connectorList.begin(); it != conEnd; ++it) {
1039         (*it)->setSemiHidden(false);
1040     }
1041 
1042     if (p_flowContainerCandidate) {
1043         for (ItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it)
1044             p_flowContainerCandidate->addChild(*it);
1045 
1046         p_flowContainerCandidate->setSelected(false);
1047         p_flowContainerCandidate = nullptr;
1048     } else {
1049         for (ItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it)
1050             (*it)->setParentItem(nullptr);
1051     }
1052 
1053     // And disable the FlowContainers again...
1054     const ItemList &cnItemList = p_icnDocument->itemList();
1055     const ItemList::const_iterator end = cnItemList.end();
1056     for (ItemList::const_iterator it = cnItemList.begin(); it != end; ++it) {
1057         if (FlowContainer *flowContainer = dynamic_cast<FlowContainer *>(static_cast<Item *>(*it)))
1058             flowContainer->setFullBounds(false);
1059     }
1060 
1061     if (p_icnDocument)
1062         p_icnDocument->requestRerouteInvalidatedConnectors();
1063 
1064     if (m_eventInfo.pos != eventInfo.pos)
1065         p_itemDocument->requestStateSave();
1066 
1067     p_itemDocument->requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
1068 
1069     return true;
1070 }
1071 
1072 bool CMItemMove::mousePressedRepeat(const EventInfo &info)
1073 {
1074     if (info.isRightClick)
1075         p_cnItemSelectList->slotRotateCW();
1076     else if (info.isMiddleClick)
1077         p_cnItemSelectList->flipHorizontally();
1078 
1079     return false;
1080 }
1081 // END class CMItemMove
1082 
1083 CMItemResize::CMItemResize(ItemDocument *itemDocument, CMManager *cmManager)
1084     : CanvasManipulator(itemDocument, cmManager)
1085 {
1086 }
1087 
1088 CMItemResize::~CMItemResize()
1089 {
1090 }
1091 
1092 CanvasManipulator *CMItemResize::construct(ItemDocument *itemDocument, CMManager *cmManager)
1093 {
1094     return new CMItemResize(itemDocument, cmManager);
1095 }
1096 
1097 ManipulatorInfo *CMItemResize::manipulatorInfo()
1098 {
1099     ManipulatorInfo *eventInfo = new ManipulatorInfo();
1100     //  eventInfo->m_itemType.m_activate = CMManager::it_canvas_item;
1101     eventInfo->m_acceptManipulationPtr = CMItemResize::acceptManipulation;
1102     eventInfo->m_createManipulatorPtr = CMItemResize::construct;
1103     return eventInfo;
1104 }
1105 
1106 bool CMItemResize::acceptManipulation(uint eventState, uint /*cmState*/, uint itemType, uint /*cnItemType*/)
1107 {
1108     return (itemType & CMManager::it_resize_handle) && !(eventState & CMManager::es_right_click);
1109 }
1110 
1111 bool CMItemResize::mousePressedInitial(const EventInfo &eventInfo)
1112 {
1113     m_eventInfo = eventInfo;
1114     p_resizeHandle = dynamic_cast<ResizeHandle *>(eventInfo.qcanvasItemClickedOn);
1115     m_rh_dx = p_resizeHandle->x() - eventInfo.pos.x();
1116     m_rh_dy = p_resizeHandle->y() - eventInfo.pos.y();
1117     return false;
1118 }
1119 
1120 bool CMItemResize::mouseMoved(const EventInfo &eventInfo)
1121 {
1122     int _x = int(m_rh_dx + eventInfo.pos.x());
1123     int _y = int(m_rh_dy + eventInfo.pos.y());
1124 
1125     // Shift pressed == snap to grid
1126     if (eventInfo.shiftPressed) {
1127         _x = snapToCanvas(_x);
1128         _y = snapToCanvas(_y);
1129     }
1130 
1131     p_resizeHandle->moveRH(_x, _y);
1132     return false;
1133 }
1134 
1135 bool CMItemResize::mouseReleased(const EventInfo & /*eventInfo*/)
1136 {
1137     if (p_icnDocument)
1138         p_icnDocument->requestRerouteInvalidatedConnectors();
1139     p_itemDocument->requestStateSave();
1140     return true;
1141 }
1142 
1143 CMMechItemMove::CMMechItemMove(ItemDocument *itemDocument, CMManager *cmManager)
1144     : CanvasManipulator(itemDocument, cmManager)
1145 {
1146 }
1147 
1148 CMMechItemMove::~CMMechItemMove()
1149 {
1150 }
1151 
1152 CanvasManipulator *CMMechItemMove::construct(ItemDocument *itemDocument, CMManager *cmManager)
1153 {
1154     return new CMMechItemMove(itemDocument, cmManager);
1155 }
1156 
1157 ManipulatorInfo *CMMechItemMove::manipulatorInfo()
1158 {
1159     ManipulatorInfo *eventInfo = new ManipulatorInfo();
1160     //  eventInfo->m_itemType.m_activate = CMManager::it_canvas_item;
1161     eventInfo->m_acceptManipulationPtr = CMMechItemMove::acceptManipulation;
1162     eventInfo->m_createManipulatorPtr = CMMechItemMove::construct;
1163     return eventInfo;
1164 }
1165 
1166 bool CMMechItemMove::acceptManipulation(uint eventState, uint /*cmState*/, uint itemType, uint /*cnItemType*/)
1167 {
1168     return ((itemType & CMManager::it_mechanics_item) || (itemType & CMManager::it_drawpart)) && !(eventState & CMManager::es_right_click);
1169 }
1170 
1171 bool CMMechItemMove::mousePressedInitial(const EventInfo &eventInfo)
1172 {
1173     m_eventInfo = eventInfo;
1174     m_prevPos = eventInfo.pos;
1175 
1176     Item *item = dynamic_cast<Item *>(eventInfo.qcanvasItemClickedOn);
1177     if (!item)
1178         return true;
1179 
1180     MechanicsItem *mechItem = dynamic_cast<MechanicsItem *>(eventInfo.qcanvasItemClickedOn);
1181 
1182     if (mechItem)
1183         m_prevClickedOnSM = mechItem->selectionMode();
1184 
1185     if (eventInfo.shiftPressed) {
1186         p_mechanicsDocument->unselectAll();
1187         p_mechanicsDocument->select(item);
1188         if (mechItem) {
1189             mechItem->setSelectionMode(MechanicsItem::sm_move);
1190             mechItem->setParentItem(nullptr);
1191         }
1192     } else if (!p_selectList->contains(mechItem)) {
1193         if (!eventInfo.ctrlPressed)
1194             p_mechanicsDocument->unselectAll();
1195 
1196         p_mechanicsDocument->select(item);
1197 
1198         if (mechItem)
1199             mechItem->setSelectionMode(MechanicsItem::sm_move);
1200     } else {
1201         if (mechItem)
1202             mechItem->setSelectionMode(MechanicsItem::sm_move);
1203 
1204         if (m_eventInfo.ctrlPressed)
1205             p_mechanicsDocument->unselect(item);
1206     }
1207 
1208     if (p_selectList->isEmpty())
1209         return true;
1210 
1211     p_mechItemSelectList->setSelectionMode(MechanicsItem::sm_move);
1212     p_mechItemSelectList->setRaised(true);
1213     return false;
1214 }
1215 
1216 bool CMMechItemMove::mouseMoved(const EventInfo &eventInfo)
1217 {
1218     const QPoint pos = eventInfo.pos;
1219 
1220     int x = pos.x();
1221     int y = pos.y();
1222 
1223     const MechItemList itemList = p_mechItemSelectList->toplevelMechItemList();
1224     const MechItemList::const_iterator ilEnd = itemList.end();
1225     for (MechItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it) {
1226         if (*it)
1227             (*it)->moveBy(x - m_prevPos.x(), y - m_prevPos.y());
1228     }
1229 
1230     m_prevPos = QPoint(x, y);
1231 
1232     p_canvas->update();
1233     p_itemDocument->requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
1234     return false;
1235 }
1236 
1237 bool CMMechItemMove::mouseReleased(const EventInfo &eventInfo)
1238 {
1239     const QPoint pos = eventInfo.pos;
1240 
1241     int dx = pos.x() - m_eventInfo.pos.x();
1242     int dy = pos.y() - m_eventInfo.pos.y();
1243 
1244     p_mechItemSelectList->setRaised(false);
1245 
1246     MechanicsItem *mechItem = dynamic_cast<MechanicsItem *>(m_eventInfo.qcanvasItemClickedOn);
1247     if (dx == 0 && dy == 0) {
1248         if (mechItem && mechItem->isSelected()) {
1249             if (m_prevClickedOnSM == MechanicsItem::sm_resize)
1250                 mechItem->setSelectionMode(MechanicsItem::sm_rotate);
1251             else
1252                 mechItem->setSelectionMode(MechanicsItem::sm_resize);
1253         }
1254         p_itemDocument->requestStateSave();
1255         return true;
1256     }
1257 
1258     if (mechItem && mechItem->isSelected()) {
1259         if (m_prevClickedOnSM == MechanicsItem::sm_rotate)
1260             mechItem->setSelectionMode(MechanicsItem::sm_rotate);
1261         else
1262             mechItem->setSelectionMode(MechanicsItem::sm_resize);
1263     }
1264 
1265     QStringList itemIDs;
1266 
1267     ItemList itemList = p_mechItemSelectList->items();
1268     const ItemList::iterator ilEnd = itemList.end();
1269     for (ItemList::iterator it = itemList.begin(); it != ilEnd; ++it) {
1270         if (*it) {
1271             itemIDs.append((*it)->id());
1272         }
1273     }
1274 
1275     p_mechItemSelectList->setSelectionMode(MechanicsItem::sm_resize);
1276     p_itemDocument->requestStateSave();
1277     p_itemDocument->requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
1278     return true;
1279 }
1280 
1281 // BEGIN class SelectRectangle
1282 SelectRectangle::SelectRectangle(int x, int y, int w, int h, KtlQCanvas *qcanvas)
1283     : m_x(x)
1284     , m_y(y)
1285 {
1286     m_topLine = new KtlQCanvasLine(qcanvas);
1287     m_rightLine = new KtlQCanvasLine(qcanvas);
1288     m_bottomLine = new KtlQCanvasLine(qcanvas);
1289     m_leftLine = new KtlQCanvasLine(qcanvas);
1290     setSize(w, h);
1291 
1292     KtlQCanvasLine *lines[] = {m_topLine, m_rightLine, m_bottomLine, m_leftLine};
1293     for (int i = 0; i < 4; ++i) {
1294         lines[i]->setPen(QPen(QColor(190, 190, 190), 1, Qt::DotLine));
1295         lines[i]->setZ(ICNDocument::Z::Select);
1296         lines[i]->show();
1297     }
1298 }
1299 
1300 SelectRectangle::~SelectRectangle()
1301 {
1302     delete m_topLine;
1303     delete m_rightLine;
1304     delete m_bottomLine;
1305     delete m_leftLine;
1306 }
1307 
1308 void SelectRectangle::setSize(int w, int h)
1309 {
1310     m_topLine->setPoints(m_x, m_y, m_x + w, m_y);
1311     m_rightLine->setPoints(m_x + w, m_y, m_x + w, m_y + h);
1312     m_bottomLine->setPoints(m_x + w, m_y + h, m_x, m_y + h);
1313     m_leftLine->setPoints(m_x, m_y + h, m_x, m_y);
1314     m_w = w;
1315     m_h = h;
1316 }
1317 
1318 KtlQCanvasItemList SelectRectangle::collisions()
1319 {
1320     KtlQCanvas *canvas = m_topLine->canvas();
1321 
1322     return canvas->collisions(QRect(m_x, m_y, m_w, m_h));
1323 }
1324 // END class SelectRectangle
1325 
1326 // BEGIN class CMSelect
1327 CMSelect::CMSelect(ItemDocument *itemDocument, CMManager *cmManager)
1328     : CanvasManipulator(itemDocument, cmManager)
1329 {
1330     m_selectRectangle = nullptr;
1331 }
1332 
1333 CMSelect::~CMSelect()
1334 {
1335     delete m_selectRectangle;
1336 }
1337 
1338 CanvasManipulator *CMSelect::construct(ItemDocument *itemDocument, CMManager *cmManager)
1339 {
1340     return new CMSelect(itemDocument, cmManager);
1341 }
1342 
1343 ManipulatorInfo *CMSelect::manipulatorInfo()
1344 {
1345     ManipulatorInfo *eventInfo = new ManipulatorInfo();
1346     //  eventInfo->m_itemType.m_activate = CMManager::it_none;
1347     eventInfo->m_acceptManipulationPtr = CMSelect::acceptManipulation;
1348     eventInfo->m_createManipulatorPtr = CMSelect::construct;
1349     return eventInfo;
1350 }
1351 
1352 bool CMSelect::acceptManipulation(uint /*eventState*/, uint /*cmState*/, uint itemType, uint /*cnItemType*/)
1353 {
1354     return (itemType & CMManager::it_none);
1355 }
1356 
1357 bool CMSelect::mousePressedInitial(const EventInfo &eventInfo)
1358 {
1359     m_eventInfo = eventInfo;
1360 
1361     if (!eventInfo.ctrlPressed) {
1362         p_itemDocument->unselectAll();
1363     }
1364 
1365     m_selectRectangle = new SelectRectangle(eventInfo.pos.x(), eventInfo.pos.y(), 0, 0, p_canvas);
1366     return false;
1367 }
1368 
1369 bool CMSelect::mouseMoved(const EventInfo &eventInfo)
1370 {
1371     QPoint pos = eventInfo.pos;
1372 
1373     m_selectRectangle->setSize(pos.x() - m_eventInfo.pos.x(), pos.y() - m_eventInfo.pos.y());
1374 
1375     if (m_eventInfo.ctrlPressed) {
1376         p_itemDocument->select(m_selectRectangle->collisions());
1377     } else if (p_selectList) {
1378         p_selectList->setItems(m_selectRectangle->collisions());
1379     }
1380 
1381     if (p_selectList && !p_mechanicsDocument) {
1382         p_selectList->setSelected(true);
1383     }
1384     return false;
1385 }
1386 
1387 bool CMSelect::mouseReleased(const EventInfo & /*eventInfo*/)
1388 {
1389     delete m_selectRectangle;
1390     m_selectRectangle = nullptr;
1391 
1392     return true;
1393 }
1394 // END class CMSelect
1395 
1396 CMItemDrag::CMItemDrag(ItemDocument *itemDocument, CMManager *cmManager)
1397     : CanvasManipulator(itemDocument, cmManager)
1398 {
1399     b_dragged = false;
1400 }
1401 
1402 CMItemDrag::~CMItemDrag()
1403 {
1404 }
1405 
1406 CanvasManipulator *CMItemDrag::construct(ItemDocument *itemDocument, CMManager *cmManager)
1407 {
1408     return new CMItemDrag(itemDocument, cmManager);
1409 }
1410 
1411 ManipulatorInfo *CMItemDrag::manipulatorInfo()
1412 {
1413     ManipulatorInfo *eventInfo = new ManipulatorInfo();
1414     //  eventInfo->m_itemType.m_activate = CMManager::it_canvas_item;
1415     eventInfo->m_acceptManipulationPtr = CMItemDrag::acceptManipulation;
1416     eventInfo->m_createManipulatorPtr = CMItemDrag::construct;
1417     return eventInfo;
1418 }
1419 
1420 bool CMItemDrag::acceptManipulation(uint /*eventState*/, uint /*cmState*/, uint itemType, uint cnItemType)
1421 {
1422     return (itemType & (CMManager::it_canvas_item | CMManager::it_pin)) && !(cnItemType & CMManager::isi_isMovable);
1423 }
1424 
1425 bool CMItemDrag::mousePressedInitial(const EventInfo &eventInfo)
1426 {
1427     m_eventInfo = eventInfo;
1428     b_dragged = false;
1429     return false;
1430 }
1431 
1432 bool CMItemDrag::mouseMoved(const EventInfo &eventInfo)
1433 {
1434     const QPoint pos = eventInfo.pos;
1435 
1436     if (b_dragged || pos.x() > (m_eventInfo.pos.x() + 4) || pos.x() < (m_eventInfo.pos.x() - 4) || pos.y() > (m_eventInfo.pos.y() + 4) || pos.y() < (m_eventInfo.pos.y() - 4)) {
1437         b_dragged = true;
1438 
1439         if (PinItem *pi = dynamic_cast<PinItem *>(m_eventInfo.qcanvasItemClickedOn))
1440             pi->dragged(pos.x() - m_eventInfo.pos.x());
1441     }
1442     return false;
1443 }
1444 
1445 bool CMItemDrag::mouseReleased(const EventInfo & /*eventInfo*/)
1446 {
1447     if (!b_dragged) {
1448         if (PinItem *pi = dynamic_cast<PinItem *>(m_eventInfo.qcanvasItemClickedOn))
1449             pi->switchState();
1450     }
1451 
1452     p_itemDocument->requestStateSave();
1453     return true;
1454 }
1455 
1456 // BEGIN class CanvasEllipseDraw
1457 CanvasEllipseDraw::CanvasEllipseDraw(int x, int y, KtlQCanvas *canvas)
1458     : KtlQCanvasEllipse(0, 0, canvas)
1459 {
1460     move(x, y);
1461 }
1462 
1463 void CanvasEllipseDraw::drawShape(QPainter &p)
1464 {
1465     p.drawEllipse(int(x() - width() / 2), int(y() - height() / 2), width(), height());
1466 }
1467 // END class CanvasEllipseDraw
1468 
1469 // BEGIN class CMDraw
1470 CMDraw::CMDraw(ItemDocument *itemDocument, CMManager *cmManager)
1471     : CanvasManipulator(itemDocument, cmManager)
1472 {
1473     m_pDrawLine = nullptr;
1474     m_pDrawRectangle = nullptr;
1475     m_pDrawEllipse = nullptr;
1476 }
1477 
1478 CMDraw::~CMDraw()
1479 {
1480     p_cmManager->setDrawAction(-1);
1481 }
1482 
1483 CanvasManipulator *CMDraw::construct(ItemDocument *itemDocument, CMManager *cmManager)
1484 {
1485     return new CMDraw(itemDocument, cmManager);
1486 }
1487 
1488 ManipulatorInfo *CMDraw::manipulatorInfo()
1489 {
1490     ManipulatorInfo *eventInfo = new ManipulatorInfo();
1491     eventInfo->m_acceptManipulationPtr = CMDraw::acceptManipulation;
1492     eventInfo->m_createManipulatorPtr = CMDraw::construct;
1493     return eventInfo;
1494 }
1495 
1496 bool CMDraw::acceptManipulation(uint /*eventState*/, uint cmState, uint /*itemType*/, uint /*cnItemType*/)
1497 {
1498     return (cmState & CMManager::cms_draw);
1499 }
1500 
1501 bool CMDraw::mousePressedInitial(const EventInfo &eventInfo)
1502 {
1503     m_eventInfo = eventInfo;
1504 
1505     switch (static_cast<DrawPart::DrawAction>(p_cmManager->drawAction())) {
1506     case DrawPart::da_text:
1507     case DrawPart::da_rectangle:
1508     case DrawPart::da_image: {
1509         m_pDrawRectangle = new KtlQCanvasRectangle(eventInfo.pos.x(), eventInfo.pos.y(), 0, 0, p_canvas);
1510         m_pDrawRectangle->setPen(QPen(QColor(0, 0, 0), 1));
1511         m_pDrawRectangle->setZ(ICNDocument::Z::ConnectorCreateLine);
1512         m_pDrawRectangle->show();
1513         break;
1514     }
1515     case DrawPart::da_ellipse: {
1516         m_pDrawEllipse = new CanvasEllipseDraw(eventInfo.pos.x(), eventInfo.pos.y(), p_canvas);
1517         m_pDrawEllipse->setPen(QPen(QColor(0, 0, 0), 1));
1518         m_pDrawEllipse->setZ(ICNDocument::Z::ConnectorCreateLine);
1519         m_pDrawEllipse->show();
1520         break;
1521     }
1522     case DrawPart::da_line:
1523     case DrawPart::da_arrow: {
1524         m_pDrawLine = new KtlQCanvasLine(p_canvas);
1525         m_pDrawLine->setPoints(eventInfo.pos.x(), eventInfo.pos.y(), eventInfo.pos.x(), eventInfo.pos.y());
1526         m_pDrawLine->setPen(QPen(QColor(0, 0, 0), 1));
1527         m_pDrawLine->setZ(ICNDocument::Z::ConnectorCreateLine);
1528         m_pDrawLine->show();
1529         break;
1530     }
1531     default:
1532         return true;
1533     }
1534 
1535     return false;
1536 }
1537 
1538 bool CMDraw::mouseMoved(const EventInfo &eventInfo)
1539 {
1540     const QPoint pos = eventInfo.pos;
1541 
1542     if (m_pDrawRectangle)
1543         m_pDrawRectangle->setSize(pos.x() - m_eventInfo.pos.x(), pos.y() - m_eventInfo.pos.y());
1544 
1545     else if (m_pDrawEllipse) {
1546         //      QRect r( m_eventInfo.pos.x(), m_eventInfo.pos.y(), pos.x()-m_eventInfo.pos.x(), pos.y()-m_eventInfo.pos.y() );
1547         //      r = r.normalized();
1548         //
1549         //      m_pDrawEllipse->setSize( r.width(), r.height() );
1550         //      m_pDrawEllipse->move( r.left()+(r.width()/2), r.top()+(r.height()/2) );
1551 
1552         m_pDrawEllipse->setSize(2 * abs(pos.x() - m_eventInfo.pos.x()), 2 * abs(pos.y() - m_eventInfo.pos.y()));
1553     }
1554 
1555     else if (m_pDrawLine)
1556         m_pDrawLine->setPoints(eventInfo.pos.x(), eventInfo.pos.y(), m_pDrawLine->endPoint().x(), m_pDrawLine->endPoint().y());
1557     else
1558         return true;
1559 
1560     return false;
1561 }
1562 
1563 bool CMDraw::mouseReleased(const EventInfo & /*eventInfo*/)
1564 {
1565     // const QPoint pos = eventInfo.pos; // 2017.10.01 - comment out unused variable
1566 
1567     QRect sizeRect;
1568 
1569     if (m_pDrawRectangle || m_pDrawEllipse) {
1570         if (m_pDrawRectangle) {
1571             sizeRect = m_pDrawRectangle->rect();
1572 
1573             // We have to manually adjust the size rect so that it matches up with what the user has drawn
1574 
1575             sizeRect.setWidth(sizeRect.width() + 1);
1576             sizeRect.setHeight(sizeRect.height() + 1);
1577 
1578             sizeRect = sizeRect.normalized();
1579 
1580             if (m_pDrawRectangle->rect().width() < 0)
1581                 sizeRect.moveLeft(sizeRect.left() + 1);
1582 
1583             if (m_pDrawRectangle->rect().height() < 0)
1584                 sizeRect.moveTop(sizeRect.top() + 1);
1585         } else {
1586             int w = m_pDrawEllipse->width() + 1;
1587             int h = m_pDrawEllipse->height() + 1;
1588             int x = int(m_pDrawEllipse->x() - w / 2);
1589             int y = int(m_pDrawEllipse->y() - h / 2);
1590             sizeRect = QRect(x, y, w, h).normalized();
1591         }
1592 
1593         delete m_pDrawRectangle;
1594         delete m_pDrawEllipse;
1595         m_pDrawRectangle = nullptr;
1596         m_pDrawEllipse = nullptr;
1597     } else if (m_pDrawLine) {
1598         int sx = m_pDrawLine->startPoint().x();
1599         int sy = m_pDrawLine->startPoint().y();
1600         int ex = m_pDrawLine->endPoint().x();
1601         int ey = m_pDrawLine->endPoint().y();
1602 
1603         sizeRect = QRect(ex, ey, sx - ex, sy - ey);
1604 
1605         delete m_pDrawLine;
1606         m_pDrawLine = nullptr;
1607     } else
1608         return true;
1609 
1610     QString id;
1611     switch (static_cast<DrawPart::DrawAction>(p_cmManager->drawAction())) {
1612     case DrawPart::da_rectangle:
1613         id = "dp/rectangle";
1614         break;
1615 
1616     case DrawPart::da_image:
1617         id = "dp/image";
1618         break;
1619 
1620     case DrawPart::da_ellipse:
1621         id = "dp/ellipse";
1622         break;
1623 
1624     case DrawPart::da_text:
1625         id = "dp/canvas_text";
1626 
1627         if (sizeRect.width() < 56)
1628             sizeRect.setWidth(56);
1629 
1630         if (sizeRect.height() < 24)
1631             sizeRect.setHeight(24);
1632 
1633         break;
1634 
1635     case DrawPart::da_line:
1636         id = "dp/line";
1637         break;
1638 
1639     case DrawPart::da_arrow:
1640         id = "dp/arrow";
1641         break;
1642     }
1643 
1644     if (id.isEmpty())
1645         return true;
1646 
1647     Item *item = p_itemDocument->addItem(id, sizeRect.topLeft(), true);
1648 
1649     if (!item)
1650         return true;
1651 
1652     item->move(sizeRect.x(), sizeRect.y()); // We call this again as p_itemDocument->addItem will move the item if it is slightly off the canvas.
1653 
1654     item->setSize(0, 0, sizeRect.width(), sizeRect.height());
1655 
1656     p_itemDocument->requestStateSave();
1657     return true;
1658 }
1659 // END class CMDraw
1660 
1661 // BEGIN class ManualConnectorDraw
1662 ManualConnectorDraw::ManualConnectorDraw(ICNDocument *_icnDocument, const QPoint &initialPos)
1663 {
1664     m_color = Qt::black;
1665 
1666     icnDocument = _icnDocument;
1667     m_currentPos = m_previousPos = m_initialPos = initialPos;
1668     p_initialItem = icnDocument->itemAtTop(initialPos);
1669 
1670     b_currentVertical = false;
1671     b_orientationDefined = false;
1672 
1673     m_connectorLines.append(m_previousCon = new KtlQCanvasLine(icnDocument->canvas()));
1674     m_connectorLines.append(m_currentCon = new KtlQCanvasLine(icnDocument->canvas()));
1675 
1676     m_currentCon->setPoints(initialPos.x(), initialPos.y(), initialPos.x(), initialPos.y());
1677     m_previousCon->setPoints(initialPos.x(), initialPos.y(), initialPos.x(), initialPos.y());
1678 
1679     m_currentCon->setPen(m_color);
1680     m_previousCon->setPen(m_color);
1681 
1682     updateConnectorEnds();
1683 
1684     m_currentCon->show();
1685     m_previousCon->show();
1686 }
1687 
1688 ManualConnectorDraw::~ManualConnectorDraw()
1689 {
1690     qDeleteAll(m_connectorLines);
1691     m_connectorLines.clear();
1692 }
1693 
1694 void ManualConnectorDraw::setColor(const QColor &color)
1695 {
1696     m_color = color;
1697 
1698     const QList<KtlQCanvasLine *>::iterator end = m_connectorLines.end();
1699     for (QList<KtlQCanvasLine *>::iterator it = m_connectorLines.begin(); it != end; ++it)
1700         (*it)->setPen(m_color);
1701 }
1702 
1703 void ManualConnectorDraw::mouseMoved(const QPoint &pos)
1704 {
1705     if (m_currentPos == pos)
1706         return;
1707 
1708     if (!b_orientationDefined) {
1709         QPoint previousStart = m_previousCon->startPoint();
1710 
1711         double distance = std::sqrt(std::pow(double(m_currentPos.x() - previousStart.x()), 2.) + std::pow(double(m_currentPos.y() - previousStart.y()), 2.));
1712 
1713         if (distance < 24) {
1714             b_currentVertical = (std::abs(double(m_currentPos.x() - previousStart.x())) >= std::abs(double(m_currentPos.y() - previousStart.y())));
1715         }
1716     }
1717 
1718     m_previousPos = m_currentPos;
1719     m_currentPos = pos;
1720     updateConnectorEnds();
1721 }
1722 
1723 KtlQCanvasItem *ManualConnectorDraw::mouseClicked(const QPoint &pos)
1724 {
1725     if (b_orientationDefined)
1726         b_currentVertical = !b_currentVertical;
1727     else
1728         mouseMoved(pos);
1729 
1730     b_orientationDefined = true;
1731 
1732     m_currentPos = pos;
1733 
1734     KtlQCanvasItem *qcanvasItem = icnDocument->itemAtTop(pos);
1735 
1736     if (qcanvasItem && pos != m_initialPos && qcanvasItem != p_initialItem)
1737         return qcanvasItem;
1738 
1739     m_previousCon = m_currentCon;
1740 
1741     m_connectorLines.append(m_currentCon = new KtlQCanvasLine(icnDocument->canvas()));
1742     m_currentCon->setPoints(pos.x(), pos.y(), pos.x(), pos.y());
1743     m_currentCon->setPen(m_color);
1744     updateConnectorEnds();
1745     m_currentCon->show();
1746 
1747     return nullptr;
1748 }
1749 
1750 void ManualConnectorDraw::updateConnectorEnds()
1751 {
1752     QPoint pivot = m_currentPos;
1753     QPoint previousStart = m_previousCon->startPoint();
1754 
1755     if (b_currentVertical) {
1756         pivot.setY(previousStart.y());
1757         m_currentCon->setPoints(pivot.x(), pivot.y(), pivot.x(), m_currentPos.y());
1758     } else {
1759         pivot.setX(previousStart.x());
1760         m_currentCon->setPoints(pivot.x(), pivot.y(), m_currentPos.x(), pivot.y());
1761     }
1762 
1763     m_previousCon->setPoints(previousStart.x(), previousStart.y(), pivot.x(), pivot.y());
1764 }
1765 
1766 QPointList ManualConnectorDraw::pointList()
1767 {
1768     QPointList list;
1769     list.append(m_initialPos);
1770 
1771     const QList<KtlQCanvasLine *>::iterator end = m_connectorLines.end();
1772     for (QList<KtlQCanvasLine *>::iterator it = m_connectorLines.begin(); it != end; ++it) {
1773         list.append((*it)->endPoint());
1774     }
1775 
1776     return list;
1777 }
1778 // END class ManualConnectorDraw
1779 
1780 // BEGIN class ManipulatorInfo
1781 ManipulatorInfo::ManipulatorInfo()
1782 {
1783 }
1784 // END class ManipulatorInfo
1785 
1786 #include "moc_canvasmanipulator.cpp"