canvasmanipulator.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2004-2005 by David Saxton                               *
00003  *   david@bluehaze.org                                                    *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  ***************************************************************************/
00010 
00011 #include "cnitemgroup.h"
00012 #include "canvasitemparts.h"
00013 #include "dptext.h"
00014 #include "canvasmanipulator.h"
00015 #include "connector.h"
00016 #include "flowcontainer.h"
00017 #include "icndocument.h"
00018 #include "itemview.h"
00019 #include "mechanicsdocument.h"
00020 #include "mechanicsgroup.h"
00021 #include "mechanicsitem.h"
00022 #include "node.h"
00023 #include "nodegroup.h"
00024 #include "picitem.h"
00025 #include "resizeoverlay.h"
00026 
00027 #include <cmath>
00028 
00029 #include <kconfig.h>
00030 #include <kdebug.h>
00031 #include <kglobal.h>
00032 
00033 #include <qpainter.h>
00034 #include <qtimer.h>
00035 
00036 /* TODO AUDIT CODE
00037 Formatting was very ugly. Probably buggy and/or poorly factored. 
00038 */ 
00039 
00040 
00041 CMManager::CMManager( ItemDocument *itemDocument )
00042         : QObject()
00043 {
00044         b_allowItemScroll = true;
00045         p_lastMouseOverResizeHandle = 0;
00046         m_canvasManipulator = 0;
00047         p_itemDocument = itemDocument;
00048         m_cmState = 0;
00049         p_lastMouseOverItem = 0;
00050         p_lastItemClicked = 0;
00051         m_drawAction = -1;
00052         m_allowItemScrollTmr = new QTimer(this);
00053         connect( m_allowItemScrollTmr, SIGNAL(timeout()), this, SLOT(slotAllowItemScroll()) );
00054 
00055         KGlobal::config()->setGroup("General");
00056         slotSetManualRoute( KGlobal::config()->readBoolEntry( "ManualRouting", false));
00057 }
00058 
00059 CMManager::~CMManager()
00060 {
00061         delete m_allowItemScrollTmr;
00062         delete m_canvasManipulator;
00063 
00064         const ManipulatorInfoList::iterator end = m_manipulatorInfoList.end();
00065         for ( ManipulatorInfoList::iterator it = m_manipulatorInfoList.begin(); it != end; ++it ) {
00066                 delete *it;
00067         }
00068 
00069         m_manipulatorInfoList.clear();
00070 }
00071 
00072 void CMManager::addManipulatorInfo( ManipulatorInfo *eventInfo )
00073 {
00074         if ( eventInfo && !m_manipulatorInfoList.contains(eventInfo) ) {
00075                 m_manipulatorInfoList.prepend(eventInfo);
00076         }
00077 }
00078 
00079 void CMManager::cancelCurrentManipulation()
00080 {
00081         delete m_canvasManipulator;
00082         m_canvasManipulator = 0;
00083         setRepeatedAddId();
00084 }
00085 
00086 void CMManager::mousePressEvent( EventInfo eventInfo )
00087 {
00088         if (m_canvasManipulator) {
00089                 if (m_canvasManipulator->mousePressedRepeat(eventInfo)) {
00090                         delete m_canvasManipulator;
00091                         m_canvasManipulator = 0;
00092                 }
00093                 return;
00094         }
00095 
00096         uint eventState=0;
00097         if(eventInfo.isRightClick)
00098                 eventState |= CMManager::es_right_click;
00099 
00100         if(eventInfo.ctrlPressed)
00101                 eventState |= CMManager::es_ctrl_pressed;
00102 
00103         uint itemType=0;
00104         uint cnItemType=0;
00105 
00106         switch(eventInfo.itemRtti) {
00107                 case ItemDocument::RTTI::None: itemType = CMManager::it_none; break;
00108                 case ItemDocument::RTTI::Node: itemType = CMManager::it_node; break;
00109                 case ItemDocument::RTTI::Connector: itemType = CMManager::it_connector; break;
00110                 case ItemDocument::RTTI::Pin: itemType = CMManager::it_pin; break;
00111                 case ItemDocument::RTTI::ResizeHandle: itemType = CMManager::it_resize_handle; break;
00112                 case ItemDocument::RTTI::DrawPart:
00113                 {
00114                         itemType = CMManager::it_drawpart;
00115                         DrawPart *drawPartClickedOn = dynamic_cast<DrawPart*>(eventInfo.qcanvasItemClickedOn);
00116                         
00117                         if ( drawPartClickedOn->mousePressEvent(eventInfo) ) {
00118                                 p_lastItemClicked = drawPartClickedOn;
00119                                 return;
00120                         }
00121                         
00122                         if ( drawPartClickedOn->isMovable() )
00123                                 cnItemType |= CMManager::isi_isMovable;
00124                         break;
00125                 }
00126                 case ItemDocument::RTTI::Widget:
00127                 {
00128                         Widget *widget = dynamic_cast<Widget*>(eventInfo.qcanvasItemClickedOn);
00129                         if (widget)
00130                                 eventInfo.qcanvasItemClickedOn = widget->parent();
00131                 }
00132                 case ItemDocument::RTTI::CNItem:
00133                 {
00134                         itemType = CMManager::it_canvas_item;
00135                         CNItem *cnItemClickedOn = dynamic_cast<CNItem*>(eventInfo.qcanvasItemClickedOn);
00136 
00137                         if(cnItemClickedOn->mousePressEvent(eventInfo) )
00138                         {
00139                                 p_lastItemClicked = cnItemClickedOn;
00140                                 return;
00141                         }
00142                         
00143                         if(cnItemClickedOn->isMovable() )
00144                                 cnItemType |= CMManager::isi_isMovable;
00145                         break;
00146                 }
00147                 case ItemDocument::RTTI::MechanicsItem:
00148                 {
00149                         itemType = CMManager::it_mechanics_item;
00150                         MechanicsItem *p_mechanicsItemClickedOn = dynamic_cast<MechanicsItem*>(eventInfo.qcanvasItemClickedOn);
00151                         
00152                         if ( p_mechanicsItemClickedOn->mousePressEvent(eventInfo) )
00153                         {
00154                                 p_lastItemClicked = p_mechanicsItemClickedOn;
00155                                 return;
00156                         }
00157                         break;
00158                 }
00159         }
00160         
00161 //      uint highestScore=0;
00162 //      ManipulatorInfo *best=0;
00163         const ManipulatorInfoList::iterator end = m_manipulatorInfoList.end();
00164 
00165         for ( ManipulatorInfoList::iterator it = m_manipulatorInfoList.begin(); it != end && !m_canvasManipulator; ++it ) {
00166                 if ( (*it)->m_acceptManipulationPtr( eventState, m_cmState, itemType, cnItemType ) ) {
00167                         m_canvasManipulator = (*it)->m_createManipulatorPtr( p_itemDocument, this );
00168                 }
00169         }
00170 
00171         if (m_canvasManipulator) {
00172                 if (m_canvasManipulator->mousePressedInitial(eventInfo)) {
00173                         delete m_canvasManipulator;
00174                         m_canvasManipulator = 0;
00175                 }
00176         }
00177 }
00178 
00179 void CMManager::mouseDoubleClickEvent( const EventInfo &eventInfo )
00180 {
00181         Item *item = dynamic_cast<Item*>(eventInfo.qcanvasItemClickedOn);
00182         if (item) {
00183                 item->mouseDoubleClickEvent(eventInfo);
00184                 return;
00185         }
00186 
00187         Widget *widget = dynamic_cast<Widget*>(eventInfo.qcanvasItemClickedOn);
00188         if (widget) {
00189                 widget->parent()->mouseDoubleClickEvent(eventInfo);
00190                 return;
00191         }
00192 }
00193 
00194 void CMManager::mouseMoveEvent( const EventInfo &eventInfo )
00195 {
00196         if (m_canvasManipulator)
00197         {
00198                 if (m_canvasManipulator->mouseMoved(eventInfo))
00199                 {
00200                         kdDebug() << k_funcinfo << "About to delete" << endl;
00201                         delete m_canvasManipulator;
00202                         kdDebug() << k_funcinfo << "Deleted" << endl;
00203                         m_canvasManipulator = 0;
00204                         kdDebug() << k_funcinfo << "Nullified" << endl;
00205                 }
00206                 ItemView *itemView = dynamic_cast<ItemView*>(p_itemDocument->activeView());
00207 
00208                 if (itemView) itemView->scrollToMouse(eventInfo.pos);
00209                 return;
00210         }
00211         
00212         //BEGIN
00213         QCanvasItem *qcnItem = p_itemDocument->itemAtTop(eventInfo.pos);
00214         Item *item;
00215         Widget *widget = dynamic_cast<Widget*>(qcnItem);
00216         if(widget) item = widget->parent();
00217         else item = dynamic_cast<Item*>(qcnItem);
00218         
00219         if ( p_lastMouseOverItem != (QGuardedPtr<Item>)item ) {
00220                 QEvent event(QEvent::Leave);
00221                 
00222                 if (p_lastMouseOverItem) p_lastMouseOverItem->leaveEvent();
00223 
00224                 if (item) item->enterEvent();
00225 
00226                 p_lastMouseOverItem = item;
00227         }
00228         
00229         // If we clicked on an item, then continue to pass mouse events to that item until we release the mouse...
00230         if(p_lastItemClicked) {
00231                 p_lastItemClicked->mouseMoveEvent(eventInfo);
00232         } else if(item) {
00233                 item->mouseMoveEvent(eventInfo);
00234         }
00235         //END
00236 
00237         updateCurrentResizeHandle( dynamic_cast<ResizeHandle*>(qcnItem) );
00238 }
00239 
00240 void CMManager::updateCurrentResizeHandle( ResizeHandle * resizeHandle )
00241 {
00242         if ( p_lastMouseOverResizeHandle != (QGuardedPtr<ResizeHandle>)resizeHandle )
00243         {
00244                 if (p_lastMouseOverResizeHandle)
00245                         p_lastMouseOverResizeHandle->setHover(false);
00246                 p_lastMouseOverResizeHandle = resizeHandle;
00247                 if (resizeHandle) resizeHandle->setHover(true);
00248         }
00249 }
00250 
00251 void CMManager::mouseReleaseEvent( const EventInfo &eventInfo )
00252 {
00253         // If it returns true, then it has finished its editing operation
00254         if(m_canvasManipulator && m_canvasManipulator->mouseReleased(eventInfo)) {
00255                 delete m_canvasManipulator;
00256                 m_canvasManipulator = 0;
00257         }
00258         
00259         if (p_lastItemClicked) {
00260                 p_lastItemClicked->mouseReleaseEvent(eventInfo);
00261                 p_lastItemClicked=0;
00262         }
00263         
00264         updateCurrentResizeHandle( dynamic_cast<ResizeHandle*>( p_itemDocument->itemAtTop(eventInfo.pos) ) );
00265 }
00266 
00267 void CMManager::wheelEvent( const EventInfo &eventInfo )
00268 {
00269         bool accepted = false;
00270         if (b_allowItemScroll) {
00271                 QCanvasItem *qcnItem = p_itemDocument->itemAtTop(eventInfo.pos);
00272                 Item *item;
00273                 Widget *widget = dynamic_cast<Widget*>(qcnItem);
00274                 if(widget) item = widget->parent();
00275                 else item = dynamic_cast<Item*>(qcnItem);
00276                 
00277                 if(item) accepted = item->wheelEvent(eventInfo);
00278         }
00279 
00280         if (!accepted) {
00281                 // Only allow scrolling of items if we have not just been scrolling the canvas
00282                 b_allowItemScroll = false;
00283                 m_allowItemScrollTmr->stop();
00284                 m_allowItemScrollTmr->start(500,true);
00285                 
00286                 ItemView *itemView = dynamic_cast<ItemView*>(p_itemDocument->activeView());
00287                 if (itemView) {
00288                         itemView->cvbEditor()->setPassEventsToView(false);
00289                         itemView->cvbEditor()->contentsWheelEvent( eventInfo.wheelEvent( 0, 0 ) );
00290                         itemView->cvbEditor()->setPassEventsToView(true);
00291                 }
00292         }
00293 }
00294 
00295 void CMManager::setDrawAction( int drawAction )
00296 {
00297         if ( m_drawAction == drawAction ) return;
00298 
00299         m_drawAction = drawAction;
00300         setCMState( cms_draw, (m_drawAction != -1) );
00301 }
00302 
00303 void CMManager::slotSetManualRoute( bool manualRoute )
00304 {
00305         KGlobal::config()->setGroup("General");
00306         KGlobal::config()->writeEntry( "ManualRouting", manualRoute );
00307         
00308         setCMState( cms_manual_route, manualRoute );
00309 }
00310 
00311 void CMManager::setCMState( CMState type, bool state )
00312 {
00313         // Set or clear the correct bit
00314         state ? (m_cmState|=type) : (m_cmState&=(~type));
00315         
00316         if ( type == CMManager::cms_manual_route )
00317                 emit manualRoutingChanged(state);
00318 }
00319 
00320 void CMManager::setRepeatedAddId( const QString & repeatedId )
00321 {
00322         m_repeatedItemId = repeatedId;
00323 }
00324 
00325 CanvasManipulator::CanvasManipulator( ItemDocument *itemDocument, CMManager *cmManager )
00326 {
00327         p_itemDocument = itemDocument;
00328         p_icnDocument = dynamic_cast<ICNDocument*>(itemDocument);
00329         p_mechanicsDocument = dynamic_cast<MechanicsDocument*>(itemDocument);
00330         p_canvas = p_itemDocument->canvas();
00331 //      b_connectorsAllowedRouting = true;
00332         p_selectList = p_itemDocument->selectList();
00333         p_cnItemSelectList = dynamic_cast<CNItemGroup*>(p_selectList);
00334         p_mechItemSelectList = dynamic_cast<MechanicsGroup*>(p_selectList);
00335         p_cnItemClickedOn = 0;
00336         p_cmManager = cmManager;
00337 }
00338 
00339 CanvasManipulator::~CanvasManipulator()
00340 {
00341 }
00342 
00343 CMRepeatedItemAdd::CMRepeatedItemAdd( ItemDocument *itemDocument, CMManager *cmManager )
00344         : CanvasManipulator( itemDocument, cmManager )
00345 {
00346 }
00347 
00348 CMRepeatedItemAdd::~CMRepeatedItemAdd()
00349 {
00350 }
00351 
00352 CanvasManipulator* CMRepeatedItemAdd::construct( ItemDocument *itemDocument, CMManager *cmManager )
00353 {
00354         return new CMRepeatedItemAdd(itemDocument,cmManager);
00355 }
00356 
00357 ManipulatorInfo *CMRepeatedItemAdd::manipulatorInfo()
00358 {
00359         ManipulatorInfo *eventInfo = new ManipulatorInfo();
00360         eventInfo->m_acceptManipulationPtr = CMRepeatedItemAdd::acceptManipulation;
00361         eventInfo->m_createManipulatorPtr = CMRepeatedItemAdd::construct;
00362         return eventInfo;
00363 }
00364 
00365 bool CMRepeatedItemAdd::acceptManipulation( uint /*eventState*/, uint cmState, uint /*itemType*/, uint /*cnItemType*/ )
00366 {
00367         return (cmState & CMManager::cms_repeated_add);
00368 }
00369 
00370 bool CMRepeatedItemAdd::mousePressedRepeat( const EventInfo &eventInfo )
00371 {
00372         return mousePressedInitial(eventInfo);
00373 }
00374 
00375 bool CMRepeatedItemAdd::mousePressedInitial( const EventInfo &eventInfo )
00376 {
00377         m_eventInfo = eventInfo;
00378         if (eventInfo.isRightClick) {
00379                 p_cmManager->setCMState( CMManager::cms_repeated_add, false );
00380                 return true;
00381         }
00382 
00383         p_icnDocument->addItem( p_cmManager->repeatedItemId(), eventInfo.pos, true );
00384         p_itemDocument->requestStateSave();
00385         return false;
00386 }
00387 
00388 bool CMRepeatedItemAdd::mouseMoved( const EventInfo &/*eventInfo*/ )
00389 {
00390         return false;
00391 }
00392 
00393 bool CMRepeatedItemAdd::mouseReleased( const EventInfo &/*eventInfo*/ )
00394 {
00395         return false;
00396 }
00397 
00398 CMRightClick::CMRightClick( ItemDocument *itemDocument, CMManager *cmManager )
00399         : CanvasManipulator( itemDocument, cmManager )
00400 {
00401 }
00402 
00403 CMRightClick::~CMRightClick()
00404 {
00405 }
00406 
00407 CanvasManipulator* CMRightClick::construct( ItemDocument *itemDocument, CMManager *cmManager )
00408 {
00409         return new CMRightClick(itemDocument,cmManager);
00410 }
00411 
00412 ManipulatorInfo *CMRightClick::manipulatorInfo()
00413 {
00414         ManipulatorInfo *eventInfo = new ManipulatorInfo();
00415 //      eventInfo->m_eventState.m_activate = CMManager::es_right_click;
00416         eventInfo->m_acceptManipulationPtr = CMRightClick::acceptManipulation;
00417         eventInfo->m_createManipulatorPtr = CMRightClick::construct;
00418         return eventInfo;
00419 }
00420 
00421 bool CMRightClick::acceptManipulation( uint eventState, uint /*cmState*/, uint /*itemType*/, uint /*cnItemType*/ )
00422 {
00423         return eventState & CMManager::es_right_click;
00424 }
00425 
00426 bool CMRightClick::mousePressedInitial( const EventInfo &eventInfo )
00427 {
00428         m_eventInfo = eventInfo;
00429         p_itemDocument->canvasRightClick( eventInfo.globalPos, eventInfo.qcanvasItemClickedOn );
00430         return true;
00431 }
00432 
00433 bool CMRightClick::mouseMoved( const EventInfo &/*eventInfo*/ )
00434 {
00435         return true;
00436 }
00437 
00438 bool CMRightClick::mouseReleased( const EventInfo &/*eventInfo*/ )
00439 {
00440         return true;
00441 }
00442 
00443 //BEGIN class ConnectorDraw
00444 ConnectorDraw::ConnectorDraw( ItemDocument *itemDocument, CMManager *cmManager )
00445         : CanvasManipulator( itemDocument, cmManager )
00446 {
00447         p_startNode = 0;
00448         p_startConnector = 0;
00449         p_endNode = 0;
00450         p_endConnector = 0;
00451 }
00452 
00453 ConnectorDraw::~ConnectorDraw()
00454 {
00455 }
00456 
00457 QColor ConnectorDraw::validConnectionColor()
00458 {
00459         return QColor(255, 166, 0);
00460 }
00461 
00462 static double qpoint_distance( const QPoint & p1, const QPoint & p2 )
00463 {
00464         double dx = p1.x() - p2.x();
00465         double dy = p1.y() - p2.y();
00466 
00467         return std::sqrt( dx*dx + dy*dy );
00468 }
00469 
00470 QPoint ConnectorDraw::toValidPos( const QPoint & clickPos, Connector * clickedConnector ) const
00471 {
00472         if(!clickedConnector ) return clickPos;
00473         
00474         const QPointList pointList = clickedConnector->connectorPoints();
00475         QPointList::const_iterator end = pointList.end();
00476         
00477         double dl[] = { 0.5, 8.5, 11.5, 18.0, 23.0 }; // various distances rounded up of (0,0) cells, (0,1), etc
00478 
00479         for ( unsigned i = 0; i < 5; ++i ) {
00480                 for ( QPointList::const_iterator it = pointList.begin(); it != end; ++it ) {
00481                         if(qpoint_distance( *it, clickPos ) <= dl[i]) return *it;
00482                 }
00483         }
00484 
00485         return clickPos;
00486 }
00487 
00488 Connector *ConnectorDraw::toConnector( Node * node )
00489 {
00490         if( !node || node->numCon( true, false ) < 3) return 0;
00491 
00492         const ConnectorList inList = node->inputConnectorList();
00493         if( !inList.isEmpty() )
00494                 return *inList.begin();
00495 
00496         const ConnectorList outList = node->outputConnectorList();
00497         if( !outList.isEmpty() )
00498                 return *outList.begin();
00499 
00500         return 0;
00501 }
00502 
00503 void ConnectorDraw::grabEndStuff( QCanvasItem * endItem, const QPoint & pos, bool posIsExact )
00504 {
00505         if (!endItem) return;
00506 
00507         CNItem * cnItem = dynamic_cast<CNItem*>(endItem);
00508         if ( cnItem && !posIsExact )
00509                 p_endNode = cnItem->getClosestNode(pos);
00510         else p_endNode = dynamic_cast<Node*>(endItem);
00511 
00512         if ( p_endNode && p_endNode->numCon( true, false ) > 2 ) {
00513                 p_endConnector = toConnector(p_endNode);
00514                 p_endNode = 0;
00515         }
00516 
00517         // If the endItem is a node, we have to finish exactly on the end when posIsExact is true
00518         if ( posIsExact && p_endNode && (p_endNode->x() != pos.x() || p_endNode->y() != pos.y()) )
00519                 p_endNode = 0;
00520 
00521         if(!p_endConnector)
00522                 p_endConnector = dynamic_cast<Connector*>(endItem);
00523 }
00524 //END class ConnectorDraw
00525 
00526 //BEGIN class CMAutoConnector
00527 CMAutoConnector::CMAutoConnector( ItemDocument *itemDocument, CMManager *cmManager )
00528         : ConnectorDraw( itemDocument, cmManager )
00529 {
00530         m_connectorLine = 0;
00531         p_startNode = 0;
00532         p_startConnector = 0;
00533 }
00534 
00535 CMAutoConnector::~CMAutoConnector()
00536 {
00537         delete m_connectorLine;
00538         m_connectorLine = 0;
00539 }
00540 
00541 CanvasManipulator* CMAutoConnector::construct( ItemDocument *itemDocument, CMManager *cmManager )
00542 {
00543         return new CMAutoConnector(itemDocument,cmManager);
00544 }
00545 
00546 ManipulatorInfo *CMAutoConnector::manipulatorInfo()
00547 {
00548         ManipulatorInfo *eventInfo = new ManipulatorInfo();
00549         eventInfo->m_acceptManipulationPtr = CMAutoConnector::acceptManipulation;
00550         eventInfo->m_createManipulatorPtr = CMAutoConnector::construct;
00551         return eventInfo;
00552 }
00553 
00554 bool CMAutoConnector::acceptManipulation( uint /*eventState*/, uint cmState, uint itemType, uint /*cnItemType*/ )
00555 {
00556         return (itemType & (CMManager::it_node | CMManager::it_connector)) && !(cmState & CMManager::cms_manual_route);
00557 }
00558 
00559 bool CMAutoConnector::mousePressedInitial( const EventInfo &eventInfo )
00560 {
00561         m_eventInfo = eventInfo;
00562         p_startNode = dynamic_cast<Node*>(eventInfo.qcanvasItemClickedOn);
00563         
00564         if (p_startNode) {
00565                 m_eventInfo.pos = m_prevPos = p_icnDocument->gridSnap( QPoint( (int)p_startNode->x(), (int)p_startNode->y() ) );
00566                 if (p_startNode->numCon( true, false ) > 2)
00567                 {
00568                         p_startConnector = toConnector(p_startNode);
00569                         p_startNode = 0;
00570                 }
00571         } else if (p_startConnector = dynamic_cast<Connector*>(eventInfo.qcanvasItemClickedOn) )
00572         {
00573 //              startConnectorPoint = m_eventInfo.pos = m_prevPos = p_icnDocument->gridSnap(m_eventInfo.pos);
00574                 startConnectorPoint = m_eventInfo.pos = m_prevPos = toValidPos( m_eventInfo.pos, p_startConnector );
00575         } else return true;
00576                 
00577         p_icnDocument->unselectAll();
00578         
00579         delete m_connectorLine;
00580         m_connectorLine = new QCanvasLine(p_canvas);
00581         m_connectorLine->setPen( QColor(0,0,0) );
00582         m_connectorLine->setZ( ItemDocument::Z::ConnectorCreateLine );
00583         m_connectorLine->show();
00584         return false;
00585 }
00586 
00587 bool CMAutoConnector::mouseMoved( const EventInfo &eventInfo )
00588 {
00589         const QPoint pos = eventInfo.pos;
00590         
00591         int newX = p_icnDocument->gridSnap( pos.x() );
00592         int newY = p_icnDocument->gridSnap( pos.y() );
00593 
00594         bool movedFlag = false;
00595 
00596         if ( newX != m_prevPos.x() ) {
00597                 m_prevPos.setX(newX);
00598                 movedFlag = true;
00599         }
00600 
00601         if ( newY != m_prevPos.y() ) {
00602                 m_prevPos.setY(newY);
00603                 movedFlag = true;
00604         }
00605 
00606         m_connectorLine->setPoints( m_eventInfo.pos.x(), m_eventInfo.pos.y(), newX, newY );
00607 
00608         if (movedFlag) {
00609                 QCanvasItem *startItem = 0;
00610                 if (p_startNode)
00611                         startItem = p_startNode;
00612                 else if (p_startConnector)
00613                         startItem = p_startConnector;
00614                 
00615                 QCanvasItem *endItem = p_icnDocument->itemAtTop( QPoint( newX, newY ) );
00616                 if ( endItem && endItem->rtti() == ItemDocument::RTTI::CNItem )
00617                         endItem = (static_cast<CNItem*>(endItem))->getClosestNode( QPoint( newX, newY ) );
00618                 
00619                 bool validLine = p_icnDocument->canConnect( startItem, endItem );
00620                 m_connectorLine->setPen( validLine ? validConnectionColor() : Qt::black );
00621         }
00622         return false;
00623 }
00624 
00625 bool CMAutoConnector::mouseReleased( const EventInfo &eventInfo )
00626 {
00627         const QPoint pos = eventInfo.pos;
00628         
00629         QPoint end = m_connectorLine->endPoint();
00630         delete m_connectorLine;
00631         m_connectorLine = 0;
00632         
00633         QCanvasItem *qcanvasItem = p_icnDocument->itemAtTop(end);
00634         if ( !qcanvasItem )
00635                 return true;
00636         
00637         grabEndStuff( qcanvasItem, pos, false );
00638         
00639         if (p_startConnector) {
00640                 if (p_endConnector) {
00641                         if ( !p_icnDocument->createConnector( p_endConnector, p_startConnector, p_icnDocument->gridSnap(pos), startConnectorPoint ) )
00642                                 return true;
00643                 } else if (p_endNode) {
00644                         if ( !p_icnDocument->createConnector( p_endNode, p_startConnector, startConnectorPoint ) )
00645                                 return true;
00646                 } else return true;
00647         } else if (p_startNode) {
00648                 if (p_endConnector) {
00649                         if ( !p_icnDocument->createConnector( p_startNode, p_endConnector, p_icnDocument->gridSnap(pos) ) )
00650                                 return true;
00651                 } else if (p_endNode) {
00652                         if ( !p_icnDocument->createConnector( p_startNode, p_endNode ) )
00653                                 return true;
00654                 } else return true;
00655         } else return true;
00656 
00657         p_itemDocument->requestStateSave();
00658         return true;
00659 }
00660 //END class CMAutoConnector
00661 
00662 //BEGIN class CMManualConnector
00663 
00664 CMManualConnector::CMManualConnector( ItemDocument *itemDocument, CMManager *cmManager )
00665         : ConnectorDraw( itemDocument, cmManager )
00666 {
00667         m_manualConnectorDraw = 0;
00668 }
00669 
00670 CMManualConnector::~CMManualConnector()
00671 {
00672         delete m_manualConnectorDraw;
00673         m_manualConnectorDraw = 0;
00674 }
00675 
00676 CanvasManipulator* CMManualConnector::construct( ItemDocument *itemDocument, CMManager *cmManager )
00677 {
00678         return new CMManualConnector(itemDocument,cmManager);
00679 }
00680 
00681 ManipulatorInfo *CMManualConnector::manipulatorInfo()
00682 {
00683         ManipulatorInfo *eventInfo = new ManipulatorInfo();
00684         eventInfo->m_acceptManipulationPtr = CMManualConnector::acceptManipulation;
00685         eventInfo->m_createManipulatorPtr = CMManualConnector::construct;
00686         return eventInfo;
00687 }
00688 
00689 bool CMManualConnector::acceptManipulation( uint /*eventState*/, uint cmState, uint itemType, uint /*cnItemType*/ )
00690 {
00691         return (itemType & (CMManager::it_node | CMManager::it_connector)) && (cmState & CMManager::cms_manual_route);
00692 }
00693 
00694 bool CMManualConnector::mousePressedInitial( const EventInfo &eventInfo )
00695 {
00696         if ( eventInfo.isRightClick ) return true;
00697 
00698         m_eventInfo = eventInfo;
00699         p_icnDocument->unselectAll();
00700         QPoint sp;
00701 
00702         if ( eventInfo.itemRtti == ItemDocument::RTTI::Node ) {
00703                 p_startNode = static_cast<Node*>(eventInfo.qcanvasItemClickedOn);
00704                 sp.setX( (int)p_startNode->x() );
00705                 sp.setY( (int)p_startNode->y() );
00706                 if ( p_startNode->numCon( true, false ) > 2 ) {
00707                         p_startConnector = toConnector(p_startNode);
00708                         p_startNode = 0;
00709                 }
00710         } else {
00711                 p_startConnector = dynamic_cast<Connector*>(eventInfo.qcanvasItemClickedOn);
00712                 sp = toValidPos( eventInfo.pos, p_startConnector );
00713         }
00714         startConnectorPoint = sp;
00715         
00716         if (m_manualConnectorDraw) {
00717                 delete m_manualConnectorDraw;
00718                 m_manualConnectorDraw = 0;
00719         }
00720         m_manualConnectorDraw = new ManualConnectorDraw( p_icnDocument, sp );
00721         return false;
00722 }
00723 
00724 bool CMManualConnector::mousePressedRepeat( const EventInfo &eventInfo )
00725 {
00726         m_eventInfo = eventInfo;
00727         if ( eventInfo.isRightClick ) return true;
00728 
00729         m_manualConnectorDraw->mouseClicked( p_icnDocument->gridSnap(m_eventInfo.pos) );
00730         return false;
00731 }
00732 
00733 bool CMManualConnector::mouseMoved( const EventInfo &eventInfo )
00734 {
00735         if(!m_manualConnectorDraw ) return true;
00736 
00737         const QPoint pos = eventInfo.pos;
00738 
00739         int newX = p_icnDocument->gridSnap( pos.x() );
00740         int newY = p_icnDocument->gridSnap( pos.y() );
00741 
00742         bool movedFlag = false;
00743 
00744         if(newX != m_prevPos.x()) {
00745                 m_prevPos.setX(newX);
00746                 movedFlag = true;
00747         }
00748 
00749         if(newY != m_prevPos.y() ) {
00750                 m_prevPos.setY(newY);
00751                 movedFlag = true;
00752         }
00753         
00754         if ( movedFlag ) {
00755                 QCanvasItem *startItem = 0;
00756                 if (p_startNode) startItem = p_startNode;
00757                 else if (p_startConnector)
00758                         startItem = p_startConnector;
00759                 
00760                 QCanvasItem * endItem = p_icnDocument->itemAtTop( QPoint( newX, newY ) );
00761                 
00762                 // If the endItem is a node, we have to finish exactly on the end.
00763                 if(Node * node = dynamic_cast<Node*>(endItem) ) {
00764                         if(node->x() != newX || node->y() != newY )
00765                                 endItem = 0;
00766                 }
00767 
00768                 bool validLine = p_icnDocument->canConnect( startItem, endItem );
00769 
00770                 m_manualConnectorDraw->setColor( validLine ? validConnectionColor() : Qt::black );
00771                 m_manualConnectorDraw->mouseMoved( QPoint( newX, newY ) );
00772         }
00773 
00774         return false;
00775 }
00776 
00777 bool CMManualConnector::mouseReleased( const EventInfo &eventInfo )
00778 {
00779         if (!m_manualConnectorDraw) return true;
00780 
00781         QPoint pos = p_icnDocument->gridSnap(eventInfo.pos);
00782         grabEndStuff( m_manualConnectorDraw->mouseClicked(pos), pos, true );
00783         
00784         if(!p_endNode && !p_endConnector ) return false;
00785         
00786         // Create the points that define the manual route
00787         QPointList list = m_manualConnectorDraw->pointList();
00788         delete m_manualConnectorDraw;
00789         m_manualConnectorDraw = 0;
00790         
00791         if (p_startConnector) {
00792                 if (p_endConnector) {
00793                         if ( !p_icnDocument->createConnector( p_endConnector, p_startConnector, p_icnDocument->gridSnap(pos), startConnectorPoint, &list ) )
00794                                 return true;
00795                 } else {
00796                         if ( !p_icnDocument->createConnector( p_endNode, p_startConnector, startConnectorPoint, &list ) )
00797                                 return true;
00798                 }
00799         } else if (p_startNode) {
00800                 if (p_endConnector) {
00801                         if ( !p_icnDocument->createConnector( p_startNode, p_endConnector, p_icnDocument->gridSnap(pos), &list ) )
00802                                 return true;
00803                 } else {
00804                         if ( !p_icnDocument->createConnector( p_startNode, p_endNode, &list ) )
00805                                 return true;
00806                 }
00807         } else return true;
00808         
00809         p_itemDocument->requestStateSave();
00810         return true;
00811 }
00812 //END class CMManualConnector
00813 
00814 CMItemMove::CMItemMove( ItemDocument *itemDocument, CMManager *cmManager )
00815         : CanvasManipulator( itemDocument, cmManager )
00816 {
00817         p_flowContainerCandidate = 0;
00818 }
00819 
00820 CMItemMove::~CMItemMove()
00821 {
00822 }
00823 
00824 CanvasManipulator* CMItemMove::construct( ItemDocument *itemDocument, CMManager *cmManager )
00825 {
00826         return new CMItemMove(itemDocument,cmManager);
00827 }
00828 
00829 ManipulatorInfo *CMItemMove::manipulatorInfo()
00830 {
00831         ManipulatorInfo *eventInfo = new ManipulatorInfo();
00832         eventInfo->m_acceptManipulationPtr = CMItemMove::acceptManipulation;
00833         eventInfo->m_createManipulatorPtr = CMItemMove::construct;
00834         return eventInfo;
00835 }
00836 
00837 bool CMItemMove::acceptManipulation( uint eventState, uint /*cmState*/, uint itemType, uint cnItemType )
00838 {
00839         return ((itemType & CMManager::it_canvas_item) || (itemType & CMManager::it_drawpart)) && (cnItemType & CMManager::isi_isMovable) && !(eventState & CMManager::es_right_click);
00840 }
00841 
00842 bool CMItemMove::mousePressedInitial( const EventInfo &eventInfo )
00843 {
00844         m_eventInfo = eventInfo;
00845         m_prevPos = eventInfo.pos;
00846         
00847         Item *item = dynamic_cast<Item*>(eventInfo.qcanvasItemClickedOn);
00848         if (!item) return true;
00849 
00850         if ( !p_selectList->contains(item) )
00851         {
00852                 if (!eventInfo.ctrlPressed)
00853                         p_itemDocument->unselectAll();
00854 
00855                 p_itemDocument->select(item);
00856         } else if (m_eventInfo.ctrlPressed)
00857                 p_itemDocument->unselect(item);
00858         
00859         if ( p_selectList->isEmpty() ) return true;
00860         
00861         // We want to allow dragging into FlowContainers if this is a FlowView
00862         p_flowContainerCandidate = 0;
00863         {
00864                 const ItemList &itemList = p_icnDocument->itemList();
00865                 const ItemList::const_iterator ciEnd = itemList.end();
00866                 for ( ItemList::const_iterator it = itemList.begin(); it != ciEnd; ++it )
00867                 {
00868                         if ( FlowContainer *flowContainer = dynamic_cast<FlowContainer*>((Item*)*it) )
00869                                 flowContainer->setFullBounds(true);
00870                 }
00871         }
00872         
00873         ItemList itemList = p_cnItemSelectList->items(false);
00874         itemList.remove((Item*)0);
00875         
00876         const ItemList::iterator itemListEnd = itemList.end();
00877         for ( ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it )
00878         {
00879                 CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it);
00880                 if ( !cnItem || !cnItem->canvas() ) continue;
00881 
00882                 cnItem->setInitialPos(m_eventInfo.pos);
00883         }
00884 
00885         ConnectorList fixedConnectors;
00886         p_icnDocument->getTranslatable( itemList, &fixedConnectors, &m_translatableConnectors, &m_translatableNodeGroups );
00887         
00888         const ConnectorList::iterator fixedConnectorsEnd = fixedConnectors.end();
00889         for ( ConnectorList::iterator it = fixedConnectors.begin(); it != fixedConnectorsEnd; ++it )
00890                 (*it)->setSemiHidden(true);
00891 
00892         p_flowContainerCandidate = p_icnDocument->flowContainer(eventInfo.pos);
00893 
00894         return false;
00895 }
00896 
00897 bool CMItemMove::mouseMoved( const EventInfo &eventInfo )
00898 {
00899         const QPoint pos = eventInfo.pos;
00900 
00901         int x = pos.x();
00902         int y = pos.y();
00903 
00904         const ItemList itemList = p_cnItemSelectList->items();
00905         const ItemList::const_iterator end = itemList.end();
00906         int dx=0, dy=0;
00907 
00908         for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it )
00909         {
00910                 if(!*it || !(*it)->isMovable() )
00911                         continue;
00912                 
00913                 const QRect oldRect = (*it)->boundingRect();
00914                 (*it)->setChanged();
00915                 
00916                 if(CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it) ) {
00917                         dx = -int((*it)->x());
00918                         dy = -int((*it)->y());
00919                         cnItem->snap( x, y );
00920                         dx += int((*it)->x());
00921                         dy += int((*it)->y());
00922                 } else (*it)->moveBy( eventInfo.pos.x()-m_prevPos.x(), eventInfo.pos.y()-m_prevPos.y() );
00923 
00924                 QRect newRect = (*it)->boundingRect();
00925                 QRect merged = oldRect | newRect;
00926         }
00927 
00928         if ( (dx != 0) || (dy != 0) ) {
00929                 const ConnectorList::iterator frEnd = m_translatableConnectors.end();
00930                 for ( ConnectorList::iterator it = m_translatableConnectors.begin(); it != frEnd; ++it )
00931                         (*it)->translateRoute( dx, dy );
00932 
00933                 const NodeGroupList::iterator end = m_translatableNodeGroups.end();
00934                 for ( NodeGroupList::iterator it = m_translatableNodeGroups.begin(); it != end; ++it )
00935                         (*it)->translate( dx, dy );
00936         }
00937 
00938         FlowContainer *fc = p_icnDocument->flowContainer(pos);
00939         if(fc != p_flowContainerCandidate ) {
00940                 if(p_flowContainerCandidate ) {
00941                         p_flowContainerCandidate->setSelected(false);
00942                         p_flowContainerCandidate = 0;
00943                 }
00944         }
00945 
00946         if (fc) {
00947                 p_flowContainerCandidate = fc;
00948                 p_flowContainerCandidate->setSelected(true);
00949         }
00950         
00951         p_itemDocument->