1 /***************************************************************************
2 * Copyright (C) 2003-2005 by David Saxton *
3 * david@bluehaze.org *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10
11 #include "canvasitemparts.h"
12 #include "cells.h"
13 #include "icndocument.h"
14 #include "flowcontainer.h"
15 #include "fpnode.h"
16 #include "nodegroup.h"
17 #include "resizeoverlay.h"
18
19 #include <QIcon>
20 #include <QPainter>
21
22 #include <cmath>
23
24 const int topStrip = 24;
25 const int botStrip = 16;
26
FlowContainer(ICNDocument * _icnDocument,bool newItem,const QString & id)27 FlowContainer::FlowContainer( ICNDocument *_icnDocument, bool newItem, const QString &id )
28 : FlowPart( _icnDocument, newItem, id )
29 {
30 m_ext_in = m_int_in = m_int_out = m_ext_out = nullptr;
31 b_expanded = true;
32
33 addButton( "expandBtn", QRect( offsetX(), offsetY()+24 - 11, 22, 22 ), QIcon::fromTheme( "go-down" ), true );
34 m_rectangularOverlay = new RectangularOverlay( this, 8, 8 );
35 setSize( -160, -120, 320, 240 );
36
37
38 m_int_in = (FPNode*)createNode( width()/2, 8+topStrip, 90, "int_in", Node::fp_out );
39 m_int_out = (FPNode*)createNode( width()/2, height()-8-botStrip, 270, "int_out", Node::fp_in );
40
41 button("expandBtn")->setState(true);
42
43 updateAttachedPositioning();
44 updateNodeLevels();
45 }
46
47
~FlowContainer()48 FlowContainer::~FlowContainer()
49 {
50 }
51
52
updateNodeLevels()53 void FlowContainer::updateNodeLevels()
54 {
55 FlowPart::updateNodeLevels();
56
57 int l = level();
58
59 if (m_ext_in)
60 m_ext_in->setLevel(l);
61 if (m_ext_out)
62 m_ext_out->setLevel(l);
63
64 if (m_int_in)
65 m_int_in->setLevel(l+1);
66 if (m_int_out)
67 m_int_out->setLevel(l+1);
68 }
69
70
filterEndPartIDs(QStringList * ids)71 void FlowContainer::filterEndPartIDs( QStringList *ids )
72 {
73 // Remove *all* nodes except for very bottom one
74 if (m_int_out) {
75 ids->removeAll(m_int_out->childId());
76 }
77 if (m_ext_in) {
78 ids->removeAll(m_ext_in->childId());
79 }
80 if (m_int_in) {
81 ids->removeAll(m_int_in->childId());
82 }
83 }
84
85
createTopContainerNode()86 void FlowContainer::createTopContainerNode()
87 {
88 m_ext_in = (FPNode*)createNode( width()/2, -8, 270, "ext_in", Node::fp_in );
89 m_ext_in->setLevel( level() );
90 m_rectangularOverlay->removeTopMiddle();
91 updateAttachedPositioning();
92 }
93
94
createBotContainerNode()95 void FlowContainer::createBotContainerNode()
96 {
97 m_ext_out = (FPNode*)createNode( width()/2, height()+8, 90, "ext_out", Node::fp_out );
98 m_ext_out->setLevel( level() );
99 m_rectangularOverlay->removeBotMiddle();
100 updateAttachedPositioning();
101 }
102
103
minimumSize() const104 QSize FlowContainer::minimumSize() const
105 {
106 return QSize( 160, 64 );
107 }
108
109
drawShape(QPainter & p)110 void FlowContainer::drawShape( QPainter &p )
111 {
112 if (b_deleted)
113 return;
114
115 if ( !m_sizeRect.isValid() )
116 return;
117
118 const int _x = (int)x()+offsetX();
119 const int _y = (int)y()+offsetY();
120
121 int col = 0xef + level()*0x6;
122 if ( col > 0xff ) col = 0xff;
123 p.setBrush( QColor( col, 0xff, col ) );
124 if( b_expanded )
125 {
126 p.setPen(Qt::DotLine);
127 p.drawRoundRect( _x, _y, width(), topStrip, 1500/width(), 1500/topStrip );
128 p.drawRoundRect( _x, _y+height()-botStrip, width(), botStrip, 1500/width(), 1500/botStrip );
129 }
130 else
131 {
132 p.setPen(QPen((isSelected()?m_selectedCol:Qt::black),1,Qt::SolidLine));
133 p.drawRoundRect( _x, _y, width(), topStrip, 1500/width(), 1500/topStrip );
134 }
135
136 p.setPen( Qt::black );
137 p.setFont( font() );
138 p.drawText( QRect( 22 + _x+8, _y, width()-8, topStrip ), Qt::AlignLeft | Qt::AlignVCenter, m_caption );
139
140 if( b_expanded )
141 {
142 p.setPen(Qt::SolidLine);
143 p.setBrush( Qt::NoBrush );
144 p.drawRoundRect( _x, _y, width(), height(), 1500/width(), 1500/height() );
145 }
146 }
147
148
childAdded(Item * child)149 void FlowContainer::childAdded( Item *child )
150 {
151 if (!child)
152 return;
153
154 FlowPart::childAdded(child);
155
156 connect( this, SIGNAL(movedBy(double, double )), child, SLOT(moveBy(double, double )) );
157 child->setZ( ICNDocument::Z::Item + child->level() );
158
159 updateContainedVisibility();
160 }
161
162
childRemoved(Item * child)163 void FlowContainer::childRemoved( Item *child )
164 {
165 FlowPart::childRemoved(child);
166
167 if (!b_expanded)
168 child->setVisible(true);
169
170 disconnect( this, SIGNAL(movedBy(double, double )), child, SLOT(moveBy(double, double )) );
171 }
172
173
updateConnectorPoints(bool add)174 void FlowContainer::updateConnectorPoints( bool add )
175 {
176 if ( b_deleted || !isVisible() )
177 add = false;
178
179 if ( b_pointsAdded == add )
180 return;
181
182 b_pointsAdded = add;
183
184 Cells *cells = p_icnDocument->cells();
185 if (!cells) return;
186
187 int _x = (int)x()+offsetX();
188 int _y = (int)y()+offsetY();
189 int w = width();
190 int h = b_expanded ? height() : topStrip;
191
192 const int mult = add ? 1 : -1;
193
194 // Top strip
195 for ( int y=_y; y <_y+24; ++y )
196 {
197 for ( int x = _x; x<=_x+w; x += 8 )
198 {
199 if ( cells->haveCellContaing( x, y ) )
200 {
201 cells->cellContaining( x, y ).CIpenalty += mult*ICNDocument::hs_item;
202 }
203 }
204 }
205
206 // Bottom strip
207 for ( int y = _y+h-16; y <= _y+h; ++y )
208 {
209 for ( int x = _x; x<=_x+width(); x += 8 )
210 {
211 if ( cells->haveCellContaing( x, y ) )
212 {
213 cells->cellContaining( x, y ).CIpenalty += mult*ICNDocument::hs_item;
214 }
215 }
216 }
217
218 // Left strip
219 int x = _x;
220 for ( int y = _y+24; y<_y+h-16; y += 8 )
221 {
222 if ( cells->haveCellContaing( x, y ) )
223 {
224 cells->cellContaining( x, y ).CIpenalty += mult*ICNDocument::hs_item;
225 }
226 }
227
228 // Right strip
229 x = _x+width();
230 for ( int y = _y+24; y<_y+h-16; y += 8 )
231 {
232 if ( cells->haveCellContaing( x, y ) )
233 {
234 cells->cellContaining( x, y ).CIpenalty += mult*ICNDocument::hs_item;
235 }
236 }
237 }
238
239
setFullBounds(bool full)240 void FlowContainer::setFullBounds( bool full )
241 {
242 if ( full || !b_expanded )
243 {
244 QRect bounds = b_expanded ? m_sizeRect : QRect( m_sizeRect.x(), m_sizeRect.y(), m_sizeRect.width(), topStrip );
245 setPoints( QPolygon(bounds) );
246 return;
247 }
248
249 // qDebug() << Q_FUNC_INFO << "width="<<width()<<" height="<<height()<<endl;
250
251 QPolygon pa(10);
252 pa[0] = QPoint( 0, 0 );
253 pa[1] = QPoint( width(), 0 );
254 pa[2] = QPoint( width(), height() );
255 pa[3] = QPoint( 0, height() );
256 pa[4] = QPoint( 0, 0 );
257 pa[5] = QPoint( 8, topStrip );
258 pa[6] = QPoint( 8, height()-botStrip );
259 pa[7] = QPoint( width()-8, height()-botStrip );
260 pa[8] = QPoint( width()-8, topStrip );
261 pa[9] = QPoint( 8, topStrip );
262 pa.translate( offsetX(), offsetY() );
263 setPoints(pa);
264 }
265
266
buttonStateChanged(const QString &,bool state)267 void FlowContainer::buttonStateChanged(const QString &/*id*/, bool state)
268 {
269 setExpanded(state);
270 }
271
272
parentIsCollapsed() const273 bool FlowContainer::parentIsCollapsed() const
274 {
275 if ( !p_parentItem )
276 return false;
277
278 FlowContainer *fc = dynamic_cast<FlowContainer*>((Item*)(p_parentItem));
279 return !fc->isExpanded() || fc->parentIsCollapsed();
280 }
281
282
setSelected(bool yes)283 void FlowContainer::setSelected( bool yes )
284 {
285 if ( yes == isSelected() )
286 return;
287
288 FlowPart::setSelected(yes);
289 m_rectangularOverlay->showResizeHandles( yes && isVisible() );
290 }
291
292
setExpanded(bool expanded)293 void FlowContainer::setExpanded( bool expanded )
294 {
295 if ( b_expanded == expanded )
296 return;
297
298 updateConnectorPoints(false);
299
300 // Set this now, so that child items that we call know whether or not we actually are expanded
301 b_expanded = expanded;
302
303 updateContainedVisibility();
304 updateAttachedPositioning();
305
306 p_itemDocument->setModified(true);
307 m_rectangularOverlay->setVisible(expanded);
308 setFullBounds(false);
309
310 bool nodesMoved = (m_ext_out != nullptr);
311 if (nodesMoved)
312 p_icnDocument->requestRerouteInvalidatedConnectors();
313
314 p_icnDocument->requestStateSave();
315 }
316
317
postResize()318 void FlowContainer::postResize()
319 {
320 // qDebug() << Q_FUNC_INFO << "width="<<width()<<endl;
321 setFullBounds(false);
322 FlowPart::postResize();
323 }
324
325
updateAttachedPositioning()326 void FlowContainer::updateAttachedPositioning()
327 {
328 if (b_deleted)
329 return;
330
331 int _x = int(x())+offsetX();
332 int _y = int(y())+offsetY();
333 int w = int((std::floor(float((width()+8)/16)))*16);
334 int h = height();
335
336 if (m_ext_in)
337 m_ext_in->move( _x+w/2, _y-8 );
338
339 if (m_int_in)
340 m_int_in->move( _x+w/2, _y+8+topStrip );
341
342 if (b_expanded)
343 {
344 if (m_int_out)
345 m_int_out->move( _x+w/2, _y+h-8-botStrip );
346
347 if (m_ext_out)
348 m_ext_out->move( _x+w/2, _y+h-8+botStrip );
349 }
350 else
351 {
352 // (Note: dont really care where internal nodes are if not expanded)
353
354 if (m_ext_out)
355 m_ext_out->move( _x+w/2, _y+8+topStrip );
356 }
357
358 button("expandBtn")->setGuiPartSize( 22, 22 );
359 button("expandBtn")->move( int(x())+offsetX()+7, int(y())+offsetY()+1 );
360 }
361
362
updateContainedVisibility()363 void FlowContainer::updateContainedVisibility()
364 {
365 if (b_deleted)
366 return;
367
368 if (m_ext_in)
369 m_ext_in->setVisible( isVisible() );
370 if (m_int_in)
371 m_int_in->setVisible( isVisible() && b_expanded );
372 if (m_int_out)
373 m_int_out->setVisible( isVisible() && b_expanded );
374 if (m_ext_out)
375 m_ext_out->setVisible( isVisible() );
376
377 const ItemList::iterator cEnd = m_children.end();
378 for ( ItemList::iterator it = m_children.begin(); it != cEnd; ++it )
379 {
380 if (*it)
381 (*it)->setVisible( isVisible() && b_expanded );
382 }
383
384 m_rectangularOverlay->setVisible( isVisible() && b_expanded );
385
386 NodeGroupList hidableNodeGroups;
387 p_icnDocument->getTranslatable( children(true) += GuardedItem(this), nullptr, nullptr, &hidableNodeGroups );
388
389 NodeGroupList::iterator hngEnd = hidableNodeGroups.end();
390 for ( NodeGroupList::iterator it = hidableNodeGroups.begin(); it != hngEnd; ++it )
391 (*it)->setVisible(b_expanded);
392 }
393
394
setVisible(bool yes)395 void FlowContainer::setVisible( bool yes )
396 {
397 if (b_deleted)
398 {
399 FlowPart::setVisible(false);
400 return;
401 }
402
403 FlowPart::setVisible(yes);
404 updateContainedVisibility();
405 }
406