File indexing completed on 2024-05-19 13:33:02

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 "flowcontainer.h"
0012 #include "canvasitemparts.h"
0013 #include "cells.h"
0014 #include "fpnode.h"
0015 #include "icndocument.h"
0016 #include "nodegroup.h"
0017 #include "resizeoverlay.h"
0018 
0019 #include <QIcon>
0020 #include <QPainter>
0021 
0022 #include <cmath>
0023 
0024 #include <ktechlab_debug.h>
0025 
0026 const int topStrip = 24;
0027 const int botStrip = 16;
0028 
0029 FlowContainer::FlowContainer(ICNDocument *_icnDocument, bool newItem, const QString &id)
0030     : FlowPart(_icnDocument, newItem, id)
0031 {
0032     m_ext_in = m_int_in = m_int_out = m_ext_out = nullptr;
0033     b_expanded = true;
0034 
0035     addButton("expandBtn", QRect(offsetX(), offsetY() + 24 - 11, 22, 22), QIcon::fromTheme("go-down"), true);
0036     m_rectangularOverlay = new RectangularOverlay(this, 8, 8);
0037     setSize(-160, -120, 320, 240);
0038 
0039     m_int_in = static_cast<FPNode *>(createNode(width() / 2, 8 + topStrip, 90, "int_in", Node::fp_out));
0040     m_int_out = static_cast<FPNode *>(createNode(width() / 2, height() - 8 - botStrip, 270, "int_out", Node::fp_in));
0041 
0042     button("expandBtn")->setState(true);
0043 
0044     updateAttachedPositioning();
0045     updateNodeLevels();
0046 }
0047 
0048 FlowContainer::~FlowContainer()
0049 {
0050 }
0051 
0052 void FlowContainer::updateNodeLevels()
0053 {
0054     FlowPart::updateNodeLevels();
0055 
0056     int l = level();
0057 
0058     if (m_ext_in)
0059         m_ext_in->setLevel(l);
0060     if (m_ext_out)
0061         m_ext_out->setLevel(l);
0062 
0063     if (m_int_in)
0064         m_int_in->setLevel(l + 1);
0065     if (m_int_out)
0066         m_int_out->setLevel(l + 1);
0067 }
0068 
0069 void FlowContainer::filterEndPartIDs(QStringList *ids)
0070 {
0071     // Remove *all* nodes except for very bottom one
0072     if (m_int_out) {
0073         ids->removeAll(m_int_out->childId());
0074     }
0075     if (m_ext_in) {
0076         ids->removeAll(m_ext_in->childId());
0077     }
0078     if (m_int_in) {
0079         ids->removeAll(m_int_in->childId());
0080     }
0081 }
0082 
0083 void FlowContainer::createTopContainerNode()
0084 {
0085     m_ext_in = static_cast<FPNode *>(createNode(width() / 2, -8, 270, "ext_in", Node::fp_in));
0086     m_ext_in->setLevel(level());
0087     m_rectangularOverlay->removeTopMiddle();
0088     updateAttachedPositioning();
0089 }
0090 
0091 void FlowContainer::createBotContainerNode()
0092 {
0093     m_ext_out = static_cast<FPNode *>(createNode(width() / 2, height() + 8, 90, "ext_out", Node::fp_out));
0094     m_ext_out->setLevel(level());
0095     m_rectangularOverlay->removeBotMiddle();
0096     updateAttachedPositioning();
0097 }
0098 
0099 QSize FlowContainer::minimumSize() const
0100 {
0101     return QSize(160, 64);
0102 }
0103 
0104 void FlowContainer::drawShape(QPainter &p)
0105 {
0106     if (b_deleted)
0107         return;
0108 
0109     if (!m_sizeRect.isValid())
0110         return;
0111 
0112     const int _x = int(x()) + offsetX();
0113     const int _y = int(y()) + offsetY();
0114 
0115     int col = 0xef + level() * 0x6;
0116     if (col > 0xff)
0117         col = 0xff;
0118     p.setBrush(QColor(col, 0xff, col));
0119     if (b_expanded) {
0120         p.setPen(Qt::DotLine);
0121         p.drawRoundedRect(_x, _y, width(), topStrip, 1500 / width(), 1500 / topStrip, Qt::RelativeSize);
0122         p.drawRoundedRect(_x, _y + height() - botStrip, width(), botStrip, 1500 / width(), 1500 / botStrip, Qt::RelativeSize);
0123     } else {
0124         p.setPen(QPen((isSelected() ? m_selectedCol : Qt::black), 1, Qt::SolidLine));
0125         p.drawRoundedRect(_x, _y, width(), topStrip, 1500 / width(), 1500 / topStrip, Qt::RelativeSize);
0126     }
0127 
0128     p.setPen(Qt::black);
0129     p.setFont(font());
0130     p.drawText(QRect(22 + _x + 8, _y, width() - 8, topStrip), Qt::AlignLeft | Qt::AlignVCenter, m_caption);
0131 
0132     if (b_expanded) {
0133         p.setPen(Qt::SolidLine);
0134         p.setBrush(Qt::NoBrush);
0135         p.drawRoundedRect(_x, _y, width(), height(), 1500 / width(), 1500 / height(), Qt::RelativeSize);
0136     }
0137 }
0138 
0139 void FlowContainer::childAdded(Item *child)
0140 {
0141     if (!child)
0142         return;
0143 
0144     FlowPart::childAdded(child);
0145 
0146     connect(this, &FlowContainer::moveBy, child, &Item::moveBy);
0147     child->setZ(ICNDocument::Z::Item + child->level());
0148 
0149     updateContainedVisibility();
0150 }
0151 
0152 void FlowContainer::childRemoved(Item *child)
0153 {
0154     FlowPart::childRemoved(child);
0155 
0156     if (!b_expanded)
0157         child->setVisible(true);
0158 
0159     disconnect(this, SIGNAL(movedBy(double, double)), child, SLOT(moveBy(double, double)));
0160 }
0161 
0162 void FlowContainer::updateConnectorPoints(bool add)
0163 {
0164     if (b_deleted || !isVisible())
0165         add = false;
0166 
0167     if (b_pointsAdded == add)
0168         return;
0169 
0170     b_pointsAdded = add;
0171 
0172     Cells *cells = p_icnDocument->cells();
0173     if (!cells)
0174         return;
0175 
0176     int _x = int(x()) + offsetX();
0177     int _y = int(y()) + offsetY();
0178     int w = width();
0179     int h = b_expanded ? height() : topStrip;
0180 
0181     const int mult = add ? 1 : -1;
0182 
0183     // Top strip
0184     for (int y = _y; y < _y + 24; ++y) {
0185         for (int x = _x; x <= _x + w; x += 8) {
0186             if (cells->haveCellContaing(x, y)) {
0187                 cells->cellContaining(x, y).CIpenalty += mult * ICNDocument::hs_item;
0188             }
0189         }
0190     }
0191 
0192     // Bottom strip
0193     for (int y = _y + h - 16; y <= _y + h; ++y) {
0194         for (int x = _x; x <= _x + width(); x += 8) {
0195             if (cells->haveCellContaing(x, y)) {
0196                 cells->cellContaining(x, y).CIpenalty += mult * ICNDocument::hs_item;
0197             }
0198         }
0199     }
0200 
0201     // Left strip
0202     int x = _x;
0203     for (int y = _y + 24; y < _y + h - 16; y += 8) {
0204         if (cells->haveCellContaing(x, y)) {
0205             cells->cellContaining(x, y).CIpenalty += mult * ICNDocument::hs_item;
0206         }
0207     }
0208 
0209     // Right strip
0210     x = _x + width();
0211     for (int y = _y + 24; y < _y + h - 16; y += 8) {
0212         if (cells->haveCellContaing(x, y)) {
0213             cells->cellContaining(x, y).CIpenalty += mult * ICNDocument::hs_item;
0214         }
0215     }
0216 }
0217 
0218 void FlowContainer::setFullBounds(bool full)
0219 {
0220     if (full || !b_expanded) {
0221         QRect bounds = b_expanded ? m_sizeRect : QRect(m_sizeRect.x(), m_sizeRect.y(), m_sizeRect.width(), topStrip);
0222         setPoints(QPolygon(bounds));
0223         return;
0224     }
0225 
0226     //  qCDebug(KTL_LOG) << "width="<<width()<<" height="<<height();
0227 
0228     QPolygon pa(10);
0229     pa[0] = QPoint(0, 0);
0230     pa[1] = QPoint(width(), 0);
0231     pa[2] = QPoint(width(), height());
0232     pa[3] = QPoint(0, height());
0233     pa[4] = QPoint(0, 0);
0234     pa[5] = QPoint(8, topStrip);
0235     pa[6] = QPoint(8, height() - botStrip);
0236     pa[7] = QPoint(width() - 8, height() - botStrip);
0237     pa[8] = QPoint(width() - 8, topStrip);
0238     pa[9] = QPoint(8, topStrip);
0239     pa.translate(offsetX(), offsetY());
0240     setPoints(pa);
0241 }
0242 
0243 void FlowContainer::buttonStateChanged(const QString & /*id*/, bool state)
0244 {
0245     setExpanded(state);
0246 }
0247 
0248 bool FlowContainer::parentIsCollapsed() const
0249 {
0250     if (!p_parentItem)
0251         return false;
0252 
0253     FlowContainer *fc = dynamic_cast<FlowContainer *>(static_cast<Item *>((p_parentItem)));
0254     return !fc->isExpanded() || fc->parentIsCollapsed();
0255 }
0256 
0257 void FlowContainer::setSelected(bool yes)
0258 {
0259     if (yes == isSelected())
0260         return;
0261 
0262     FlowPart::setSelected(yes);
0263     m_rectangularOverlay->showResizeHandles(yes && isVisible());
0264 }
0265 
0266 void FlowContainer::setExpanded(bool expanded)
0267 {
0268     if (b_expanded == expanded)
0269         return;
0270 
0271     updateConnectorPoints(false);
0272 
0273     // Set this now, so that child items that we call know whether or not we actually are expanded
0274     b_expanded = expanded;
0275 
0276     updateContainedVisibility();
0277     updateAttachedPositioning();
0278 
0279     p_itemDocument->setModified(true);
0280     m_rectangularOverlay->setVisible(expanded);
0281     setFullBounds(false);
0282 
0283     bool nodesMoved = (m_ext_out != nullptr);
0284     if (nodesMoved)
0285         p_icnDocument->requestRerouteInvalidatedConnectors();
0286 
0287     p_icnDocument->requestStateSave();
0288 }
0289 
0290 void FlowContainer::postResize()
0291 {
0292     //  qCDebug(KTL_LOG) << "width="<<width();
0293     setFullBounds(false);
0294     FlowPart::postResize();
0295 }
0296 
0297 void FlowContainer::updateAttachedPositioning()
0298 {
0299     if (b_deleted)
0300         return;
0301 
0302     int _x = int(x()) + offsetX();
0303     int _y = int(y()) + offsetY();
0304     int w = int((std::floor(float((width() + 8) / 16))) * 16);
0305     int h = height();
0306 
0307     if (m_ext_in)
0308         m_ext_in->move(_x + w / 2, _y - 8);
0309 
0310     if (m_int_in)
0311         m_int_in->move(_x + w / 2, _y + 8 + topStrip);
0312 
0313     if (b_expanded) {
0314         if (m_int_out)
0315             m_int_out->move(_x + w / 2, _y + h - 8 - botStrip);
0316 
0317         if (m_ext_out)
0318             m_ext_out->move(_x + w / 2, _y + h - 8 + botStrip);
0319     } else {
0320         // (Note: dont really care where internal nodes are if not expanded)
0321 
0322         if (m_ext_out)
0323             m_ext_out->move(_x + w / 2, _y + 8 + topStrip);
0324     }
0325 
0326     button("expandBtn")->setGuiPartSize(22, 22);
0327     button("expandBtn")->move(int(x()) + offsetX() + 7, int(y()) + offsetY() + 1);
0328 }
0329 
0330 void FlowContainer::updateContainedVisibility()
0331 {
0332     if (b_deleted)
0333         return;
0334 
0335     if (m_ext_in)
0336         m_ext_in->setVisible(isVisible());
0337     if (m_int_in)
0338         m_int_in->setVisible(isVisible() && b_expanded);
0339     if (m_int_out)
0340         m_int_out->setVisible(isVisible() && b_expanded);
0341     if (m_ext_out)
0342         m_ext_out->setVisible(isVisible());
0343 
0344     const ItemList::iterator cEnd = m_children.end();
0345     for (ItemList::iterator it = m_children.begin(); it != cEnd; ++it) {
0346         if (*it)
0347             (*it)->setVisible(isVisible() && b_expanded);
0348     }
0349 
0350     m_rectangularOverlay->setVisible(isVisible() && b_expanded);
0351 
0352     NodeGroupList hidableNodeGroups;
0353     p_icnDocument->getTranslatable(children(true) += GuardedItem(this), nullptr, nullptr, &hidableNodeGroups);
0354 
0355     NodeGroupList::iterator hngEnd = hidableNodeGroups.end();
0356     for (NodeGroupList::iterator it = hidableNodeGroups.begin(); it != hngEnd; ++it)
0357         (*it)->setVisible(b_expanded);
0358 }
0359 
0360 void FlowContainer::setVisible(bool yes)
0361 {
0362     if (b_deleted) {
0363         FlowPart::setVisible(false);
0364         return;
0365     }
0366 
0367     FlowPart::setVisible(yes);
0368     updateContainedVisibility();
0369 }
0370 
0371 #include "moc_flowcontainer.cpp"