File indexing completed on 2024-12-01 11:20:30
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 "flowpart.h" 0012 #include "canvasitemparts.h" 0013 #include "connector.h" 0014 #include "flowcode.h" 0015 #include "flowcodedocument.h" 0016 #include "fpnode.h" 0017 #include "inputflownode.h" 0018 #include "itemdocument.h" 0019 #include "itemdocumentdata.h" 0020 #include "micropackage.h" 0021 #include "microsettings.h" 0022 #include "picinfo.h" 0023 #include "pinmapping.h" 0024 #include "variant.h" 0025 0026 #include <QBitArray> 0027 #include <QBitmap> 0028 #include <QPainter> 0029 #include <QPixmap> 0030 #include <QRegExp> 0031 0032 #include <algorithm> 0033 #include <cassert> 0034 #include <cmath> 0035 0036 #include <ktechlab_debug.h> 0037 0038 // Degrees per radian 0039 const double DPR = 57.29577951308232087665461840231273527024; 0040 0041 // The following arrays of numbers represent the positions of nodes in different configurations, 0042 // with the numbers as NodeInfo::Position. 0043 0044 int diamondNodePositioning[8][3] = {{270, 90, 0}, {270, 90, 180}, {270, 0, 90}, {270, 0, 180}, {180, 0, 90}, {180, 0, 270}, {180, 90, 0}, {180, 90, 270}}; 0045 0046 int inOutNodePositioning[8][2] = {{270, 90}, 0047 {270, 0}, 0048 {270, 180}, 0049 {0, 0}, // (invalid) 0050 {180, 0}, 0051 {180, 90}, 0052 {180, 270}, 0053 {0, 0}}; // (invalid) 0054 0055 int inNodePositioning[4] = {270, 0, 90, 180}; 0056 0057 int outNodePositioning[4] = {90, 180, 270, 0}; 0058 0059 FlowPart::FlowPart(ICNDocument *icnDocument, bool newItem, const QString &id) 0060 : CNItem(icnDocument, newItem, id) 0061 { 0062 m_flowSymbol = FlowPart::ps_other; 0063 m_orientation = 0; 0064 m_stdInput = nullptr; 0065 m_stdOutput = nullptr; 0066 m_altOutput = nullptr; 0067 0068 if (icnDocument) { 0069 icnDocument->registerItem(this); 0070 m_pFlowCodeDocument = dynamic_cast<FlowCodeDocument *>(icnDocument); 0071 assert(m_pFlowCodeDocument); 0072 0073 connect(m_pFlowCodeDocument, &FlowCodeDocument::picTypeChanged, this, &FlowPart::slotUpdateFlowPartVariables); 0074 connect(m_pFlowCodeDocument, &FlowCodeDocument::pinMappingsChangedFlowCode, this, &FlowPart::slotUpdateFlowPartVariables); 0075 } 0076 } 0077 0078 FlowPart::~FlowPart() 0079 { 0080 // We have to check view, as if the item is deleted before the CNItem constructor 0081 // is called, then there will be no view 0082 if (m_pFlowCodeDocument) { 0083 const VariantDataMap::iterator end = m_variantData.end(); 0084 for (VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it) { 0085 Variant *v = it.value(); 0086 if (v) 0087 m_pFlowCodeDocument->varNameChanged("", v->value().toString()); 0088 } 0089 } 0090 } 0091 0092 void FlowPart::setCaption(const QString &caption) 0093 { 0094 if (m_flowSymbol == FlowPart::ps_other) { 0095 m_caption = caption; 0096 return; 0097 } 0098 0099 // 2016.05.03 - do not use temporary widget for getting font metrics 0100 // QWidget *w = new QWidget(); 0101 // //QPainter p(w); 0102 // QPainter p; 0103 // const bool isSuccess = p.begin(w); 0104 // if (!isSuccess) { 0105 // qCWarning(KTL_LOG) << " painter not active"; 0106 // } 0107 // p.setFont( font() ); 0108 // const int text_width = p.boundingRect( boundingRect(), (Qt::SingleLine | Qt::AlignHCenter | Qt::AlignVCenter), caption ).width(); 0109 // p.end(); 0110 // delete w; 0111 0112 QFontMetrics fontMetrics(font()); 0113 const int text_width = fontMetrics.boundingRect(boundingRect(), (Qt::TextSingleLine | Qt::AlignHCenter | Qt::AlignVCenter), caption).width(); 0114 0115 int width = std::max((int(text_width / 16)) * 16, 48); 0116 0117 switch (m_flowSymbol) { 0118 case FlowPart::ps_call: 0119 width += 48; 0120 break; 0121 case FlowPart::ps_io: 0122 case FlowPart::ps_round: 0123 width += 32; 0124 break; 0125 case FlowPart::ps_decision: 0126 width += 64; 0127 break; 0128 case FlowPart::ps_process: 0129 default: 0130 width += 32; 0131 break; 0132 } 0133 0134 bool hasSideConnectors = m_flowSymbol == FlowPart::ps_decision; 0135 if (hasSideConnectors && (width != this->width())) 0136 p_icnDocument->requestRerouteInvalidatedConnectors(); 0137 0138 initSymbol(m_flowSymbol, width); 0139 m_caption = caption; 0140 } 0141 void FlowPart::postResize() 0142 { 0143 updateNodePositions(); 0144 CNItem::postResize(); 0145 } 0146 0147 void FlowPart::createStdInput() 0148 { 0149 m_stdInput = static_cast<FPNode *>(createNode(0, 0, 270, "stdinput", Node::fp_in)); 0150 updateNodePositions(); 0151 } 0152 void FlowPart::createStdOutput() 0153 { 0154 m_stdOutput = static_cast<FPNode *>(createNode(0, 0, 90, "stdoutput", Node::fp_out)); 0155 updateNodePositions(); 0156 } 0157 void FlowPart::createAltOutput() 0158 { 0159 m_altOutput = static_cast<FPNode *>(createNode(0, 0, 0, "altoutput", Node::fp_out)); 0160 updateNodePositions(); 0161 } 0162 0163 void FlowPart::initSymbol(FlowPart::FlowSymbol symbol, int width) 0164 { 0165 m_flowSymbol = symbol; 0166 0167 switch (symbol) { 0168 case FlowPart::ps_other: 0169 return; 0170 case FlowPart::ps_call: 0171 case FlowPart::ps_process: 0172 setItemPoints(QRect(-width / 2, -16, width, 24)); 0173 break; 0174 case FlowPart::ps_io: { 0175 // define parallelogram shape 0176 QPolygon pa(4); 0177 pa[0] = QPoint(-(width - 10) / 2, -16); 0178 pa[1] = QPoint(width / 2, -16); 0179 pa[2] = QPoint((width - 10) / 2, 8); 0180 pa[3] = QPoint(-width / 2, 8); 0181 setItemPoints(pa); 0182 break; 0183 } 0184 case FlowPart::ps_round: { 0185 // define rounded rectangles as two semicricles with RP_NUM/2 points with gap inbetween 0186 // These points are not used for drawing; merely for passing to qcanvaspolygonitem for collision detection 0187 // If there is a better way for a rounder rectangle + collision detection, please let me know... 0188 0189 int halfHeight = 12; 0190 0191 // Draw semicircle 0192 double x; 0193 const int RP_NUM = 48; 0194 QPolygon pa(RP_NUM); 0195 int point = 0; 0196 for (double y = -1.0; y <= 1.0; y += 4.0 / (RP_NUM - 2)) { 0197 x = sqrt(1 - y * y) * halfHeight; 0198 pa[point] = QPoint(int(width + x) - halfHeight, int(halfHeight * y)); 0199 pa[RP_NUM - 1 - point] = QPoint(int(halfHeight - x), int(halfHeight * y)); 0200 point++; 0201 } 0202 0203 pa.translate(-width / 2, 4); 0204 setItemPoints(pa); 0205 break; 0206 } 0207 0208 case FlowPart::ps_decision: { 0209 // define rhombus 0210 QPolygon pa(6); 0211 pa[0] = QPoint(0, -24); 0212 pa[1] = QPoint(width / 2, -6); 0213 pa[2] = QPoint(width / 2, 6); 0214 pa[3] = QPoint(0, 24); 0215 pa[4] = QPoint(-width / 2, 6); 0216 pa[5] = QPoint(-width / 2, -6); 0217 setItemPoints(pa); 0218 break; 0219 } 0220 default: 0221 qCCritical(KTL_LOG) << "Unknown flowSymbol: " << symbol; 0222 } 0223 } 0224 0225 void FlowPart::drawShape(QPainter &p) 0226 { 0227 initPainter(p); 0228 0229 const double _x = int(x() + offsetX()); 0230 const double _y = int(y() + offsetY()); 0231 const double w = width(); 0232 double h = height(); 0233 0234 switch (m_flowSymbol) { 0235 case FlowPart::ps_other: 0236 CNItem::drawShape(p); 0237 break; 0238 case FlowPart::ps_io: { 0239 h--; 0240 double roundSize = 8; 0241 double slantIndent = 5; 0242 0243 // CNItem::drawShape(p); 0244 double inner = std::atan(h / slantIndent); 0245 double outer = M_PI - inner; 0246 0247 int inner16 = int(16 * inner * DPR); 0248 int outer16 = int(16 * outer * DPR); 0249 0250 p.save(); 0251 p.setPen(Qt::NoPen); 0252 p.drawPolygon(areaPoints()); 0253 p.restore(); 0254 0255 p.drawLine(int(_x + slantIndent + roundSize / 2), int(_y), int(_x + w - roundSize / 2), int(_y)); 0256 p.drawLine(int(_x + w - slantIndent - roundSize / 2), int(_y + h), int(_x + roundSize / 2), int(_y + h)); 0257 p.drawLine( 0258 int(_x + w + (std::sin(outer) - 1) * roundSize / 2), int(_y + (1 - std::cos(outer)) * roundSize / 2), int(_x + w - slantIndent + (std::sin(inner) - 1) * roundSize / 2), int(_y + h + (std::cos(inner) - 1) * roundSize / 2)); 0259 p.drawLine(int(_x + (1 - std::sin(outer)) * roundSize / 2), int(_y + h + (std::cos(outer) - 1) * roundSize / 2), int(_x + slantIndent + (1 - std::sin(inner)) * roundSize / 2), int(_y + (1 - std::cos(inner)) * roundSize / 2)); 0260 p.drawArc(int(_x + slantIndent), int(_y), int(roundSize), int(roundSize), 90 * 16, inner16); 0261 p.drawArc(int(_x + w - roundSize), int(_y), int(roundSize), int(roundSize), 270 * 16 + inner16, outer16); 0262 p.drawArc(int(_x - slantIndent + w - roundSize), int(_y + h - roundSize), int(roundSize), int(roundSize), 270 * 16, inner16); 0263 p.drawArc(int(_x), int(_y + h - roundSize), int(roundSize), int(roundSize), 90 * 16 + inner16, outer16); 0264 break; 0265 } 0266 case FlowPart::ps_decision: 0267 // TODO Make the shape nice and pretty with rounded corners 0268 CNItem::drawShape(p); 0269 break; 0270 case FlowPart::ps_call: 0271 p.drawRoundedRect(int(_x), int(_y), int(w), int(h + 1), int(1000. / w), int(1000. / h), Qt::RelativeSize); 0272 p.drawLine(int(_x + 8), int(_y), int(_x + 8), int(_y + h)); 0273 p.drawLine(int(_x + w - 8), int(_y), int(_x + w - 8), int(_y + h)); 0274 break; 0275 case FlowPart::ps_process: 0276 p.drawRoundedRect(int(_x), int(_y), int(w), int(h + 1), int(1000. / w), int(1000. / h), Qt::RelativeSize); 0277 break; 0278 case FlowPart::ps_round: 0279 p.drawRoundedRect(int(_x), int(_y), int(w), int(h + 1), 30, 100, Qt::RelativeSize); 0280 break; 0281 } 0282 0283 p.setPen(Qt::black); 0284 p.setFont(font()); 0285 p.drawText(boundingRect(), (Qt::TextWordWrap | Qt::AlignHCenter | Qt::AlignVCenter), m_caption); 0286 } 0287 0288 QString FlowPart::gotoCode(const QString &internalNodeId) 0289 { 0290 FlowPart *end = outputPart(internalNodeId); 0291 if (!end) 0292 return ""; 0293 return "goto " + end->id(); 0294 } 0295 0296 FlowPart *FlowPart::outputPart(const QString &internalNodeId) 0297 { 0298 Node *node = p_icnDocument->nodeWithID(nodeId(internalNodeId)); 0299 0300 FPNode *fpnode = dynamic_cast<FPNode *>(node); 0301 // FIXME dynamic_cast used to replace fpnode::type() call 0302 if (!fpnode || (dynamic_cast<InputFlowNode *>(fpnode) != nullptr)) 0303 // if ( !fpnode || fpnode->type() == Node::fp_in ) 0304 return nullptr; 0305 0306 return fpnode->outputFlowPart(); 0307 } 0308 0309 FlowPartList FlowPart::inputParts(const QString &id) 0310 { 0311 Node *node = p_icnDocument->nodeWithID(id); 0312 0313 if (FPNode *fpNode = dynamic_cast<FPNode *>(node)) 0314 return fpNode->inputFlowParts(); 0315 0316 return FlowPartList(); 0317 } 0318 0319 FlowPartList FlowPart::inputParts() 0320 { 0321 FlowPartList list; 0322 0323 const NodeInfoMap::iterator nEnd = m_nodeMap.end(); 0324 for (NodeInfoMap::iterator it = m_nodeMap.begin(); it != nEnd; ++it) { 0325 Node *node = p_icnDocument->nodeWithID(it.value().id); 0326 FlowPartList newList; 0327 0328 if (FPNode *fpNode = dynamic_cast<FPNode *>(node)) 0329 newList = fpNode->inputFlowParts(); 0330 0331 const FlowPartList::iterator nlEnd = newList.end(); 0332 for (FlowPartList::iterator it = newList.begin(); it != nlEnd; ++it) { 0333 if (*it) 0334 list.append(*it); 0335 } 0336 } 0337 0338 return list; 0339 } 0340 0341 FlowPartList FlowPart::outputParts() 0342 { 0343 FlowPartList list; 0344 0345 const NodeInfoMap::iterator end = m_nodeMap.end(); 0346 for (NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it) { 0347 FlowPart *part = outputPart(it.key()); 0348 if (part) 0349 list.append(part); 0350 } 0351 0352 return list; 0353 } 0354 0355 FlowPart *FlowPart::endPart(QStringList ids, FlowPartList *previousParts) 0356 { 0357 if (ids.empty()) { 0358 const NodeInfoMap::iterator end = m_nodeMap.end(); 0359 for (NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it) { 0360 ids.append(it.key()); 0361 } 0362 filterEndPartIDs(&ids); 0363 } 0364 0365 const bool createdList = (!previousParts); 0366 if (createdList) { 0367 previousParts = new FlowPartList; 0368 } else if (previousParts->contains(this)) { 0369 return nullptr; 0370 } 0371 0372 previousParts->append(this); 0373 0374 if (ids.empty()) { 0375 return nullptr; 0376 } 0377 0378 if (ids.size() == 1) { 0379 return outputPart(*(ids.begin())); 0380 } 0381 0382 typedef QList<FlowPartList> ValidPartsList; 0383 ValidPartsList validPartsList; 0384 0385 const QStringList::iterator idsEnd = ids.end(); 0386 for (QStringList::iterator it = ids.begin(); it != idsEnd; ++it) { 0387 //int prevLevel = level(); 0388 FlowPartList validParts; 0389 FlowPart *part = outputPart(*it); 0390 while (part) { 0391 if (!validParts.contains(part)) { 0392 validParts.append(part); 0393 // if ( part->level() >= level() ) { 0394 //const int _l = part->level(); 0395 part = part->endPart(QStringList(), previousParts); 0396 //prevLevel = _l; 0397 // } else { 0398 // part = nullptr; 0399 // } 0400 } else { 0401 part = nullptr; 0402 } 0403 } 0404 if (!validParts.empty()) { 0405 validPartsList.append(validParts); 0406 } 0407 } 0408 0409 if (createdList) { 0410 delete previousParts; 0411 previousParts = nullptr; 0412 } 0413 0414 if (validPartsList.empty()) 0415 return nullptr; 0416 0417 FlowPartList firstList = *(validPartsList.begin()); 0418 const FlowPartList::iterator flEnd = firstList.end(); 0419 const ValidPartsList::iterator vplEnd = validPartsList.end(); 0420 for (FlowPartList::iterator it = firstList.begin(); it != flEnd; ++it) { 0421 bool ok = true; 0422 for (ValidPartsList::iterator vplit = validPartsList.begin(); vplit != vplEnd; ++vplit) { 0423 if (!(*vplit).contains(*it)) 0424 ok = false; 0425 } 0426 if (ok) 0427 return *it; 0428 } 0429 0430 return nullptr; 0431 } 0432 0433 void FlowPart::handleIfElse(FlowCode *code, const QString &case1Statement, const QString &case2Statement, const QString &case1, const QString &case2) 0434 { 0435 if (!code) 0436 return; 0437 0438 FlowPart *stop = nullptr; 0439 FlowPart *part1 = outputPart(case1); 0440 FlowPart *part2 = outputPart(case2); 0441 0442 if (part1 && part2) 0443 stop = endPart((QStringList(case1) << case2)); 0444 0445 if ((!part1 && !part2) || (part1 == stop && part2 == stop)) 0446 return; 0447 0448 code->addStopPart(stop); 0449 0450 if (part1 && part1 != stop && code->isValidBranch(part1)) { 0451 // Use the case1 statement 0452 code->addCode("if " + case1Statement + " then " + "\n{"); 0453 code->addCodeBranch(part1); 0454 code->addCode("}"); 0455 0456 if (part2 && part2 != stop && code->isValidBranch(part2)) { 0457 code->addCode("else\n{"); 0458 code->addCodeBranch(part2); 0459 code->addCode("}"); 0460 } 0461 } else if (code->isValidBranch(part2)) { 0462 // Use the case2 statement 0463 code->addCode("if " + case2Statement + " then " + "\n{"); 0464 code->addCodeBranch(part2); 0465 code->addCode("}"); 0466 } 0467 0468 code->removeStopPart(stop); 0469 code->addCodeBranch(stop); 0470 } 0471 0472 Variant *FlowPart::createProperty(const QString &id, Variant::Type::Value type) 0473 { 0474 if (type != Variant::Type::Port && type != Variant::Type::Pin && type != Variant::Type::VarName && type != Variant::Type::SevenSegment && type != Variant::Type::KeyPad) 0475 return CNItem::createProperty(id, type); 0476 0477 Variant *v = createProperty(id, Variant::Type::String); 0478 v->setType(type); 0479 0480 if (type == Variant::Type::VarName) { 0481 if (m_pFlowCodeDocument) { 0482 if (MicroSettings *settings = m_pFlowCodeDocument->microSettings()) 0483 v->setAllowed(settings->variableNames()); 0484 } 0485 connect(property(id), qOverload<QVariant, QVariant>(&Property::valueChanged), this, &FlowPart::varNameChanged); 0486 } else 0487 slotUpdateFlowPartVariables(); 0488 0489 return v; 0490 } 0491 0492 void FlowPart::slotUpdateFlowPartVariables() 0493 { 0494 if (!m_pFlowCodeDocument) 0495 return; 0496 0497 MicroSettings *s = m_pFlowCodeDocument->microSettings(); 0498 if (!s) 0499 return; 0500 0501 const PinMappingMap pinMappings = s->pinMappings(); 0502 QStringList sevenSegMaps; 0503 QStringList keyPadMaps; 0504 0505 PinMappingMap::const_iterator pEnd = pinMappings.end(); 0506 for (PinMappingMap::const_iterator it = pinMappings.begin(); it != pEnd; ++it) { 0507 switch (it.value().type()) { 0508 case PinMapping::SevenSegment: 0509 sevenSegMaps << it.key(); 0510 break; 0511 0512 case PinMapping::Keypad_4x3: 0513 case PinMapping::Keypad_4x4: 0514 keyPadMaps << it.key(); 0515 break; 0516 0517 case PinMapping::Invalid: 0518 break; 0519 } 0520 } 0521 0522 QStringList ports = s->microInfo()->package()->portNames(); 0523 ports.sort(); 0524 0525 QStringList pins = s->microInfo()->package()->pinIDs(PicPin::type_bidir | PicPin::type_input | PicPin::type_open); 0526 pins.sort(); 0527 0528 const VariantDataMap::iterator vEnd = m_variantData.end(); 0529 for (VariantDataMap::iterator it = m_variantData.begin(); it != vEnd; ++it) { 0530 Variant *v = it.value(); 0531 if (!v) 0532 continue; 0533 0534 if (v->type() == Variant::Type::Port) 0535 v->setAllowed(ports); 0536 else if (v->type() == Variant::Type::Pin) 0537 v->setAllowed(pins); 0538 else if (v->type() == Variant::Type::SevenSegment) { 0539 v->setAllowed(sevenSegMaps); 0540 if (!sevenSegMaps.isEmpty() && !sevenSegMaps.contains(v->value().toString())) 0541 v->setValue(sevenSegMaps.first()); 0542 } else if (v->type() == Variant::Type::KeyPad) { 0543 v->setAllowed(keyPadMaps); 0544 if (!keyPadMaps.isEmpty() && !keyPadMaps.contains(v->value().toString())) 0545 v->setValue(keyPadMaps.first()); 0546 } 0547 } 0548 } 0549 0550 void FlowPart::updateVarNames() 0551 { 0552 if (!m_pFlowCodeDocument) 0553 return; 0554 0555 MicroSettings *s = m_pFlowCodeDocument->microSettings(); 0556 if (!s) 0557 return; 0558 0559 const QStringList names = s->variableNames(); 0560 const VariantDataMap::iterator end = m_variantData.end(); 0561 for (VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it) { 0562 Variant *v = it.value(); 0563 if (v && v->type() == Variant::Type::VarName) 0564 v->setAllowed(names); 0565 } 0566 } 0567 0568 void FlowPart::varNameChanged(QVariant newValue, QVariant oldValue) 0569 { 0570 if (!m_pFlowCodeDocument) 0571 return; 0572 m_pFlowCodeDocument->varNameChanged(newValue.toString(), oldValue.toString()); 0573 } 0574 0575 inline int nodeDirToPos(int dir) 0576 { 0577 switch (dir) { 0578 case 0: 0579 return 0; 0580 case 270: 0581 return 1; 0582 case 180: 0583 return 2; 0584 case 90: 0585 return 3; 0586 } 0587 return 0; 0588 } 0589 0590 void FlowPart::updateAttachedPositioning() 0591 { 0592 if (b_deleted) 0593 return; 0594 0595 // BEGIN Rearrange text if appropriate 0596 const QRect textPos[4] = {QRect(offsetX() + width(), 6, 40, 16), QRect(0, offsetY() - 16, 40, 16), QRect(offsetX() - 40, 6, 40, 16), QRect(0, offsetY() + height(), 40, 16)}; 0597 0598 NodeInfo *stdOutputInfo = m_stdOutput ? &m_nodeMap["stdoutput"] : nullptr; 0599 NodeInfo *altOutputInfo = m_altOutput ? &m_nodeMap["altoutput"] : nullptr; 0600 0601 Text *outputTrueText = m_textMap.contains("output_true") ? m_textMap["output_true"] : nullptr; 0602 Text *outputFalseText = m_textMap.contains("output_false") ? m_textMap["output_false"] : nullptr; 0603 0604 if (stdOutputInfo && outputTrueText) 0605 outputTrueText->setOriginalRect(textPos[nodeDirToPos(stdOutputInfo->orientation)]); 0606 0607 if (altOutputInfo && outputFalseText) 0608 outputFalseText->setOriginalRect(textPos[nodeDirToPos(altOutputInfo->orientation)]); 0609 0610 const TextMap::iterator textMapEnd = m_textMap.end(); 0611 for (TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it) { 0612 QRect pos = it.value()->recommendedRect(); 0613 it.value()->move(pos.x() + x(), pos.y() + y()); 0614 it.value()->setGuiPartSize(pos.width(), pos.height()); 0615 } 0616 // END Rearrange text if appropriate 0617 0618 const NodeInfoMap::iterator end = m_nodeMap.end(); 0619 for (NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it) { 0620 if (!it.value().node) { 0621 qCCritical(KTL_LOG) << "Node in nodemap is null"; 0622 continue; 0623 } 0624 0625 double nx = it.value().x; 0626 double ny = it.value().y; 0627 0628 #define round_8(x) (((x) > 0) ? int(((x) + 4) / 8) * 8 : int(((x)-4) / 8) * 8) 0629 nx = round_8(nx); 0630 ny = round_8(ny); 0631 #undef round_8 0632 0633 it.value().node->move(int(nx + x()), int(ny + y())); 0634 it.value().node->setOrientation(it.value().orientation); 0635 } 0636 } 0637 0638 ItemData FlowPart::itemData() const 0639 { 0640 ItemData itemData = CNItem::itemData(); 0641 itemData.orientation = m_orientation; 0642 return itemData; 0643 } 0644 0645 void FlowPart::restoreFromItemData(const ItemData &itemData) 0646 { 0647 CNItem::restoreFromItemData(itemData); 0648 if (itemData.orientation >= 0) 0649 setOrientation(uint(itemData.orientation)); 0650 } 0651 0652 void FlowPart::updateNodePositions() 0653 { 0654 if (m_orientation > 7) { 0655 qCWarning(KTL_LOG) << "Invalid orientation: " << m_orientation; 0656 return; 0657 } 0658 0659 NodeInfo *stdInputInfo = m_stdInput ? &m_nodeMap["stdinput"] : nullptr; 0660 NodeInfo *stdOutputInfo = m_stdOutput ? &m_nodeMap["stdoutput"] : nullptr; 0661 NodeInfo *altOutputInfo = m_altOutput ? &m_nodeMap["altoutput"] : nullptr; 0662 0663 if (m_stdInput && m_stdOutput && m_altOutput) { 0664 stdInputInfo->orientation = diamondNodePositioning[m_orientation][0]; 0665 stdOutputInfo->orientation = diamondNodePositioning[m_orientation][1]; 0666 altOutputInfo->orientation = diamondNodePositioning[m_orientation][2]; 0667 } else if (m_stdInput && m_stdOutput) { 0668 stdInputInfo->orientation = inOutNodePositioning[m_orientation][0]; 0669 stdOutputInfo->orientation = inOutNodePositioning[m_orientation][1]; 0670 } else if (m_orientation < 4) { 0671 if (stdInputInfo) 0672 stdInputInfo->orientation = inNodePositioning[m_orientation]; 0673 else if (stdOutputInfo) 0674 stdOutputInfo->orientation = outNodePositioning[m_orientation]; 0675 } else { 0676 qCWarning(KTL_LOG) << "Invalid orientation: " << m_orientation; 0677 return; 0678 } 0679 0680 const NodeInfoMap::iterator end = m_nodeMap.end(); 0681 for (NodeInfoMap::iterator it = m_nodeMap.begin(); it != end; ++it) { 0682 if (!it.value().node) 0683 qCCritical(KTL_LOG) << "Node in nodemap is null"; 0684 else { 0685 switch (it.value().orientation) { 0686 case 0: 0687 it.value().x = offsetX() + width() + 8; 0688 it.value().y = 0; 0689 break; 0690 case 270: 0691 it.value().x = 0; 0692 it.value().y = offsetY() - 8; 0693 break; 0694 case 180: 0695 it.value().x = offsetX() - 8; 0696 it.value().y = 0; 0697 break; 0698 case 90: 0699 it.value().x = 0; 0700 it.value().y = offsetY() + height() + 8; 0701 ; 0702 break; 0703 } 0704 } 0705 } 0706 0707 updateAttachedPositioning(); 0708 } 0709 0710 void FlowPart::setOrientation(uint orientation) 0711 { 0712 if (orientation == m_orientation) 0713 return; 0714 0715 m_orientation = orientation; 0716 updateNodePositions(); 0717 p_icnDocument->requestRerouteInvalidatedConnectors(); 0718 } 0719 0720 uint FlowPart::allowedOrientations() const 0721 { 0722 // The bit positions shown here represent whether or not that orientation is allowed, the orientation being 0723 // what is displayed in the i'th position (0 to 3 on top, 4 to 7 on bottom) of orientation widget 0724 0725 if (m_stdInput && m_stdOutput && m_altOutput) 0726 return 255; 0727 0728 if (m_stdInput && m_stdOutput) 0729 return 119; 0730 0731 if (m_stdInput || m_stdOutput) 0732 return 15; 0733 0734 return 0; 0735 } 0736 0737 void FlowPart::orientationPixmap(uint orientation, QPixmap &pm) const 0738 { 0739 const QSize size = pm.size(); 0740 0741 if (!(allowedOrientations() & (1 << orientation))) { 0742 qCWarning(KTL_LOG) << "Requesting invalid orientation of " << orientation; 0743 return; 0744 } 0745 0746 QBitmap mask(50, 50); 0747 // QPainter maskPainter(&mask); // 2016.05.03 - initialize painter explicitly 0748 QPainter maskPainter; 0749 { 0750 const bool isSuccess = maskPainter.begin(&mask); 0751 if (!isSuccess) { 0752 qCWarning(KTL_LOG) << " painter not active"; 0753 } 0754 } 0755 0756 mask.fill(Qt::color0); 0757 maskPainter.setBrush(Qt::color1); 0758 maskPainter.setPen(Qt::color1); 0759 0760 // BEGIN painter on pm 0761 { 0762 // QPainter p(&pm); // 2016.05.03 - explicitly initialize painter 0763 QPainter p; 0764 const bool isBeginSuccess = p.begin(&pm); 0765 if (!isBeginSuccess) { 0766 qCWarning(KTL_LOG) << " painter not active"; 0767 } 0768 p.setBrush(m_brushCol); 0769 p.setPen(Qt::black); 0770 0771 // In order: right corner, top corner, left corner, bottom corner 0772 0773 QPoint c[4] = {QPoint(int(0.7 * size.width()), int(0.5 * size.height())), 0774 QPoint(int(0.5 * size.width()), int(0.4 * size.height())), 0775 QPoint(int(0.3 * size.width()), int(0.5 * size.height())), 0776 QPoint(int(0.5 * size.width()), int(0.6 * size.height()))}; 0777 0778 QPoint d[4]; 0779 d[0] = c[0] + QPoint(7, 0); 0780 d[1] = c[1] + QPoint(0, -7); 0781 d[2] = c[2] + QPoint(-7, 0); 0782 d[3] = c[3] + QPoint(0, 7); 0783 0784 if (m_stdInput && m_stdOutput && m_altOutput) { 0785 // BEGIN Draw diamond outline 0786 QPolygon diamond(4); 0787 for (uint i = 0; i < 4; ++i) 0788 diamond[i] = c[i]; 0789 0790 p.drawPolygon(diamond); 0791 maskPainter.drawPolygon(diamond); 0792 // END Draw diamond outline 0793 0794 // BEGIN Draw input 0795 int pos0 = nodeDirToPos(diamondNodePositioning[orientation][0]); 0796 p.drawLine(c[pos0], d[pos0]); 0797 maskPainter.drawLine(c[pos0], d[pos0]); 0798 // END Draw input 0799 0800 // BEGIN Draw "true" output as a tick 0801 QPolygon tick(4); 0802 tick[0] = QPoint(-3, 0); 0803 tick[1] = QPoint(0, 2); 0804 tick[2] = QPoint(0, 2); 0805 tick[3] = QPoint(4, -2); 0806 0807 int pos1 = nodeDirToPos(diamondNodePositioning[orientation][1]); 0808 tick.translate(d[pos1].x(), d[pos1].y()); 0809 p.drawLines(tick); 0810 maskPainter.drawLines(tick); 0811 // END Draw "true" output as a tick 0812 0813 // BEGIN Draw "false" output as a cross 0814 QPolygon cross(4); 0815 cross[0] = QPoint(-2, -2); 0816 cross[1] = QPoint(2, 2); 0817 cross[2] = QPoint(-2, 2); 0818 cross[3] = QPoint(2, -2); 0819 0820 int pos2 = nodeDirToPos(diamondNodePositioning[orientation][2]); 0821 cross.translate(d[pos2].x(), d[pos2].y()); 0822 p.drawLines(cross); 0823 maskPainter.drawLines(cross); 0824 // END Draw "false" output as a cross 0825 } else if (m_stdInput || m_stdOutput) { 0826 p.drawRoundedRect(int(0.3 * size.width()), int(0.4 * size.height()), int(0.4 * size.width()), int(0.2 * size.height()), 25, 25, Qt::RelativeSize); 0827 maskPainter.drawRoundedRect(int(0.3 * size.width()), int(0.4 * size.height()), int(0.4 * size.width()), int(0.2 * size.height()), 25, 25, Qt::RelativeSize); 0828 0829 int hal = 5; // half arrow length 0830 int haw = 3; // half arrow width 0831 0832 QPoint arrows[4][6] = {{QPoint(hal, 0), QPoint(0, -haw), QPoint(hal, 0), QPoint(-hal, 0), QPoint(hal, 0), QPoint(0, haw)}, 0833 0834 {QPoint(0, -hal), QPoint(-haw, 0), QPoint(0, -hal), QPoint(0, hal), QPoint(0, -hal), QPoint(haw, 0)}, 0835 0836 {QPoint(-hal, 0), QPoint(0, -haw), QPoint(-hal, 0), QPoint(hal, 0), QPoint(-hal, 0), QPoint(0, haw)}, 0837 0838 {QPoint(0, hal), QPoint(-haw, 0), QPoint(0, hal), QPoint(0, -hal), QPoint(0, hal), QPoint(haw, 0)}}; 0839 0840 int inPos = -1; 0841 int outPos = -1; 0842 0843 if (m_stdInput && m_stdOutput) { 0844 inPos = nodeDirToPos(inOutNodePositioning[orientation][0]); 0845 outPos = nodeDirToPos(inOutNodePositioning[orientation][1]); 0846 } else if (m_stdInput) { 0847 inPos = nodeDirToPos(inNodePositioning[orientation]); 0848 } else if (m_stdOutput) { 0849 outPos = nodeDirToPos(outNodePositioning[orientation]); 0850 } 0851 0852 if (inPos != -1) { 0853 QPolygon inArrow(6); 0854 for (int i = 0; i < 6; ++i) { 0855 inArrow[i] = arrows[(inPos + 2) % 4][i]; 0856 } 0857 inArrow.translate(d[inPos].x(), d[inPos].y()); 0858 p.drawPolygon(inArrow); 0859 maskPainter.drawPolygon(inArrow); 0860 } 0861 0862 if (outPos != -1) { 0863 QPolygon outArrow(6); 0864 for (int i = 0; i < 6; ++i) { 0865 outArrow[i] = arrows[outPos][i]; 0866 } 0867 outArrow.translate(d[outPos].x(), d[outPos].y()); 0868 p.drawPolygon(outArrow); 0869 maskPainter.drawPolygon(outArrow); 0870 } 0871 } 0872 } 0873 // END painter on pm 0874 pm.setMask(mask); // pm needs not to have active painters on it 0875 } 0876 0877 #include "moc_flowpart.cpp"