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