File indexing completed on 2025-02-16 11:41:04
0001 /*************************************************************************** 0002 * Copyright (C) 2003-2005 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 "canvasitemparts.h" 0012 #include "cells.h" 0013 #include "component.h" 0014 #include "icndocument.h" 0015 #include "inputflownode.h" 0016 #include "itemdocumentdata.h" 0017 #include "junctionflownode.h" 0018 #include "junctionnode.h" 0019 #include "outputflownode.h" 0020 #include "pinnode.h" 0021 0022 #include <QBitArray> 0023 #include <QPainter> 0024 0025 #include <cmath> 0026 #include <cstdlib> 0027 0028 #include <ktechlab_debug.h> 0029 0030 CNItem::CNItem(ICNDocument *icnDocument, bool newItem, const QString &id) 0031 : Item(icnDocument, newItem, id) 0032 , CIWidgetMgr(icnDocument ? icnDocument->canvas() : nullptr, this) 0033 , p_icnDocument(icnDocument) 0034 , b_pointsAdded(false) 0035 { 0036 qCDebug(KTL_LOG) << " this=" << this; 0037 0038 setZ(ICNDocument::Z::Item); 0039 setSelected(false); 0040 0041 m_brushCol = QColor(0xf7, 0xf7, 0xff); 0042 m_selectedCol = QColor(101, 134, 192); 0043 0044 setBrush(m_brushCol); 0045 setPen(QPen(Qt::black)); 0046 } 0047 0048 CNItem::~CNItem() 0049 { 0050 const TextMap::iterator textMapEnd = m_textMap.end(); 0051 for (TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it) { 0052 if (it.value()) 0053 it.value()->setCanvas(nullptr); 0054 0055 delete static_cast<Text *>(it.value()); 0056 } 0057 m_textMap.clear(); 0058 0059 updateConnectorPoints(false); 0060 } 0061 0062 bool CNItem::preResize(QRect sizeRect) 0063 { 0064 if ((std::abs(sizeRect.width()) < minimumSize().width()) || (std::abs(sizeRect.height()) < minimumSize().height())) 0065 return false; 0066 0067 updateConnectorPoints(false); 0068 return true; 0069 } 0070 0071 void CNItem::postResize() 0072 { 0073 updateAttachedPositioning(); 0074 } 0075 0076 void CNItem::setVisible(bool yes) 0077 { 0078 if (b_deleted) { 0079 Item::setVisible(false); 0080 return; 0081 } 0082 0083 Item::setVisible(yes); 0084 0085 const TextMap::iterator textMapEnd = m_textMap.end(); 0086 for (TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it) { 0087 it.value()->setVisible(yes); 0088 } 0089 0090 const NodeInfoMap::iterator nodeMapEnd = m_nodeMap.end(); 0091 for (NodeInfoMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it) { 0092 it.value().node->setVisible(yes); 0093 } 0094 0095 CNItem::setDrawWidgets(yes); 0096 0097 if (!yes) 0098 updateConnectorPoints(false); 0099 } 0100 0101 void CNItem::reparented(Item *oldParent, Item *newParent) 0102 { 0103 Item::reparented(oldParent, newParent); 0104 updateNodeLevels(); 0105 } 0106 0107 void CNItem::updateNodeLevels() 0108 { 0109 int l = level(); 0110 0111 // Tell our nodes about our level 0112 const NodeInfoMap::iterator nodeMapEnd = m_nodeMap.end(); 0113 for (NodeInfoMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it) { 0114 it.value().node->setLevel(l); 0115 } 0116 0117 const ItemList::iterator end = m_children.end(); 0118 for (ItemList::iterator it = m_children.begin(); it != end; ++it) { 0119 if (CNItem *cnItem = dynamic_cast<CNItem *>(static_cast<Item *>(*it))) 0120 cnItem->updateNodeLevels(); 0121 } 0122 } 0123 0124 ConnectorList CNItem::connectorList() 0125 { 0126 ConnectorList list; 0127 0128 const NodeInfoMap::iterator nodeMapEnd = m_nodeMap.end(); 0129 for (NodeInfoMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it) { 0130 Node *node = p_icnDocument->nodeWithID(it.value().id); 0131 if (node) { 0132 ConnectorList nodeList = node->getAllConnectors(); 0133 ConnectorList::iterator end = nodeList.end(); 0134 for (ConnectorList::iterator it = nodeList.begin(); it != end; ++it) { 0135 if (*it && !list.contains(*it)) { 0136 list.append(*it); 0137 } 0138 } 0139 } 0140 } 0141 0142 return list; 0143 } 0144 0145 void CNItem::removeItem() 0146 { 0147 if (b_deleted) 0148 return; 0149 0150 const TextMap::iterator textMapEnd = m_textMap.end(); 0151 for (TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it) 0152 it.value()->setCanvas(nullptr); 0153 0154 Item::removeItem(); 0155 updateConnectorPoints(false); 0156 } 0157 0158 void CNItem::restoreFromItemData(const ItemData &itemData) 0159 { 0160 Item::restoreFromItemData(itemData); 0161 0162 updateConnectorPoints(false); 0163 0164 { 0165 const BoolMap::const_iterator end = itemData.buttonMap.end(); 0166 for (BoolMap::const_iterator it = itemData.buttonMap.begin(); it != end; ++it) { 0167 Button *b = button(it.key()); 0168 if (b) 0169 b->setState(it.value()); 0170 } 0171 } 0172 { 0173 const IntMap::const_iterator end = itemData.sliderMap.end(); 0174 for (IntMap::const_iterator it = itemData.sliderMap.begin(); it != end; ++it) { 0175 Slider *s = slider(it.key()); 0176 if (s) 0177 s->setValue(it.value()); 0178 } 0179 } 0180 } 0181 0182 ItemData CNItem::itemData() const 0183 { 0184 ItemData itemData = Item::itemData(); 0185 0186 const WidgetMap::const_iterator end = m_widgetMap.end(); 0187 for (WidgetMap::const_iterator it = m_widgetMap.begin(); it != end; ++it) { 0188 if (Slider *slider = dynamic_cast<Slider *>(*it)) 0189 itemData.sliderMap[slider->id()] = slider->value(); 0190 0191 else if (Button *button = dynamic_cast<Button *>(*it)) 0192 itemData.buttonMap[button->id()] = button->state(); 0193 } 0194 0195 return itemData; 0196 } 0197 0198 Node *CNItem::createNode(double _x, double _y, int orientation, const QString &name, uint type) 0199 { 0200 orientation %= 360; 0201 if (orientation < 0) 0202 orientation += 360; 0203 0204 Node *node = nullptr; 0205 0206 // TODO get rid of this switch statement... 0207 switch (type) { 0208 case Node::ec_pin: 0209 node = new PinNode(p_icnDocument, orientation, QPoint(0, 0)); 0210 break; 0211 case Node::ec_junction: 0212 node = new JunctionNode(p_icnDocument, orientation, QPoint(0, 0)); 0213 break; 0214 case Node::fp_junction: 0215 node = new JunctionFlowNode(p_icnDocument, orientation, QPoint(0, 0)); 0216 break; 0217 case Node::fp_in: 0218 node = new InputFlowNode(p_icnDocument, orientation, QPoint(0, 0)); 0219 break; 0220 case Node::fp_out: 0221 node = new OutputFlowNode(p_icnDocument, orientation, QPoint(0, 0)); 0222 break; 0223 } 0224 0225 node->setLevel(level()); 0226 0227 node->setParentItem(this); 0228 node->setChildId(name); 0229 0230 NodeInfo info; 0231 info.id = node->id(); 0232 info.node = node; 0233 info.x = _x; 0234 info.y = _y; 0235 info.orientation = orientation; 0236 0237 m_nodeMap[name] = info; 0238 0239 updateAttachedPositioning(); 0240 0241 return node; 0242 } 0243 0244 bool CNItem::removeNode(const QString &name) 0245 { 0246 NodeInfoMap::iterator it = m_nodeMap.find(name); 0247 if (it == m_nodeMap.end()) { 0248 return false; 0249 } 0250 it.value().node->removeNode(); 0251 p_icnDocument->flushDeleteList(); 0252 m_nodeMap.erase(it); 0253 return true; 0254 } 0255 0256 Node *CNItem::getClosestNode(const QPoint &pos) 0257 { 0258 // Work through the nodes, finding the one closest to the (x, y) position 0259 Node *shortestNode = nullptr; 0260 double shortestDistance = 1e10; // Nice large distance :-) 0261 0262 const NodeInfoMap::iterator end = m_nodeMap.end(); 0263 for (NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it) { 0264 Node *node = p_icnDocument->nodeWithID(it.value().id); 0265 if (node) { 0266 // Calculate the distance 0267 // Yeah, it's actually the distance squared; but it's all relative, so doesn't matter 0268 double distance = std::pow(node->x() - pos.x(), 2) + std::pow(node->y() - pos.y(), 2); 0269 0270 if (distance < shortestDistance) { 0271 shortestDistance = distance; 0272 shortestNode = node; 0273 } 0274 } 0275 } 0276 0277 return shortestNode; 0278 } 0279 0280 void CNItem::updateAttachedPositioning() 0281 { 0282 if (b_deleted) 0283 return; 0284 0285 // Actually, we don't do anything anymore... 0286 } 0287 0288 void CNItem::updateZ(int baseZ) 0289 { 0290 Item::updateZ(baseZ); 0291 0292 double _z = z(); 0293 0294 const NodeInfoMap::iterator nodeMapEnd = m_nodeMap.end(); 0295 for (NodeInfoMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it) 0296 it.value().node->setZ(_z + 0.5); 0297 0298 const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); 0299 for (WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it) 0300 it.value()->setZ(_z + 0.5); 0301 0302 const TextMap::iterator textMapEnd = m_textMap.end(); 0303 for (TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it) 0304 it.value()->setZ(_z + 0.5); 0305 } 0306 0307 void CNItem::moveBy(double dx, double dy) 0308 { 0309 if (dx == 0 && dy == 0) 0310 return; 0311 updateConnectorPoints(false); 0312 Item::moveBy(dx, dy); 0313 0314 setWidgetsPos(QPoint(int(x()), int(y()))); 0315 } 0316 0317 bool CNItem::mousePressEvent(const EventInfo &info) 0318 { 0319 bool accepted = Item::mousePressEvent(info); 0320 if (!accepted) 0321 accepted = CIWidgetMgr::mousePressEvent(info); 0322 if (accepted) 0323 setChanged(); 0324 return accepted; 0325 } 0326 0327 bool CNItem::mouseReleaseEvent(const EventInfo &info) 0328 { 0329 bool accepted = Item::mouseReleaseEvent(info); 0330 if (!accepted) 0331 accepted = CIWidgetMgr::mouseReleaseEvent(info); 0332 if (accepted) 0333 setChanged(); 0334 return accepted; 0335 } 0336 0337 bool CNItem::mouseDoubleClickEvent(const EventInfo &info) 0338 { 0339 bool accepted = Item::mouseDoubleClickEvent(info); 0340 if (!accepted) 0341 accepted = CIWidgetMgr::mouseDoubleClickEvent(info); 0342 if (accepted) 0343 setChanged(); 0344 return accepted; 0345 } 0346 0347 bool CNItem::mouseMoveEvent(const EventInfo &info) 0348 { 0349 bool accepted = Item::mouseMoveEvent(info); 0350 if (!accepted) 0351 accepted = CIWidgetMgr::mouseMoveEvent(info); 0352 if (accepted) 0353 setChanged(); 0354 return accepted; 0355 } 0356 0357 bool CNItem::wheelEvent(const EventInfo &info) 0358 { 0359 bool accepted = Item::wheelEvent(info); 0360 if (!accepted) 0361 accepted = CIWidgetMgr::wheelEvent(info); 0362 if (accepted) 0363 setChanged(); 0364 return accepted; 0365 } 0366 0367 void CNItem::enterEvent(QEvent *) 0368 { 0369 Item::enterEvent(nullptr); 0370 CIWidgetMgr::enterEvent(nullptr); 0371 setChanged(); 0372 } 0373 0374 void CNItem::leaveEvent(QEvent *) 0375 { 0376 Item::leaveEvent(nullptr); 0377 CIWidgetMgr::leaveEvent(nullptr); 0378 setChanged(); 0379 } 0380 0381 void CNItem::drawShape(QPainter &p) 0382 { 0383 if (!isVisible()) 0384 return; 0385 0386 // initPainter(p); 0387 if (isSelected()) 0388 p.setPen(m_selectedCol); 0389 0390 p.drawPolygon(areaPoints()); 0391 p.drawPolyline(areaPoints()); 0392 // deinitPainter(p); 0393 } 0394 0395 void CNItem::initPainter(QPainter &p) 0396 { 0397 p.setRenderHint(QPainter::Antialiasing); 0398 if (isSelected()) 0399 p.setPen(m_selectedCol); 0400 } 0401 0402 void CNItem::updateConnectorPoints(bool add) 0403 { 0404 if (b_deleted || !isVisible()) 0405 add = false; 0406 0407 if (b_pointsAdded == add) 0408 return; 0409 0410 b_pointsAdded = add; 0411 0412 Cells *cells = p_icnDocument->cells(); 0413 if (!cells) 0414 return; 0415 0416 // Get translation matrix 0417 // Hackish... 0418 QTransform m; 0419 if (Component *c = dynamic_cast<Component *>(this)) 0420 m = c->transMatrix(c->angleDegrees(), c->flipped(), int(x()), int(y()), false); 0421 0422 // Convention used here: _UM = unmapped by both matrix and cell reference, _M = mapped 0423 0424 const QPoint start_UM = QPoint(int(x() + offsetX()) - 8, int(y() + offsetY()) - 8); 0425 const QPoint end_UM = start_UM + QPoint(width() + 2 * 8, height() + 2 * 8); 0426 0427 const QPoint start_M = roundDown(m.map(start_UM), 8); 0428 const QPoint end_M = roundDown(m.map(end_UM), 8); 0429 0430 int sx_M = start_M.x(); 0431 int ex_M = end_M.x(); 0432 0433 int sy_M = start_M.y(); 0434 int ey_M = end_M.y(); 0435 0436 // Normalise start and end points 0437 if (sx_M > ex_M) { 0438 const int temp = sx_M; 0439 sx_M = ex_M; 0440 ex_M = temp; 0441 } 0442 if (sy_M > ey_M) { 0443 const int temp = sy_M; 0444 sy_M = ey_M; 0445 ey_M = temp; 0446 } 0447 0448 ex_M++; 0449 ey_M++; 0450 0451 const int mult = add ? 1 : -1; 0452 0453 for (int x = sx_M; x < ex_M; x++) { 0454 for (int y = sy_M; y < ey_M; y++) { 0455 if (cells->haveCell(x, y)) { 0456 if (x != sx_M && y != sy_M && x != (ex_M - 1) && y != (ey_M - 1)) { 0457 cells->cell(x, y).CIpenalty += mult * ICNDocument::hs_item; 0458 } else { 0459 // (*cells)[x][y].CIpenalty += mult*ICNDocument::hs_item/2; 0460 cells->cell(x, y).CIpenalty += mult * ICNDocument::hs_connector * 5; 0461 } 0462 } 0463 } 0464 } 0465 0466 #if 0 0467 // And subtract the positions of the node on the border 0468 NodeInfoMap::iterator end = m_nodeMap.end(); 0469 for ( NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it ) 0470 { 0471 const int x = (int)((it->second.node->x()-4)/cellSize); 0472 const int y = (int)((it->second.node->y()-4)/cellSize); 0473 if ( p_icnDocument->isValidCellReference(x,y) ) { 0474 (*cells)[x][y].CIpenalty -= mult*ICNDocument::hs_connector*5; 0475 } 0476 } 0477 #endif 0478 0479 const TextMap::iterator textMapEnd = m_textMap.end(); 0480 for (TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it) { 0481 it.value()->updateConnectorPoints(add); 0482 } 0483 const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); 0484 for (WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it) { 0485 it.value()->updateConnectorPoints(add); 0486 } 0487 } 0488 0489 Text *CNItem::addDisplayText(const QString &id, const QRect &pos, const QString &display, bool internal, int flags) 0490 { 0491 Text *text = nullptr; 0492 TextMap::iterator it = m_textMap.find(id); 0493 if (it != m_textMap.end()) { 0494 // qCWarning(KTL_LOG) << "CNItem::addDisplayText: removing old text"; 0495 delete it.value(); 0496 m_textMap.erase(it); 0497 } 0498 0499 text = new Text("", this, pos, canvas(), flags); 0500 text->setZ(z() + (internal ? 0.1 : -0.1)); 0501 0502 m_textMap[id] = text; 0503 0504 // Calculate the correct size 0505 setDisplayText(id, display); 0506 text->show(); 0507 return text; 0508 } 0509 0510 void CNItem::setDisplayText(const QString &id, const QString &display) 0511 { 0512 TextMap::iterator it = m_textMap.find(id); 0513 if (it == m_textMap.end()) { 0514 qCCritical(KTL_LOG) << "CNItem::setDisplayText: Could not find text with id \"" << id << "\""; 0515 return; 0516 } 0517 it.value()->setText(display); 0518 updateAttachedPositioning(); 0519 } 0520 0521 void CNItem::removeDisplayText(const QString &id) 0522 { 0523 TextMap::iterator it = m_textMap.find(id); 0524 if (it == m_textMap.end()) { 0525 // qCCritical(KTL_LOG) << "CNItem::removeDisplayText: Could not find text with id \""<<id<<"\""; 0526 return; 0527 } 0528 it.value()->updateConnectorPoints(false); 0529 delete it.value(); 0530 m_textMap.erase(it); 0531 } 0532 0533 QString CNItem::nodeId(const QString &internalNodeId) 0534 { 0535 NodeInfoMap::iterator it = m_nodeMap.find(internalNodeId); 0536 if (it == m_nodeMap.end()) 0537 return ""; 0538 else 0539 return it.value().id; 0540 } 0541 0542 Node *CNItem::childNode(const QString &childId) 0543 { 0544 return p_icnDocument->nodeWithID(nodeId(childId)); 0545 } 0546 0547 NodeInfo::NodeInfo() 0548 { 0549 node = nullptr; 0550 x = 0.; 0551 y = 0.; 0552 orientation = 0; 0553 } 0554 0555 #include "moc_cnitem.cpp"