cnitem.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2003-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 "canvasitemparts.h"
00012 #include "icndocument.h"
00013 #include "cells.h"
00014 #include "component.h"
00015 #include "ecnode.h"
00016 #include "fpnode.h"
00017 #include "itemdocumentdata.h"
00018 #include <kdebug.h>
00019 
00020 #include <qbitarray.h>
00021 #include <qpainter.h>
00022 
00023 #include <cmath>
00024 
00025 
00026 CNItem::CNItem( ICNDocument *icnDocument, bool newItem, const QString &id )
00027         : Item( icnDocument, newItem, id ),
00028           CIWidgetMgr( icnDocument->canvas(), this ),
00029           p_icnDocument(icnDocument),
00030           b_pointsAdded(false)
00031 {
00032         setZ( ICNDocument::Z::Item );
00033         setSelected(false);
00034         
00035         m_brushCol = QColor( 0xf7, 0xf7, 0xff );
00036         m_selectedCol = QColor( 101, 134, 192 );
00037         
00038         setBrush(m_brushCol);
00039         setPen( Qt::black );
00040 }
00041 
00042 CNItem::~CNItem()
00043 {
00044         const TextMap::iterator textMapEnd = m_textMap.end();
00045         for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it )
00046         {
00047                 if (it.data())
00048                         it.data()->setCanvas(0);
00049                 delete (Text*)it.data();
00050         }
00051         m_textMap.clear();
00052         
00053         updateConnectorPoints(false);
00054 }
00055 
00056 int CNItem::rtti() const
00057 {
00058         return ItemDocument::RTTI::CNItem;
00059 }
00060 
00061 bool CNItem::preResize( QRect sizeRect )
00062 {
00063         if ( (std::abs(sizeRect.width()) < minimumSize().width()) ||
00064                  (std::abs(sizeRect.height()) < minimumSize().height()) )
00065                 return false;
00066         
00067         updateConnectorPoints(false);
00068         return true;
00069 }
00070 
00071 void CNItem::postResize()
00072 {
00073         updateAttachedPositioning();
00074 }
00075 
00076 void CNItem::setVisible( bool yes )
00077 {
00078         if (b_deleted) {
00079                 Item::setVisible(false);
00080                 return;
00081         }
00082 
00083         Item::setVisible(yes);
00084 
00085         const TextMap::iterator textMapEnd = m_textMap.end();
00086         for(TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it) {
00087                 it.data()->setVisible(yes);
00088         }
00089 
00090         const NodeMap::iterator nodeMapEnd = m_nodeMap.end();
00091         for(NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it) {
00092                 it.data().node->setVisible(yes);
00093         }
00094 
00095         CNItem::setDrawWidgets(yes);
00096 
00097         if (!yes)
00098                 updateConnectorPoints(false);
00099 }
00100 
00101 void CNItem::setInitialPos( const QPoint &pos )
00102 {
00103         m_offset = pos - QPoint( (int)x(), (int)y() );
00104 }
00105 
00106 void CNItem::reparented( Item *oldParent, Item *newParent )
00107 {
00108         Item::reparented( oldParent, newParent );
00109         updateNodeLevels();
00110 }
00111 
00112 void CNItem::updateNodeLevels()
00113 {
00114         int l = level();
00115         
00116         // Tell our nodes about our level
00117         const NodeMap::iterator nodeMapEnd = m_nodeMap.end();
00118         for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it )
00119         {
00120                 it.data().node->setLevel(l);
00121         }
00122         
00123         const ItemList::iterator end = m_children.end();
00124         for ( ItemList::iterator it = m_children.begin(); it != end; ++it )
00125         {
00126                 if ( CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it) )
00127                         cnItem->updateNodeLevels();
00128         }
00129 }
00130 
00131 ConnectorList CNItem::connectorList()
00132 {
00133         ConnectorList list;
00134 
00135         const NodeMap::iterator nodeMapEnd = m_nodeMap.end();
00136         for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it )
00137         {
00138                 Node *node = p_icnDocument->nodeWithID(it.data().id);
00139                 if (node)
00140                 {
00141                         ConnectorList nodeList = node->inputConnectorList();
00142                         ConnectorList::iterator end = nodeList.end();
00143                         for ( ConnectorList::iterator it = nodeList.begin(); it != end; ++it )
00144                         {
00145                                 if ( *it && !list.contains(*it) )
00146                                 {
00147                                         list.append(*it);
00148                                 }
00149                         }
00150                         nodeList = node->outputConnectorList();
00151                         end = nodeList.end();
00152                         for ( ConnectorList::iterator it = nodeList.begin(); it != end; ++it )
00153                         {
00154                                 if ( *it && !list.contains(*it) )
00155                                 {
00156                                         list.append(*it);
00157                                 }
00158                         }
00159                 }
00160         }
00161 
00162         return list;
00163 }
00164 
00165 void CNItem::removeItem()
00166 {
00167         if (b_deleted)
00168                 return;
00169         
00170         const TextMap::iterator textMapEnd = m_textMap.end();
00171         for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it )
00172                 it.data()->setCanvas(0);
00173         
00174         Item::removeItem();
00175         updateConnectorPoints(false);
00176 }
00177 
00178 void CNItem::restoreFromItemData( const ItemData &itemData )
00179 {
00180         Item::restoreFromItemData(itemData);
00181         
00182         updateConnectorPoints(false);
00183         
00184         {
00185                 const BoolMap::const_iterator end = itemData.buttonMap.end();
00186                 for ( BoolMap::const_iterator it = itemData.buttonMap.begin(); it != end; ++it )
00187                 {
00188                         Button *b = button(it.key());
00189                         if (b)
00190                                 b->setState(it.data());
00191                 }
00192         }
00193         {
00194                 const IntMap::const_iterator end = itemData.sliderMap.end();
00195                 for ( IntMap::const_iterator it = itemData.sliderMap.begin(); it != end; ++it )
00196                 {
00197                         Slider *s = slider(it.key());
00198                         if (s)
00199                                 s->setValue(it.data());
00200                 }
00201         }
00202 }
00203 
00204 ItemData CNItem::itemData() const
00205 {
00206         ItemData itemData = Item::itemData();
00207         
00208         const WidgetMap::const_iterator end = m_widgetMap.end();
00209         for ( WidgetMap::const_iterator it = m_widgetMap.begin(); it != end; ++it )
00210         {
00211                 if ( Slider *slider = dynamic_cast<Slider*>(*it) )
00212                         itemData.sliderMap[slider->id()] = slider->value();
00213                 
00214                 else if ( Button *button = dynamic_cast<Button*>(*it) )
00215                         itemData.buttonMap[button->id()] = button->state();
00216                 
00217         }
00218         
00219         return itemData;
00220 }
00221 
00222 Node* CNItem::createNode( double _x, double _y, int orientation, const QString &name, uint type )
00223 {
00224         orientation %= 360;
00225         if(orientation < 0 ) orientation += 360;
00226 
00227         Node::node_dir dir;
00228 
00229         if      (orientation ==   0 ) dir = Node::dir_right;
00230         else if (orientation ==  90 ) dir = Node::dir_down;
00231         else if (orientation == 180 ) dir = Node::dir_left;
00232         else if (orientation == 270 ) dir = Node::dir_up;
00233         else {
00234                 kdError() << k_funcinfo << "Unknown orientation: " << orientation << endl;
00235                 return 0;
00236         }
00237         
00238         Node *node;
00239         if ( (type == Node::ec_pin) || (type == Node::ec_junction) )
00240         {
00241                 node = new ECNode( p_icnDocument, Node::node_type(type), dir, QPoint( 0, 0 ) );
00242         } else {
00243                 node = new FPNode( p_icnDocument, Node::node_type(type), dir, QPoint( 0, 0 ) );
00244         }
00245 
00246         node->setLevel( level() );
00247         
00248         node->setParentItem(this);
00249         node->setChildId(name);
00250         
00251         NodeInfo info;
00252         info.id = node->id();
00253         info.node = node;
00254         info.x = _x;
00255         info.y = _y;
00256         info.orientation = orientation;
00257         
00258         m_nodeMap[name] = info;
00259         
00260         updateAttachedPositioning();
00261         
00262         return node;
00263 }
00264 
00265 bool CNItem::removeNode( const QString &name )
00266 {
00267         NodeMap::iterator it = m_nodeMap.find(name);
00268         if ( it == m_nodeMap.end() ) {
00269                 return false;
00270         }
00271         it.data().node->removeNode();
00272         p_icnDocument->flushDeleteList();
00273         m_nodeMap.erase(it);
00274         return true;
00275 }
00276 
00277 Node *CNItem::getClosestNode( const QPoint &pos )
00278 {
00279         // Work through the nodes, finding the one closest to the (x, y) position
00280         Node *shortestNode = 0;
00281         double shortestDistance = 1e10; // Nice large distance :-)
00282         
00283         const NodeMap::iterator end = m_nodeMap.end();
00284         for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it )
00285         {
00286                 Node *node = p_icnDocument->nodeWithID(it.data().id);
00287                 if (node)
00288                 {
00289                         // Calculate the distance
00290                         // Yeah, it's actually the distance squared; but it's all relative, so doesn't matter
00291                         double distance = std::pow(node->x()-pos.x(),2) + std::pow(node->y()-pos.y(),2);
00292                         
00293                         if ( distance < shortestDistance ) {
00294                                 shortestDistance = distance;
00295                                 shortestNode = node;
00296                         }
00297                 }
00298         }
00299         
00300         return shortestNode;
00301 }
00302 
00303 void CNItem::updateAttachedPositioning()
00304 {
00305         // Actually, we don't do anything anymore...
00306 }
00307 
00308 void CNItem::updateZ( int baseZ )
00309 {
00310         Item::updateZ(baseZ);
00311         double _z = z();
00312         
00313         const NodeMap::iterator nodeMapEnd = m_nodeMap.end();
00314         for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it )
00315                 it.data().node->setZ( _z + 0.5 );
00316         
00317         const WidgetMap::iterator widgetMapEnd = m_widgetMap.end();
00318         for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it )
00319                 it.data()->setZ( _z + 0.5 );
00320         
00321         const TextMap::iterator textMapEnd = m_textMap.end();
00322         for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it )
00323                 it.data()->setZ( _z + 0.5 );
00324 }
00325 
00326 void CNItem::snap( int newx, int newy )
00327 {
00328         // Ugly looking thing
00329         // Basically means: Move item to the new position of newx-offsetx and then snap it to the 8-square-side grid
00330         // This is in one move item call so that any attached connectors are only called once to update their routes.
00331         moveBy( 4+newx-m_offset.x()-x()-(int)(newx-m_offset.x())%8, 4+newy-m_offset.y()-y()-(int)(newy-m_offset.y())%8 );
00332 }
00333 
00334 void CNItem::moveBy( double dx, double dy )
00335 {
00336         if ( dx == 0 && dy == 0 ) return;
00337         updateConnectorPoints(false);
00338         Item::moveBy( dx, dy );
00339         
00340         setWidgetsPos( QPoint( int(x()), int(y()) ) );
00341 }
00342 
00343 bool CNItem::mousePressEvent( const EventInfo &info )
00344 {
00345         bool accepted = Item::mousePressEvent(info);
00346         if (!accepted) accepted = CIWidgetMgr::mousePressEvent(info);
00347         if (accepted) setChanged();
00348         return accepted;
00349 }
00350 
00351 bool CNItem::mouseReleaseEvent( const EventInfo &info )
00352 {
00353         bool accepted = Item::mouseReleaseEvent(info);
00354 
00355         if (!accepted) accepted = CIWidgetMgr::mouseReleaseEvent(info);
00356         if (accepted) setChanged();
00357 
00358         return accepted;
00359 }
00360 
00361 bool CNItem::mouseDoubleClickEvent( const EventInfo &info )
00362 {
00363         bool accepted = Item::mouseDoubleClickEvent(info);
00364         if (!accepted)
00365                 accepted = CIWidgetMgr::mouseDoubleClickEvent(info);
00366         if (accepted)
00367                 setChanged();
00368         return accepted;
00369 }
00370 
00371 bool CNItem::mouseMoveEvent( const EventInfo &info )
00372 {
00373         bool accepted = Item::mouseMoveEvent(info);
00374         if (!accepted) accepted = CIWidgetMgr::mouseMoveEvent(info);
00375         if (accepted) setChanged();
00376         return accepted;
00377 }
00378 
00379 bool CNItem::wheelEvent( const EventInfo &info )
00380 {
00381         bool accepted = Item::wheelEvent(info);
00382         if (!accepted) accepted = CIWidgetMgr::wheelEvent(info);
00383         if (accepted) setChanged();
00384         return accepted;
00385 }
00386 
00387 void CNItem::enterEvent()
00388 {
00389         Item::enterEvent();
00390         CIWidgetMgr::enterEvent();
00391         setChanged();
00392 }
00393 
00394 void CNItem::leaveEvent()
00395 {
00396         Item::leaveEvent();
00397         CIWidgetMgr::leaveEvent();
00398         setChanged();
00399 }
00400 
00401 void CNItem::drawShape( QPainter &p )
00402 {
00403         if(!isVisible()) return;
00404 
00405 //      initPainter(p);
00406         if(isSelected()) p.setPen(m_selectedCol);
00407 
00408         p.drawPolygon(areaPoints());
00409         p.drawPolyline(areaPoints());
00410 //      deinitPainter(p);
00411 }
00412 
00413 void CNItem::initPainter( QPainter &p )
00414 {
00415         if(isSelected()) p.setPen(m_selectedCol);
00416 }
00417 
00418 void CNItem::updateConnectorPoints( bool add )
00419 {
00420         if(b_deleted || !isVisible()) add = false;
00421 
00422         if(b_pointsAdded == add) return;
00423 
00424         b_pointsAdded = add;
00425 
00426         Cells *cells = p_icnDocument->cells();
00427         if (!cells) return;
00428 
00429         const int cx = cells->width();
00430         const int cy = cells->height();
00431 
00432         if ( cx < 1 || cy < 1 ) return;
00433 
00434         // Get translation matrix
00435         // Hackish...
00436         QWMatrix m;
00437         if ( Component *c = dynamic_cast<Component*>(this) )
00438                 m = c->transMatrix( c->angleDegrees(), c->flipped(), int(x()), int(y()), false );
00439         
00440         // Convention used here: _UM = unmapped by both matrix and cell reference, _M = mapped
00441 
00442         const QPoint start_UM = QPoint( int(x()+offsetX())-cellSize, int(y()+offsetY())-cellSize );
00443         const QPoint end_UM = start_UM + QPoint( width()+2*cellSize, height()+2*cellSize );
00444         
00445         const QPoint start_M = m.map(start_UM)/cellSize;
00446         const QPoint end_M = m.map(end_UM)/cellSize;
00447 
00448         int sx_M = start_M.x();
00449         int ex_M = end_M.x();
00450 
00451         int sy_M = start_M.y();
00452         int ey_M = end_M.y();
00453 
00454         // Normalise start and end points
00455         if ( sx_M > ex_M ) {
00456                 const int temp = sx_M;
00457                 sx_M = ex_M;
00458                 ex_M = temp;
00459         }
00460 
00461         if ( sy_M > ey_M ) {
00462                 const int temp = sy_M;
00463                 sy_M = ey_M;
00464                 ey_M = temp;
00465         }
00466 
00467         ex_M++;
00468         ey_M++;
00469 
00470         const int mult = add ? 1 : -1;
00471 
00472         for ( int x = sx_M; x < ex_M; x++ )
00473         {
00474                 for ( int y = sy_M; y < ey_M; y++ )
00475                 {
00476                         if ( p_icnDocument->isValidCellReference( x, y ) )
00477                         {
00478                                 if ( x != sx_M && y != sy_M && x != (ex_M-1) && y != (ey_M-1) )
00479                                 {
00480                                         (*cells)[x][y].CIpenalty += mult*ICNDocument::hs_item;
00481                                 } else {
00482 //                              (*cells)[x][y].CIpenalty += mult*ICNDocument::hs_item/2;
00483                                         (*cells)[x][y].CIpenalty += mult*ICNDocument::hs_connector*5;
00484                                 }
00485                         }
00486                 }
00487         }
00488         
00489 #if 0
00490         // And subtract the positions of the node on the border
00491         NodeMap::iterator end = m_nodeMap.end();
00492         for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it )
00493         {
00494                 const int x = (int)((it->second.node->x()-4)/cellSize);
00495                 const int y = (int)((it->second.node->y()-4)/cellSize);
00496                 if ( p_icnDocument->isValidCellReference(x,y) ) {
00497                         (*cells)[x][y].CIpenalty -= mult*ICNDocument::hs_connector*5;
00498                 }
00499         }
00500 #endif
00501         
00502         const TextMap::iterator textMapEnd = m_textMap.end();
00503         for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it )
00504         {
00505                 it.data()->updateConnectorPoints(add);
00506         }
00507 
00508         const WidgetMap::iterator widgetMapEnd = m_widgetMap.end();
00509         for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it )
00510         {
00511                 it.data()->updateConnectorPoints(add);
00512         }
00513 }
00514 
00515 Text* CNItem::addDisplayText( const QString &id, const QRect & pos, const QString &display, bool internal, int flags )
00516 {
00517         Text *text = 0;
00518         TextMap::iterator it = m_textMap.find(id);
00519         if ( it != m_textMap.end() )
00520         {
00521 //              kdWarning() << "CNItem::addDisplayText: removing old text"<<endl;
00522                 delete it.data();
00523                 m_textMap.remove(it);
00524         }
00525         
00526         text = new Text( "", this, pos, canvas(), flags );
00527         text->setZ( z()+(internal?0.1:-0.1) );
00528         
00529         m_textMap[id] = text;
00530         
00531         // Calculate the correct size
00532         setDisplayText( id, display );
00533         text->show();
00534         return text;
00535 }
00536 
00537 void CNItem::setDisplayText( const QString &id, const QString &display )
00538 {
00539         TextMap::iterator it = m_textMap.find(id);
00540         if ( it == m_textMap.end() )
00541         {
00542                 kdError() << "CNItem::setDisplayText: Could not find text with id \""<<id<<"\""<<endl;
00543                 return;
00544         }
00545         it.data()->setText(display);
00546         updateAttachedPositioning();
00547 }
00548 
00549 void CNItem::removeDisplayText( const QString &id )
00550 {
00551         TextMap::iterator it = m_textMap.find(id);
00552         if ( it == m_textMap.end() ) {
00553 //              kdError() << "CNItem::removeDisplayText: Could not find text with id \""<<id<<"\""<<endl;
00554                 return;
00555         }
00556 
00557         it.data()->updateConnectorPoints(false);
00558         delete it.data();
00559         m_textMap.remove(it);
00560 }
00561 
00562 QString CNItem::nodeId( const QString &internalNodeId )
00563 {
00564         NodeMap::iterator it = m_nodeMap.find(internalNodeId);
00565         if ( it == m_nodeMap.end() ) return "";
00566         else return it.data().id;
00567 }
00568 
00569 Node *CNItem::childNode( const QString &childId )
00570 {
00571         return p_icnDocument->nodeWithID( nodeId(childId) );
00572 }
00573 
00574 NodeInfo::NodeInfo()
00575 {
00576         node = 0;
00577         x = 0.;
00578         y = 0.;
00579         orientation = 0;
00580 }
00581 
00582 #include "cnitem.moc"
00583 

Generated on Tue May 8 17:05:28 2007 for KTechLab by  doxygen 1.5.1