diff options
Diffstat (limited to 'src/flowparts/flowpart.cpp')
-rw-r--r-- | src/flowparts/flowpart.cpp | 977 |
1 files changed, 977 insertions, 0 deletions
diff --git a/src/flowparts/flowpart.cpp b/src/flowparts/flowpart.cpp new file mode 100644 index 0000000..e12213c --- /dev/null +++ b/src/flowparts/flowpart.cpp @@ -0,0 +1,977 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "connector.h" +#include "flowcodedocument.h" +#include "flowcode.h" +#include "flowpart.h" +#include "fpnode.h" +#include "itemdocument.h" +#include "itemdocumentdata.h" +#include "microsettings.h" +#include "micropackage.h" +#include "picinfo.h" +#include "pinmapping.h" +#include "variant.h" + +#include <kdebug.h> + +#include <qbitarray.h> +#include <qbitmap.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qregexp.h> + +#include <assert.h> +#include <algorithm> +#include <cmath> + +// The following arrays of numbers represent the positions of nodes in different configurations, +// with the numbers as NodeInfo::Position. + +Node::node_dir diamondNodePositioning[8][3] = { + {Node::dir_up, Node::dir_down, Node::dir_right}, + {Node::dir_up, Node::dir_down, Node::dir_left}, + {Node::dir_up, Node::dir_right,Node::dir_down}, + {Node::dir_up, Node::dir_right,Node::dir_left}, + {Node::dir_left,Node::dir_right,Node::dir_down}, + {Node::dir_left,Node::dir_right,Node::dir_up}, + {Node::dir_left,Node::dir_down, Node::dir_right}, + {Node::dir_left,Node::dir_down, Node::dir_up} }; + +Node::node_dir inOutNodePositioning[8][2] = { + {Node::dir_up,Node::dir_down}, + {Node::dir_up,Node::dir_right}, + {Node::dir_up,Node::dir_left}, + {Node::dir_right,Node::dir_right}, // (invalid) + {Node::dir_left,Node::dir_right}, + {Node::dir_left,Node::dir_down}, + {Node::dir_left,Node::dir_up}, + {Node::dir_right,Node::dir_right} }; // (invalid) + +Node::node_dir inNodePositioning[4] = {Node::dir_up,Node::dir_right,Node::dir_down,Node::dir_left}; + +Node::node_dir outNodePositioning[4] = {Node::dir_down,Node::dir_left,Node::dir_up,Node::dir_right}; + +FlowPart::FlowPart( ICNDocument *icnDocument, bool newItem, const QString &id ) + : CNItem( icnDocument, newItem, id ) +{ + icnDocument->registerItem(this); + m_pFlowCodeDocument = dynamic_cast<FlowCodeDocument*>(icnDocument); + assert( m_pFlowCodeDocument ); + + m_flowSymbol = FlowPart::ps_other; + m_orientation = 0; + m_stdInput = 0l; + m_stdOutput = 0l; + m_altOutput = 0l; + + connect( m_pFlowCodeDocument, SIGNAL(picTypeChanged()), this, SLOT(slotUpdateFlowPartVariables()) ); + connect( m_pFlowCodeDocument, SIGNAL(pinMappingsChanged()), this, SLOT(slotUpdateFlowPartVariables()) ); +} + + +FlowPart::~FlowPart() +{ + // We have to check view, as if the item is deleted before the CNItem constructor + // is called, then there will be no view + if (m_pFlowCodeDocument) + { + const VariantDataMap::iterator end = m_variantData.end(); + for ( VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it ) + { + Variant *v = it.data(); + if (v) + m_pFlowCodeDocument->varNameChanged( "", v->value().toString() ); + } + } +} + + +void FlowPart::setCaption( const QString &caption ) +{ + if ( m_flowSymbol == FlowPart::ps_other ) + { + m_caption = caption; + return; + } + + QWidget *w = new QWidget(); + QPainter p(w); + p.setFont( font() ); + const int text_width = p.boundingRect( boundingRect(), (Qt::SingleLine | Qt::AlignHCenter | Qt::AlignVCenter), caption ).width(); + p.end(); + delete w; + int width = std::max( ((int)(text_width/16))*16, 48 ); + + switch(m_flowSymbol) + { + case FlowPart::ps_call: + { + width += 48; + break; + } + case FlowPart::ps_io: + case FlowPart::ps_round: + { + width += 32; + break; + } + case FlowPart::ps_decision: + { + width += 64; + break; + } + case FlowPart::ps_process: + default: + { + width += 32; + break; + } + } + + bool hasSideConnectors = m_flowSymbol == FlowPart::ps_decision; + if ( hasSideConnectors && (width != this->width()) ) + p_icnDocument->requestRerouteInvalidatedConnectors(); + + initSymbol( m_flowSymbol, width ); + m_caption = caption; +} +void FlowPart::postResize() +{ + updateNodePositions(); + CNItem::postResize(); +} + +void FlowPart::createStdInput() +{ + m_stdInput = (FPNode*)createNode( 0, 0, Node::dir_up, "stdinput", Node::fp_in ); + updateNodePositions(); +} +void FlowPart::createStdOutput() +{ + m_stdOutput = (FPNode*)createNode( 0, 0, Node::dir_down, "stdoutput", Node::fp_out ); + updateNodePositions(); +} +void FlowPart::createAltOutput() +{ + m_altOutput = (FPNode*)createNode( 0, 0, Node::dir_right, "altoutput", Node::fp_out ); + updateNodePositions(); +} + +void FlowPart::initSymbol( FlowPart::FlowSymbol symbol, int width ) +{ + m_flowSymbol = symbol; + + switch(symbol) + { + case FlowPart::ps_other: + { + return; + } + case FlowPart::ps_call: + case FlowPart::ps_process: + { + setItemPoints( QRect( -width/2, -16, width, 24 ) ); + break; + } + + case FlowPart::ps_io: + { + // define parallelogram shape + QPointArray pa(4); + pa[0] = QPoint( -(width-10)/2, -16 ); + pa[1] = QPoint( width/2, -16 ); + pa[2] = QPoint( (width-10)/2, 8 ); + pa[3] = QPoint( -width/2, 8 ); + setItemPoints(pa); + break; + } + + case FlowPart::ps_round: + { + // define rounded rectangles as two semicricles with RP_NUM/2 points with gap inbetween + // These points are not used for drawing; merely for passing to qcanvaspolygonitem for collision detection + // If there is a better way for a rounder rectangle + collision detection, please let me know... + + int halfHeight = 12; + + // Draw semicircle + double x; + const int RP_NUM = 48; + QPointArray pa(RP_NUM); + int point = 0; + for ( double y = -1.0; y <= 1.0; y+= 4.0/(RP_NUM-2) ) + { + x = sqrt(1-y*y)*halfHeight; + pa[point] = QPoint( (int)(width+x)-halfHeight, (int)(halfHeight*y) ); + pa[RP_NUM-1-point] = QPoint ( (int)(halfHeight-x), (int)(halfHeight*y) ); + point++; + } + + pa.translate( -width/2, 4 ); + setItemPoints(pa); + break; + } + + case FlowPart::ps_decision: + { + // define rhombus + QPointArray pa(6); + pa[0] = QPoint( 0, -24 ); + pa[1] = QPoint( width/2, -6 ); + pa[2] = QPoint( width/2, 6 ); + pa[3] = QPoint( 0, 24 ); + pa[4] = QPoint( -width/2, 6 ); + pa[5] = QPoint( -width/2, -6 ); + setItemPoints(pa); + break; + } + default: kdError() << k_funcinfo << "Unknown flowSymbol: "<<symbol<<endl; + } +} + +void FlowPart::drawShape( QPainter &p ) +{ + initPainter(p); + + const double _x = int( x() + offsetX() ); + const double _y = int( y() + offsetY() ); + const double w = width(); + double h = height(); + + switch (m_flowSymbol) + { + case FlowPart::ps_other: + { + CNItem::drawShape(p); + break; + } + + case FlowPart::ps_io: + { + h--; + double roundSize = 8; + double slantIndent = 5; + + const double pi = 3.1415926536; + const double DPR = 180./pi; +// CNItem::drawShape(p); + double inner = std::atan(h/slantIndent); + double outer = pi-inner; + + int inner16 = int(16*inner*DPR); + int outer16 = int(16*outer*DPR); + + p.save(); + p.setPen( Qt::NoPen ); + p.drawPolygon( areaPoints() ); + p.restore(); + + p.drawLine( int(_x+slantIndent+roundSize/2), int(_y), int(_x+w-roundSize/2), int(_y) ); + p.drawLine( int(_x-slantIndent+w-roundSize/2), int(_y+h), int(_x+roundSize/2), int(_y+h) ); + p.drawLine( 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)) ); + 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)) ); + + p.drawArc( int(_x+slantIndent), int(_y), int(roundSize), int(roundSize), 90*16, inner16 ); + p.drawArc( int(_x+w-roundSize), int(_y), int(roundSize), int(roundSize), 270*16+inner16, outer16 ); + p.drawArc( int(_x-slantIndent+w-roundSize), int(_y+h-roundSize), int(roundSize), int(roundSize), 270*16, inner16 ); + p.drawArc( int(_x), int(_y+h-roundSize), int(roundSize), int(roundSize), 90*16+inner16, outer16) ; + break; + } + + case FlowPart::ps_decision: + { + // TODO Make the shape nice and pretty with rounded corners + CNItem::drawShape(p); + break; + } + + case FlowPart::ps_call: + { + p.drawRoundRect( int(_x), int(_y), int(w), int(h+1), int(1000./w), int(1000./h) ); + p.drawLine( int(_x+8), int(_y), int(_x+8), int(_y+h) ); + p.drawLine( int(_x+w-8), int(_y), int(_x+w-8), int(_y+h) ); + break; + } + case FlowPart::ps_process: + { + p.drawRoundRect( int(_x), int(_y), int(w), int(h+1), int(1000./w), int(1000./h) ); + break; + } + + case FlowPart::ps_round: + { + p.drawRoundRect( int(_x), int(_y), int(w), int(h+1), 30, 100 ); + break; + } + } + + p.setPen( Qt::black ); + p.setFont( font() ); + p.drawText( boundingRect(), (Qt::WordBreak | Qt::AlignHCenter | Qt::AlignVCenter), m_caption ); +} + +QString FlowPart::gotoCode( const QString& internalNodeId ) +{ + FlowPart *end = outputPart(internalNodeId); + if (!end) return ""; + return "goto "+end->id(); +} + +FlowPart* FlowPart::outputPart( const QString& internalNodeId ) +{ + Node *node = p_icnDocument->nodeWithID( nodeId(internalNodeId) ); + + FPNode *fpnode = dynamic_cast<FPNode*>(node); + if ( !fpnode || fpnode->type() == Node::fp_in ) + return 0l; + + return fpnode->outputFlowPart(); +} + +FlowPartList FlowPart::inputParts( const QString& id ) +{ + Node *node = p_icnDocument->nodeWithID(id); + + if ( FPNode *fpNode = dynamic_cast<FPNode*>(node) ) + return fpNode->inputFlowParts(); + + return FlowPartList(); +} + +FlowPartList FlowPart::inputParts() +{ + FlowPartList list; + + const NodeMap::iterator nEnd = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != nEnd; ++it ) + { + Node *node = p_icnDocument->nodeWithID( it.data().id ); + FlowPartList newList; + + if ( FPNode *fpNode = dynamic_cast<FPNode*>(node) ) + newList = fpNode->inputFlowParts(); + + const FlowPartList::iterator nlEnd = newList.end(); + for ( FlowPartList::iterator it = newList.begin(); it != nlEnd; ++it ) + { + if (*it) list.append(*it); + } + } + + return list; +} + +FlowPartList FlowPart::outputParts() +{ + FlowPartList list; + + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + FlowPart *part = outputPart( it.key() ); + if (part) list.append(part); + } + + return list; +} + + +FlowPart* FlowPart::endPart( QStringList ids, FlowPartList *previousParts ) +{ + if ( ids.empty() ) + { + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + ids.append( it.key() ); + } + filterEndPartIDs( &ids ); + } + + const bool createdList = (!previousParts); + if (createdList) { + previousParts = new FlowPartList; + } else if ( previousParts->contains(this) ) { + return 0l; + } + previousParts->append(this); + + if ( ids.empty() ) { + return 0l; + } + if ( ids.size() == 1 ) { + return outputPart( *(ids.begin()) ); + } + + typedef QValueList<FlowPartList> ValidPartsList; + ValidPartsList validPartsList; + + const QStringList::iterator idsEnd = ids.end(); + for ( QStringList::iterator it = ids.begin(); it != idsEnd; ++it ) + { + int prevLevel = level(); + FlowPartList validParts; + FlowPart *part = outputPart(*it); + while (part) + { + if ( !validParts.contains(part) ) + { + validParts.append(part); +// if ( part->level() >= level() ) { + const int _l = part->level(); + part = part->endPart( QStringList(), previousParts ); + prevLevel = _l; +// } else { +// part = 0l; +// } + } + else { + part = 0l; + } + } + if ( !validParts.empty() ) { + validPartsList.append(validParts); + } + } + + if (createdList) + { + delete previousParts; + previousParts = 0l; + } + + if ( validPartsList.empty() ) return 0l; + + FlowPartList firstList = *(validPartsList.begin()); + const FlowPartList::iterator flEnd = firstList.end(); + const ValidPartsList::iterator vplEnd = validPartsList.end(); + for ( FlowPartList::iterator it = firstList.begin(); it != flEnd; ++it ) + { + bool ok = true; + for ( ValidPartsList::iterator vplit = validPartsList.begin(); vplit != vplEnd; ++vplit ) + { + if ( !(*vplit).contains(*it) ) ok = false; + } + if (ok) return *it; + } + + return 0l; +} + + +void FlowPart::handleIfElse( FlowCode *code, const QString &case1Statement, const QString &case2Statement, + const QString &case1, const QString &case2 ) +{ + if (!code) return; + + FlowPart *stop = 0l; + FlowPart *part1 = outputPart(case1); + FlowPart *part2 = outputPart(case2); + + if ( part1 && part2 ) stop = endPart( QStringList::split( ',', case1+","+case2 ) ); + + if ( (!part1 && !part2) || (part1 == stop && part2 == stop) ) return; + + code->addStopPart(stop); + + if ( part1 && part1 != stop && code->isValidBranch(part1) ) + { + // Use the case1 statement + code->addCode( "if "+case1Statement+" then "+"\n{" ); + code->addCodeBranch(part1); + code->addCode("}"); + + if ( part2 && part2 != stop && code->isValidBranch(part2) ) + { + code->addCode( "else\n{" ); + code->addCodeBranch(part2); + code->addCode("}"); + } + } + else if ( code->isValidBranch(part2) ) + { + // Use the case2 statement + code->addCode( "if "+case2Statement+" then "+"\n{" ); + code->addCodeBranch(part2); + code->addCode("}"); + } + + code->removeStopPart(stop); + code->addCodeBranch(stop); +} + + +Variant * FlowPart::createProperty( const QString & id, Variant::Type::Value type ) +{ + if ( type != Variant::Type::Port + && type != Variant::Type::Pin + && type != Variant::Type::VarName + && type != Variant::Type::SevenSegment + && type != Variant::Type::KeyPad ) + return CNItem::createProperty( id, type ); + + Variant * v = createProperty( id, Variant::Type::String ); + v->setType(type); + + if ( type == Variant::Type::VarName ) + { + if ( MicroSettings * settings = m_pFlowCodeDocument->microSettings() ) + v->setAllowed( settings->variableNames() ); + connect( property(id), SIGNAL(valueChanged(QVariant, QVariant )), this, SLOT(varNameChanged(QVariant, QVariant )) ); + } + else + slotUpdateFlowPartVariables(); + + return v; +} + + +void FlowPart::slotUpdateFlowPartVariables() +{ + if (!m_pFlowCodeDocument) + return; + + MicroSettings *s = m_pFlowCodeDocument->microSettings(); + if (!s) + return; + + const PinMappingMap pinMappings = s->pinMappings(); + QStringList sevenSegMaps; + QStringList keyPadMaps; + PinMappingMap::const_iterator pEnd = pinMappings.end(); + for ( PinMappingMap::const_iterator it = pinMappings.begin(); it != pEnd; ++it ) + { + switch ( it.data().type() ) + { + case PinMapping::SevenSegment: + sevenSegMaps << it.key(); + break; + + case PinMapping::Keypad_4x3: + case PinMapping::Keypad_4x4: + keyPadMaps << it.key(); + break; + + case PinMapping::Invalid: + break; + } + } + + QStringList ports = s->microInfo()->package()->portNames(); + ports.sort(); + + QStringList pins = s->microInfo()->package()->pinIDs(PicPin::type_bidir | PicPin::type_input | PicPin::type_open); + pins.sort(); + + const VariantDataMap::iterator vEnd = m_variantData.end(); + for ( VariantDataMap::iterator it = m_variantData.begin(); it != vEnd; ++it ) + { + Variant * v = it.data(); + if ( !v ) + continue; + + if ( v->type() == Variant::Type::Port ) + v->setAllowed( ports ); + + else if ( v->type() == Variant::Type::Pin ) + v->setAllowed( pins ); + + else if ( v->type() == Variant::Type::SevenSegment ) + { + v->setAllowed( sevenSegMaps ); + if ( !sevenSegMaps.isEmpty() && !sevenSegMaps.contains( v->value().toString() ) ) + v->setValue( sevenSegMaps.first() ); + } + + else if ( v->type() == Variant::Type::KeyPad ) + { + v->setAllowed( keyPadMaps ); + if ( !keyPadMaps.isEmpty() && !keyPadMaps.contains( v->value().toString() ) ) + v->setValue( keyPadMaps.first() ); + } + } +} + + +void FlowPart::updateVarNames() +{ + if (!m_pFlowCodeDocument) + return; + + MicroSettings *s = m_pFlowCodeDocument->microSettings(); + if (!s) + return; + + const QStringList names = s->variableNames(); + const VariantDataMap::iterator end = m_variantData.end(); + for ( VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it ) + { + Variant *v = it.data(); + if ( v && v->type() == Variant::Type::VarName ) + v->setAllowed(names); + } +} + + +void FlowPart::varNameChanged( QVariant newValue, QVariant oldValue ) +{ + if (!m_pFlowCodeDocument) + return; + m_pFlowCodeDocument->varNameChanged( newValue.asString(), oldValue.asString() ); +} + + +inline int nodeDirToPos( Node::node_dir dir ) +{ + switch (dir) + { + case Node::dir_right: + return 0; + case Node::dir_up: + return 1; + case Node::dir_left: + return 2; + case Node::dir_down: + return 3; + } + return 0; +} + + +void FlowPart::updateAttachedPositioning( ) +{ + if (b_deleted) + return; + + //BEGIN Rearrange text if appropriate + 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 ) }; + + NodeInfo * stdOutputInfo = m_stdOutput ? &m_nodeMap["stdoutput"] : 0; + NodeInfo * altOutputInfo = m_altOutput ? &m_nodeMap["altoutput"] : 0l; + + Text *outputTrueText = m_textMap.contains("output_true") ? m_textMap["output_true"] : 0l; + Text *outputFalseText = m_textMap.contains("output_false") ? m_textMap["output_false"] : 0l; + + if ( stdOutputInfo && outputTrueText ) + outputTrueText->setOriginalRect( textPos[ nodeDirToPos( (Node::node_dir)stdOutputInfo->orientation ) ] ); + + if ( altOutputInfo && outputFalseText ) + outputFalseText->setOriginalRect( textPos[ nodeDirToPos( (Node::node_dir)altOutputInfo->orientation ) ] ); + + const TextMap::iterator textMapEnd = m_textMap.end(); + for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) + { + QRect pos = it.data()->recommendedRect(); + it.data()->move( pos.x() + x(), pos.y() + y() ); + it.data()->setGuiPartSize( pos.width(), pos.height() ); + } + //END Rearrange text if appropriate + + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + if ( !it.data().node ) + { + kdError() << k_funcinfo << "Node in nodemap is null" << endl; + continue; + } + + double nx = it.data().x; + double ny = it.data().y; + +#define round_8(x) (((x) > 0) ? int(((x)+4)/8)*8 : int(((x)-4)/8)*8) + nx = round_8(nx); + ny = round_8(ny); +#undef round_8 + + it.data().node->move( int(nx+x()), int(ny+y()) ); + it.data().node->setOrientation( (Node::node_dir)it.data().orientation ); + } +} + + +ItemData FlowPart::itemData( ) const +{ + ItemData itemData = CNItem::itemData(); + itemData.orientation = m_orientation; + return itemData; +} + + +void FlowPart::restoreFromItemData( const ItemData & itemData ) +{ + CNItem::restoreFromItemData(itemData); + if ( itemData.orientation >= 0 ) + setOrientation( uint(itemData.orientation) ); +} + + +void FlowPart::updateNodePositions() +{ + if ( m_orientation > 7 ) + { + kdWarning() << k_funcinfo << "Invalid orientation: "<<m_orientation<<endl; + return; + } + + NodeInfo * stdInputInfo = m_stdInput ? &m_nodeMap["stdinput"] : 0l; + NodeInfo * stdOutputInfo = m_stdOutput ? &m_nodeMap["stdoutput"] : 0; + NodeInfo * altOutputInfo = m_altOutput ? &m_nodeMap["altoutput"] : 0l; + + if ( m_stdInput && m_stdOutput && m_altOutput ) + { + stdInputInfo->orientation = diamondNodePositioning[m_orientation][0]; + stdOutputInfo->orientation = diamondNodePositioning[m_orientation][1]; + altOutputInfo->orientation = diamondNodePositioning[m_orientation][2]; + } + else if ( m_stdInput && m_stdOutput ) + { + stdInputInfo->orientation = inOutNodePositioning[m_orientation][0]; + stdOutputInfo->orientation = inOutNodePositioning[m_orientation][1]; + } + else if ( m_orientation < 4 ) + { + if (stdInputInfo) + stdInputInfo->orientation = inNodePositioning[m_orientation]; + else if (stdOutputInfo) + stdOutputInfo->orientation = outNodePositioning[m_orientation]; + } + else + { + kdWarning() << k_funcinfo << "Invalid orientation: "<<m_orientation<<endl; + return; + } + + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + if ( !it.data().node ) + kdError() << k_funcinfo << "Node in nodemap is null" << endl; + + else + { + switch ( it.data().orientation ) + { + case Node::dir_right: + it.data().x = offsetX()+width()+8; + it.data().y = 0; + break; + case Node::dir_up: + it.data().x = 0; + it.data().y = offsetY()-8; + break; + case Node::dir_left: + it.data().x = offsetX()-8; + it.data().y = 0; + break; + case Node::dir_down: + it.data().x = 0; + it.data().y = offsetY()+height()+8;; + break; + } + } + } + + updateAttachedPositioning(); +} + + +void FlowPart::setOrientation( uint orientation ) +{ + if ( orientation == m_orientation ) + return; + + m_orientation = orientation; + updateNodePositions(); + p_icnDocument->requestRerouteInvalidatedConnectors(); +} + + +uint FlowPart::allowedOrientations( ) const +{ + // The bit positions shown here represent whether or not that orientation is allowed, the orientation being + // what is displayed in the i'th position (0 to 3 on top, 4 to 7 on bottom) of orientation widget + + if ( m_stdInput && m_stdOutput && m_altOutput ) + return 255; + + if ( m_stdInput && m_stdOutput ) + return 119; + + if ( m_stdInput || m_stdOutput ) + return 15; + + return 0; +} + +void FlowPart::orientationPixmap( uint orientation, QPixmap & pm ) const +{ + const QSize size = pm.size(); + + if ( ! ( allowedOrientations() & ( 1 << orientation ) ) ) + { + kdWarning() << k_funcinfo << "Requesting invalid orientation of " << orientation << endl; + return; + } + + QBitmap mask( 50, 50 ); + QPainter maskPainter(&mask); + mask.fill( Qt::color0 ); + maskPainter.setBrush(Qt::color1); + maskPainter.setPen(Qt::color1); + + QPainter p(&pm); + p.setBrush(m_brushCol); + p.setPen( Qt::black ); + + // In order: right corner, top corner, left corner, bottom corner + + QPoint c[4] = { + QPoint( int(0.7*size.width()), int(0.5*size.height()) ), + QPoint( int(0.5*size.width()), int(0.4*size.height()) ), + QPoint( int(0.3*size.width()), int(0.5*size.height()) ), + QPoint( int(0.5*size.width()), int(0.6*size.height()) ) }; + + QPoint d[4]; + d[0] = c[0] + QPoint( 7, 0 ); + d[1] = c[1] + QPoint( 0, -7 ); + d[2] = c[2] + QPoint( -7, 0 ); + d[3] = c[3] + QPoint( 0, 7 ); + + if ( m_stdInput && m_stdOutput && m_altOutput ) + { + //BEGIN Draw diamond outline + QPointArray diamond(4); + for ( uint i=0; i<4; ++i ) + diamond[i] = c[i]; + + p.drawPolygon(diamond); + maskPainter.drawPolygon(diamond); + //END Draw diamond outline + + + //BEGIN Draw input + int pos0 = nodeDirToPos( diamondNodePositioning[orientation][0] ); + p.drawLine( c[pos0], d[pos0] ); + maskPainter.drawLine( c[pos0], d[pos0] ); + //END Draw input + + + //BEGIN Draw "true" output as a tick + QPointArray tick(4); + tick[0] = QPoint( -3, 0 ); + tick[1] = QPoint( 0, 2 ); + tick[2] = QPoint( 0, 2 ); + tick[3] = QPoint( 4, -2 ); + + int pos1 = nodeDirToPos( diamondNodePositioning[orientation][1] ); + tick.translate( d[pos1].x(), d[pos1].y() ); + p.drawLineSegments(tick); + maskPainter.drawLineSegments(tick); + //END Draw "true" output as a tick + + + //BEGIN Draw "false" output as a cross + QPointArray cross(4); + cross[0] = QPoint( -2, -2 ); + cross[1] = QPoint( 2, 2 ); + cross[2] = QPoint( -2, 2 ); + cross[3] = QPoint( 2, -2 ); + + int pos2 = nodeDirToPos( diamondNodePositioning[orientation][2] ); + cross.translate( d[pos2].x(), d[pos2].y() ); + p.drawLineSegments(cross); + maskPainter.drawLineSegments(cross); + //END Draw "false" output as a cross + } + + else if ( m_stdInput || m_stdOutput ) + { + p.drawRoundRect( int(0.3*size.width()), int(0.4*size.height()), int(0.4*size.width()), int(0.2*size.height()) ); + maskPainter.drawRoundRect( int(0.3*size.width()), int(0.4*size.height()), int(0.4*size.width()), int(0.2*size.height()) ); + + int hal = 5; // half arrow length + int haw = 3; // half arrow width + + QPoint arrows[4][6] = { + { QPoint( hal, 0 ), QPoint( 0, -haw ), + QPoint( hal, 0 ), QPoint( -hal, 0 ), + QPoint( hal, 0 ), QPoint( 0, haw ) }, + + { QPoint( 0, -hal ), QPoint( -haw, 0 ), + QPoint( 0, -hal ), QPoint( 0, hal ), + QPoint( 0, -hal ), QPoint( haw, 0 ) }, + + { QPoint( -hal, 0 ), QPoint( 0, -haw ), + QPoint( -hal, 0 ), QPoint( hal, 0 ), + QPoint( -hal, 0 ), QPoint( 0, haw ) }, + + { QPoint( 0, hal ), QPoint( -haw, 0 ), + QPoint( 0, hal ), QPoint( 0, -hal ), + QPoint( 0, hal ), QPoint( haw, 0 ) } }; + + int inPos = -1; + int outPos = -1; + + if ( m_stdInput && m_stdOutput ) + { + inPos = nodeDirToPos( inOutNodePositioning[orientation][0] ); + outPos = nodeDirToPos( inOutNodePositioning[orientation][1] ); + } + else if ( m_stdInput ) + { + inPos = nodeDirToPos( inNodePositioning[orientation] ); + } + else if ( m_stdOutput ) + { + outPos = nodeDirToPos( outNodePositioning[orientation] ); + } + + if ( inPos != -1 ) + { + QPointArray inArrow(6); + for ( int i=0; i<6; ++i ) + { + inArrow[i] = arrows[(inPos+2)%4][i]; + } + inArrow.translate( d[inPos].x(), d[inPos].y() ); + p.drawPolygon(inArrow); + maskPainter.drawPolygon(inArrow); + } + + if ( outPos != -1 ) + { + QPointArray outArrow(6); + for ( int i=0; i<6; ++i ) + { + outArrow[i] = arrows[outPos][i]; + } + outArrow.translate( d[outPos].x(), d[outPos].y() ); + p.drawPolygon(outArrow); + maskPainter.drawPolygon(outArrow); + } + } + + pm.setMask(mask); +} + + +#include "flowpart.moc" + + |