1
//---------------------------------------------------------------------------
3
// Project: OpenWalnut ( http://www.openwalnut.org )
5
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6
// For more information see http://www.openwalnut.org/copying
8
// This file is part of OpenWalnut.
10
// OpenWalnut is free software: you can redistribute it and/or modify
11
// it under the terms of the GNU Lesser General Public License as published by
12
// the Free Software Foundation, either version 3 of the License, or
13
// (at your option) any later version.
15
// OpenWalnut is distributed in the hope that it will be useful,
16
// but WITHOUT ANY WARRANTY; without even the implied warranty of
17
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
// GNU Lesser General Public License for more details.
20
// You should have received a copy of the GNU Lesser General Public License
21
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
23
//---------------------------------------------------------------------------
29
#include <boost/shared_ptr.hpp>
31
#include <QtGui/QStyleOptionGraphicsItem>
32
#include <QtGui/QTextCharFormat>
33
#include <QtGui/QTextCursor>
35
#include "core/common/WStringUtils.h"
37
#include "../controlPanel/WQtTreeItem.h"
39
#include "WQtNetworkArrow.h"
40
#include "WQtNetworkItem.h"
41
#include "WQtNetworkItemActivator.h"
42
#include "WQtNetworkScene.h"
43
#include "WQtNetworkEditor.h"
44
#include "WQtNetworkColors.h"
45
#include "WQtNetworkEditorGlobals.h"
47
WQtNetworkItem::WQtNetworkItem( WQtNetworkEditor *editor, boost::shared_ptr< WModule > module )
48
: QGraphicsRectItem(),
50
m_isSelected( false ),
51
m_busyIsDetermined( false ),
53
m_busyIndicatorShow( false ),
56
m_networkEditor = editor;
59
setCacheMode( DeviceCoordinateCache );
62
m_textFull = module->getName();
63
m_text = new QGraphicsTextItem( m_textFull.c_str() );
64
m_text->setParentItem( this );
65
m_text->setDefaultTextColor( Qt::white );
67
// for captions of data modules
68
boost::shared_ptr< WDataModule > dataModule;
69
dataModule = boost::shared_dynamic_cast< WDataModule >( module );
72
m_subtitleFull = dataModule->getFilename().filename().string();
76
m_subtitleFull = "Idle";
79
m_subtitle = new QGraphicsTextItem( m_subtitleFull.c_str() );
80
m_subtitle->setParentItem( this );
81
m_subtitle->setDefaultTextColor( Qt::white );
82
QFont f = m_subtitle->font();
83
f.setPointSizeF( f.pointSizeF() * 0.75 );
85
m_subtitle->setFont( f );
87
m_inPorts = QList< WQtNetworkInputPort* > ();
88
m_outPorts = QList< WQtNetworkOutputPort* > ();
91
WModule::InputConnectorList cons = module->getInputConnectors();
92
bool hasInput = cons.size();
93
for( WModule::InputConnectorList::const_iterator iter = cons.begin(); iter != cons.end(); ++iter )
95
WQtNetworkInputPort *port = new WQtNetworkInputPort( *iter );
96
port->setParentItem( this );
97
this->addInputPort( port );
101
WModule::OutputConnectorList outCons = module->getOutputConnectors();
102
bool hasOutput = outCons.size();
103
for( WModule::OutputConnectorList::const_iterator iter = outCons.begin(); iter != outCons.end(); ++iter )
105
WQtNetworkOutputPort *port = new WQtNetworkOutputPort( *iter );
106
port->setParentItem( this );
107
this->addOutputPort( port );
110
// Standard processing modules with in- and outputs are colored this way:
111
m_itemColor = WQtNetworkColors::Module;
112
if( !hasInput && !hasOutput )
114
// neither inputs nor outputs
115
m_itemColor = WQtNetworkColors::StandaloneModule;
119
// no inputs -> source
120
m_itemColor = WQtNetworkColors::SourceModule;
122
else if( !hasOutput )
124
// no outputs but inputs -> sink
125
m_itemColor = WQtNetworkColors::SinkModule;
128
m_hidden = new WQtNetworkItemActivator( m_module );
129
m_hidden->setParentItem( this );
134
// this now calculated the optimal size. We keep them for later use
135
m_itemBestWidth = boundingRect().width();
140
WQtNetworkItem::~WQtNetworkItem()
142
foreach( WQtNetworkPort *port, m_inPorts )
147
foreach( WQtNetworkPort *port, m_outPorts )
155
int WQtNetworkItem::type() const
160
void WQtNetworkItem::updater()
162
// it is very important to avoid unnecessary changes to pen/brush and similar stuff to avoid permanent updates of the graphics item.
163
bool needUpdate = m_forceUpdate;
164
m_forceUpdate = false;
166
// progress indication is only needed for running modules
167
if( m_currentState != Crashed )
169
// handle progress indication
170
boost::shared_ptr< WProgressCombiner> p = m_module->getRootProgressCombiner();
172
// update the progress combiners internal state
177
m_busyIndicatorShow = true;
178
m_busyIsDetermined = p->isDetermined();
181
m_subtitleFull = p->getCombinedNames( true );
182
if( m_subtitleFull.empty() ) // if some lazy programmer did not provide names for the progress -> set one
184
m_subtitleFull = "Busy";
187
// we add the percent-counter to the front because the fitLook method shortens the subtext string if it is too long. This might clip out
188
// the percentage if the p->getCombinedNames string is quite long.
189
if(m_busyIsDetermined ) // <- of course only add if we have a known percentage
191
// NOTE: Percentage of a WProgressCombiner always multiplicatively combines all percentages of the children
192
m_subtitleFull = string_utils::toString( static_cast< uint16_t >( p->getProgress() ) ) + "% - " + m_subtitleFull;
195
// this method ensures the text is shortened and correctly placed in the iem
196
fitLook( m_itemBestWidth, m_itemBestWidth );
199
if( m_busyIsDetermined )
201
m_busyPercent = p->getProgress() / 100.0;
205
m_busyPercent += 0.025;
206
if( m_busyPercent > 1.0 )
215
// if busy indication was active -> update to remove it again
216
needUpdate |= m_busyIndicatorShow;
217
m_busyIndicatorShow = false;
218
WDataModule::SPtr dataModule = boost::shared_dynamic_cast< WDataModule >( m_module );
221
m_subtitleFull = dataModule->getFilename().filename().string();
225
m_subtitleFull = "Idle";
227
fitLook( m_itemBestWidth, m_itemBestWidth );
231
// show crash state as text too
232
if( ( m_currentState == Crashed ) && ( m_subtitleFull != "Error" ) )
234
m_subtitleFull = "Error";
235
// this method ensures the text is shortened and correctly placed in the iem
236
fitLook( m_itemBestWidth, m_itemBestWidth );
241
setToolTip( WQtTreeItem::createTooltip( m_module ).c_str() );
243
// if something has changed -> update
250
void WQtNetworkItem::hoverEnterEvent( QGraphicsSceneHoverEvent *event )
257
void WQtNetworkItem::hoverLeaveEvent( QGraphicsSceneHoverEvent *event )
264
void WQtNetworkItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* w )
266
// This is the default appearance
267
QPen newPen = QPen( m_itemColor, 1, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin );
268
QColor fillColor = m_itemColor;
269
// change appearance due to state changes
270
switch( m_currentState )
273
fillColor = m_itemColor.darker( 300 );
276
fillColor = WQtNetworkColors::ModuleCrashed;
287
fillColor = fillColor.lighter();
292
newPen = QPen( Qt::black, 2, Qt::DotLine, Qt::SquareCap, Qt::RoundJoin );
295
// only set brush and pen if they have changed
296
QBrush newBrush = QBrush( fillColor );
297
if( newBrush != brush() )
299
setBrush( newBrush );
301
if( newPen != pen() )
306
QStyleOptionGraphicsItem *o = const_cast<QStyleOptionGraphicsItem*>( option );
307
o->state &= ~QStyle::State_Selected;
308
QGraphicsRectItem::paint( painter, o, w );
310
// strike through crashed modules
311
if( m_currentState == Crashed )
313
painter->setPen( QPen( Qt::black, 1, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin ) );
314
painter->drawLine( QPoint( 0.0, 0.0 ), QPoint( m_width, m_height ) );
315
painter->drawLine( QPoint( 0.0, m_height ), QPoint( m_width, 0.0 ) );
318
// draw busy indicator
319
if( m_busyIndicatorShow )
321
float busyBarMarginX = 5.0;
322
float busyIndicatorHeight = 2.0;
323
painter->setPen( QPen( WQtNetworkColors::BusyIndicatorBackground, busyIndicatorHeight, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin ) );
324
painter->drawLine( QPoint( busyBarMarginX, m_height / 2.0 ), QPoint( m_width - busyBarMarginX, m_height / 2.0 ) );
325
painter->setPen( QPen( WQtNetworkColors::BusyIndicator, busyIndicatorHeight, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin ) );
326
float pos = m_busyPercent * ( m_width - ( 2.0 * busyBarMarginX ) );
328
// if the progress indicator is determined (known percentage) -> draw line from 0 to pos
329
if( m_busyIsDetermined )
331
painter->drawLine( QPoint( busyBarMarginX, m_height / 2.0 ), QPoint( busyBarMarginX + pos, m_height / 2.0 ) );
335
painter->drawLine( QPoint( busyBarMarginX + pos, m_height / 2.0 ), QPoint( busyBarMarginX + pos + 5, m_height / 2.0 ) );
340
void WQtNetworkItem::mouseMoveEvent( QGraphicsSceneMouseEvent *mouseEvent )
342
QGraphicsItem::mouseMoveEvent( mouseEvent );
344
foreach( WQtNetworkPort *port, m_inPorts )
346
port->updateArrows();
348
foreach( WQtNetworkPort *port, m_outPorts )
350
port->updateArrows();
354
void WQtNetworkItem::mousePressEvent( QGraphicsSceneMouseEvent *event )
356
m_networkEditor->getScene()->clearSelection();
358
QGraphicsItem::mousePressEvent( event );
361
QVariant WQtNetworkItem::itemChange( GraphicsItemChange change, const QVariant &value )
365
case ItemSelectedHasChanged:
366
m_isSelected = isSelected();
368
case ItemPositionHasChanged:
369
foreach( WQtNetworkPort *port, m_inPorts )
371
port->updateArrows();
373
foreach( WQtNetworkPort *port, m_outPorts )
375
port->updateArrows();
381
return QGraphicsItem::itemChange( change, value );
384
void WQtNetworkItem::addInputPort( WQtNetworkInputPort *port )
386
m_inPorts.append( port );
389
void WQtNetworkItem::addOutputPort( WQtNetworkOutputPort *port )
391
m_outPorts.append( port );
394
QList< WQtNetworkInputPort *> WQtNetworkItem::getInPorts()
399
QList< WQtNetworkOutputPort *> WQtNetworkItem::getOutPorts()
404
WNetworkLayoutNode * WQtNetworkItem::getLayoutNode()
410
* This function cuts away some text and attaches "..." to ensure a maximum width.
412
* \param item the item to clip
413
* \param maxWidth the maximum width. After this function finished, the item is <=maxWidth.
414
* \param fullText the original full text
416
void clipText( QGraphicsTextItem* item, float maxWidth, std::string fullText )
418
item->setPlainText( fullText.c_str() );
419
//item->adjustSize();
422
float w = item->boundingRect().width();
423
std::string newText = fullText;
425
// as long as the width is too large, cut away some letters
426
while( w > maxWidth )
429
newText = newText.substr( 0, newText.length() - 1 );
430
item->setPlainText( ( newText + "..." ).c_str() );
431
// and measure new size
432
w = item->boundingRect().width();
436
void WQtNetworkItem::fitLook( float maximumWidth, float minimumWidth )
438
// The purpose of this method is to ensure proper dimensions of the item and the contained text. This method ensures:
439
// * an item maximum size is WNETWORKITEM_MINIMUM_WIDTH or the width of the connectors!
440
// * text exceeding size limits is cut
442
m_width = minimumWidth;
443
m_height = WNETWORKITEM_MINIMUM_HEIGHT;
445
// we need to respect the size minimally needed by ports
446
float portWidth = WQtNetworkPort::getMultiplePortWidth( std::max( m_outPorts.size(), m_inPorts.size() ) );
448
// the item needs a maximum size constraint to avoid enormously large items
449
// NOTE: the specified size max can only be overwritten by the
450
float maxWidth = std::max( static_cast< float >( maximumWidth ), portWidth );
452
// the width of the text elements
453
float textWidth = 0.0;
454
float textHeight = 0.0;
456
// the width and height of the subtext elements
457
float subtextWidth = 0.0;
458
float subtextHeight = 0.0;
459
float subtextMargin = 0.0; // the margin between text and subtext
461
// 1: query sizes of sub elements
464
textWidth = static_cast< float >( m_text->boundingRect().width() );
465
textHeight = static_cast< float >( m_text->boundingRect().height() );
467
if( m_subtitle != 0 )
469
subtextWidth = static_cast< float >( m_subtitle->boundingRect().width() );
470
subtextHeight = static_cast< float >( m_subtitle->boundingRect().height() );
471
subtextMargin = 1.0f * WNETWORKITEM_MARGINY;
474
// and another height: the height of text and subtext
475
float wholeTextHeight = textHeight + subtextHeight + subtextMargin;
477
// get the required width and height
478
float maxTextWidth = maxWidth - ( 2.0f * WNETWORKITEM_MARGINX );
480
// 2: limit sizes of sub elements if needed (especially the subtext)
481
if( ( m_text != 0 ) )
483
clipText( m_text, maxTextWidth, m_textFull );
485
if( ( m_subtitle != 0 ) )
487
clipText( m_subtitle, maxTextWidth, m_subtitleFull );
490
// the new text boxes now define the final sizes:
493
textWidth = static_cast< float >( m_text->boundingRect().width() );
494
textHeight = static_cast< float >( m_text->boundingRect().height() );
496
if( m_subtitle != 0 )
498
subtextWidth = static_cast< float >( m_subtitle->boundingRect().width() );
499
subtextHeight = static_cast< float >( m_subtitle->boundingRect().height() );
501
float requiredWidth = std::max( portWidth, std::max( subtextWidth, textWidth ) + ( 2.0f * WNETWORKITEM_MARGINX ) );
502
float requiredHeight = wholeTextHeight + ( 2.0f * WNETWORKITEM_MARGINY );
504
// 3: set the final sizes
505
m_height = std::max( requiredHeight, static_cast< float >( WNETWORKITEM_MINIMUM_HEIGHT ) );
506
m_width = std::min( std::max( requiredWidth, static_cast< float >( minimumWidth ) ), maxWidth );
508
QRectF rect( 0, 0, m_width, m_height );
512
// 4: use the sizes and set the positions and sizes of the text elements properly
515
qreal x = ( m_width / 2.0 ) - ( m_text->boundingRect().width() / 2.0 );
516
qreal y = ( m_height / 2.0 ) - ( wholeTextHeight / 2.0 );
517
m_text->setPos( x, y );
522
qreal x = ( m_width / 2.0 ) - ( m_subtitle->boundingRect().width() / 2.0 );
523
qreal y = ( m_height / 2.0 ) - ( subtextMargin );
524
m_subtitle->setPos( x, y );
527
// 5: handle the ports
529
foreach( WQtNetworkPort *port, m_inPorts )
531
port->alignPosition( m_inPorts.size(), portNumber, m_rect, false );
536
foreach( WQtNetworkPort *port, m_outPorts )
538
port->alignPosition( m_outPorts.size(), portNumber, m_rect, true );
543
void WQtNetworkItem::setTextItem( QGraphicsTextItem *text )
548
QString WQtNetworkItem::getText()
550
return QString::fromStdString( m_textFull );
553
void WQtNetworkItem::setCrashed()
555
changeState( Crashed );
558
void WQtNetworkItem::changeState( State state )
560
m_forceUpdate = ( m_currentState != state );
561
m_currentState = state;
565
boost::shared_ptr< WModule > WQtNetworkItem::getModule()
570
void WQtNetworkItem::activate( bool active )
572
setEnabled( active );
576
setAcceptsHoverEvents( true );
577
setFlag( QGraphicsItem::ItemIsSelectable );
578
setFlag( QGraphicsItem::ItemIsMovable );
579
changeState( m_module->isCrashed() ? Crashed : Normal );
581
if( active == false )
583
setAcceptsHoverEvents( false );
584
setFlag( QGraphicsItem::ItemIsSelectable, false );
585
setFlag( QGraphicsItem::ItemIsMovable, false );
586
changeState( Disabled );
590
bool WQtNetworkItem::advance()
592
if( m_newPos == pos() )
597
foreach( WQtNetworkPort *port, m_inPorts )
599
port->updateArrows();
601
foreach( WQtNetworkPort *port, m_outPorts )
603
port->updateArrows();