You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
648 lines
14 KiB
648 lines
14 KiB
/***************************************************************************
|
|
* 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 "icndocument.h"
|
|
#include "cells.h"
|
|
#include "component.h"
|
|
#include "ecnode.h"
|
|
#include "fpnode.h"
|
|
#include "itemdocumentdata.h"
|
|
#include <kdebug.h>
|
|
|
|
#include <tqbitarray.h>
|
|
#include <tqpainter.h>
|
|
|
|
#include <cmath>
|
|
|
|
// Degrees per radian
|
|
const double DPR = 57.29577951308232087665461840231273527024;
|
|
|
|
|
|
CNItem::CNItem( ICNDocument *icnDocument, bool newItem, const TQString &id )
|
|
: Item( icnDocument, newItem, id ),
|
|
CIWidgetMgr( icnDocument->canvas(), this ),
|
|
p_icnDocument(icnDocument),
|
|
b_pointsAdded(false)
|
|
{
|
|
setZ( ICNDocument::Z::Item );
|
|
setSelected(false);
|
|
|
|
m_brushCol = TQColor( 0xf7, 0xf7, 0xff );
|
|
m_selectedCol = TQColor( 101, 134, 192 );
|
|
|
|
setBrush(m_brushCol);
|
|
setPen( TQt::black );
|
|
}
|
|
|
|
CNItem::~CNItem()
|
|
{
|
|
const TextMap::iterator textMapEnd = m_textMap.end();
|
|
for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it )
|
|
{
|
|
if (it.data())
|
|
it.data()->setCanvas(0l);
|
|
delete (Text*)it.data();
|
|
}
|
|
m_textMap.clear();
|
|
|
|
updateConnectorPoints(false);
|
|
}
|
|
|
|
|
|
int CNItem::rtti() const
|
|
{
|
|
return ItemDocument::RTTI::CNItem;
|
|
}
|
|
|
|
|
|
bool CNItem::preResize( TQRect sizeRect )
|
|
{
|
|
if ( (std::abs((double)sizeRect.width()) < minimumSize().width()) ||
|
|
(std::abs((double)sizeRect.height()) < minimumSize().height()) )
|
|
return false;
|
|
|
|
updateConnectorPoints(false);
|
|
return true;
|
|
}
|
|
|
|
|
|
void CNItem::postResize()
|
|
{
|
|
updateAttachedPositioning();
|
|
}
|
|
|
|
|
|
void CNItem::setVisible( bool yes )
|
|
{
|
|
if (b_deleted)
|
|
{
|
|
Item::setVisible(false);
|
|
return;
|
|
}
|
|
|
|
Item::setVisible(yes);
|
|
|
|
const TextMap::iterator textMapEnd = m_textMap.end();
|
|
for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it )
|
|
{
|
|
it.data()->setVisible(yes);
|
|
}
|
|
|
|
const NodeMap::iterator nodeMapEnd = m_nodeMap.end();
|
|
for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it )
|
|
{
|
|
it.data().node->setVisible(yes);
|
|
}
|
|
|
|
CNItem::setDrawWidgets(yes);
|
|
|
|
if (!yes)
|
|
updateConnectorPoints(false);
|
|
}
|
|
|
|
|
|
void CNItem::setInitialPos( const TQPoint &pos )
|
|
{
|
|
m_offset = pos - TQPoint( (int)x(), (int)y() );
|
|
}
|
|
|
|
|
|
void CNItem::reparented( Item *oldParent, Item *newParent )
|
|
{
|
|
Item::reparented( oldParent, newParent );
|
|
updateNodeLevels();
|
|
}
|
|
|
|
|
|
void CNItem::updateNodeLevels()
|
|
{
|
|
int l = level();
|
|
|
|
// Tell our nodes about our level
|
|
const NodeMap::iterator nodeMapEnd = m_nodeMap.end();
|
|
for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it )
|
|
{
|
|
it.data().node->setLevel(l);
|
|
}
|
|
|
|
const ItemList::iterator end = m_children.end();
|
|
for ( ItemList::iterator it = m_children.begin(); it != end; ++it )
|
|
{
|
|
if ( CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it) )
|
|
cnItem->updateNodeLevels();
|
|
}
|
|
}
|
|
|
|
|
|
ConnectorList CNItem::connectorList()
|
|
{
|
|
ConnectorList list;
|
|
|
|
const NodeMap::iterator nodeMapEnd = m_nodeMap.end();
|
|
for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it )
|
|
{
|
|
Node *node = p_icnDocument->nodeWithID(it.data().id);
|
|
if (node)
|
|
{
|
|
ConnectorList nodeList = node->inputConnectorList();
|
|
ConnectorList::iterator end = nodeList.end();
|
|
for ( ConnectorList::iterator it = nodeList.begin(); it != end; ++it )
|
|
{
|
|
if ( *it && !list.contains(*it) )
|
|
{
|
|
list.append(*it);
|
|
}
|
|
}
|
|
nodeList = node->outputConnectorList();
|
|
end = nodeList.end();
|
|
for ( ConnectorList::iterator it = nodeList.begin(); it != end; ++it )
|
|
{
|
|
if ( *it && !list.contains(*it) )
|
|
{
|
|
list.append(*it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
|
|
void CNItem::removeItem()
|
|
{
|
|
if (b_deleted)
|
|
return;
|
|
|
|
const TextMap::iterator textMapEnd = m_textMap.end();
|
|
for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it )
|
|
it.data()->setCanvas(0l);
|
|
|
|
Item::removeItem();
|
|
updateConnectorPoints(false);
|
|
}
|
|
|
|
|
|
void CNItem::restoreFromItemData( const ItemData &itemData )
|
|
{
|
|
Item::restoreFromItemData(itemData);
|
|
|
|
updateConnectorPoints(false);
|
|
|
|
{
|
|
const BoolMap::const_iterator end = itemData.buttonMap.end();
|
|
for ( BoolMap::const_iterator it = itemData.buttonMap.begin(); it != end; ++it )
|
|
{
|
|
Button *b = button(it.key());
|
|
if (b)
|
|
b->setState(it.data());
|
|
}
|
|
}
|
|
{
|
|
const IntMap::const_iterator end = itemData.sliderMap.end();
|
|
for ( IntMap::const_iterator it = itemData.sliderMap.begin(); it != end; ++it )
|
|
{
|
|
Slider *s = slider(it.key());
|
|
if (s)
|
|
s->setValue(it.data());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ItemData CNItem::itemData() const
|
|
{
|
|
ItemData itemData = Item::itemData();
|
|
|
|
const WidgetMap::const_iterator end = m_widgetMap.end();
|
|
for ( WidgetMap::const_iterator it = m_widgetMap.begin(); it != end; ++it )
|
|
{
|
|
if ( Slider *slider = dynamic_cast<Slider*>(*it) )
|
|
itemData.sliderMap[slider->id()] = slider->value();
|
|
|
|
else if ( Button *button = dynamic_cast<Button*>(*it) )
|
|
itemData.buttonMap[button->id()] = button->state();
|
|
|
|
}
|
|
|
|
return itemData;
|
|
}
|
|
|
|
|
|
Node* CNItem::createNode( double _x, double _y, int orientation, const TQString &name, uint type )
|
|
{
|
|
orientation %= 360;
|
|
if ( orientation < 0 )
|
|
orientation += 360;
|
|
|
|
Node::node_dir dir;
|
|
|
|
if ( orientation == 0 ) dir = Node::dir_right;
|
|
else if ( orientation == 90 ) dir = Node::dir_down;
|
|
else if ( orientation == 180 ) dir = Node::dir_left;
|
|
else if ( orientation == 270 ) dir = Node::dir_up;
|
|
else
|
|
{
|
|
kdError() << k_funcinfo << "Unknown orientation: " << orientation << endl;
|
|
return 0l;
|
|
}
|
|
|
|
Node *node;
|
|
if ( (type == Node::ec_pin) || (type == Node::ec_junction) )
|
|
{
|
|
node = new ECNode( p_icnDocument, Node::node_type(type), dir, TQPoint( 0, 0 ) );
|
|
}
|
|
else
|
|
{
|
|
node = new FPNode( p_icnDocument, Node::node_type(type), dir, TQPoint( 0, 0 ) );
|
|
}
|
|
node->setLevel( level() );
|
|
|
|
node->setParentItem(this);
|
|
node->setChildId(name);
|
|
|
|
NodeInfo info;
|
|
info.id = node->id();
|
|
info.node = node;
|
|
info.x = _x;
|
|
info.y = _y;
|
|
info.orientation = orientation;
|
|
|
|
m_nodeMap[name] = info;
|
|
|
|
updateAttachedPositioning();
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
bool CNItem::removeNode( const TQString &name )
|
|
{
|
|
NodeMap::iterator it = m_nodeMap.find(name);
|
|
if ( it == m_nodeMap.end() ) {
|
|
return false;
|
|
}
|
|
it.data().node->removeNode();
|
|
p_icnDocument->flushDeleteList();
|
|
m_nodeMap.erase(it);
|
|
return true;
|
|
}
|
|
|
|
|
|
Node *CNItem::getClosestNode( const TQPoint &pos )
|
|
{
|
|
// Work through the nodes, finding the one closest to the (x, y) position
|
|
Node *shortestNode = 0L;
|
|
double shortestDistance = 1e10; // Nice large distance :-)
|
|
|
|
const NodeMap::iterator end = m_nodeMap.end();
|
|
for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it )
|
|
{
|
|
Node *node = p_icnDocument->nodeWithID(it.data().id);
|
|
if (node)
|
|
{
|
|
// Calculate the distance
|
|
// Yeah, it's actually the distance squared; but it's all relative, so doesn't matter
|
|
double distance = std::pow(node->x()-pos.x(),2) + std::pow(node->y()-pos.y(),2);
|
|
|
|
if ( distance < shortestDistance )
|
|
{
|
|
shortestDistance = distance;
|
|
shortestNode = node;
|
|
}
|
|
}
|
|
}
|
|
|
|
return shortestNode;
|
|
}
|
|
|
|
|
|
void CNItem::updateAttachedPositioning()
|
|
{
|
|
if (b_deleted)
|
|
return;
|
|
|
|
// Actually, we don't do anything anymore...
|
|
}
|
|
|
|
|
|
void CNItem::updateZ( int baseZ )
|
|
{
|
|
Item::updateZ(baseZ);
|
|
|
|
double _z = z();
|
|
|
|
const NodeMap::iterator nodeMapEnd = m_nodeMap.end();
|
|
for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it )
|
|
it.data().node->setZ( _z + 0.5 );
|
|
|
|
const WidgetMap::iterator widgetMapEnd = m_widgetMap.end();
|
|
for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it )
|
|
it.data()->setZ( _z + 0.5 );
|
|
|
|
const TextMap::iterator textMapEnd = m_textMap.end();
|
|
for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it )
|
|
it.data()->setZ( _z + 0.5 );
|
|
}
|
|
|
|
|
|
void CNItem::snap( int newx, int newy )
|
|
{
|
|
// Ugly looking thing
|
|
// Basically means: Move item to the new position of newx-offsetx and then snap it to the 8-square-side grid
|
|
// This is in one move item call so that any attached connectors are only called once to update their routes.
|
|
moveBy( 4+newx-m_offset.x()-x()-(int)(newx-m_offset.x())%8, 4+newy-m_offset.y()-y()-(int)(newy-m_offset.y())%8 );
|
|
}
|
|
|
|
void CNItem::moveBy( double dx, double dy )
|
|
{
|
|
if ( dx == 0 && dy == 0 ) return;
|
|
updateConnectorPoints(false);
|
|
Item::moveBy( dx, dy );
|
|
|
|
setWidgetsPos( TQPoint( int(x()), int(y()) ) );
|
|
}
|
|
|
|
|
|
bool CNItem::mousePressEvent( const EventInfo &info )
|
|
{
|
|
bool accepted = Item::mousePressEvent(info);
|
|
if (!accepted)
|
|
accepted = CIWidgetMgr::mousePressEvent(info);
|
|
if (accepted)
|
|
setChanged();
|
|
return accepted;
|
|
}
|
|
|
|
|
|
bool CNItem::mouseReleaseEvent( const EventInfo &info )
|
|
{
|
|
bool accepted = Item::mouseReleaseEvent(info);
|
|
if (!accepted)
|
|
accepted = CIWidgetMgr::mouseReleaseEvent(info);
|
|
if (accepted)
|
|
setChanged();
|
|
return accepted;
|
|
}
|
|
|
|
|
|
bool CNItem::mouseDoubleClickEvent( const EventInfo &info )
|
|
{
|
|
bool accepted = Item::mouseDoubleClickEvent(info);
|
|
if (!accepted)
|
|
accepted = CIWidgetMgr::mouseDoubleClickEvent(info);
|
|
if (accepted)
|
|
setChanged();
|
|
return accepted;
|
|
}
|
|
|
|
|
|
bool CNItem::mouseMoveEvent( const EventInfo &info )
|
|
{
|
|
bool accepted = Item::mouseMoveEvent(info);
|
|
if (!accepted)
|
|
accepted = CIWidgetMgr::mouseMoveEvent(info);
|
|
if (accepted)
|
|
setChanged();
|
|
return accepted;
|
|
}
|
|
|
|
|
|
bool CNItem::wheelEvent( const EventInfo &info )
|
|
{
|
|
bool accepted = Item::wheelEvent(info);
|
|
if (!accepted)
|
|
accepted = CIWidgetMgr::wheelEvent(info);
|
|
if (accepted)
|
|
setChanged();
|
|
return accepted;
|
|
}
|
|
|
|
|
|
void CNItem::enterEvent()
|
|
{
|
|
Item::enterEvent();
|
|
CIWidgetMgr::enterEvent();
|
|
setChanged();
|
|
}
|
|
|
|
|
|
void CNItem::leaveEvent()
|
|
{
|
|
Item::leaveEvent();
|
|
CIWidgetMgr::leaveEvent();
|
|
setChanged();
|
|
}
|
|
|
|
|
|
void CNItem::drawShape( TQPainter &p )
|
|
{
|
|
if (!isVisible())
|
|
return;
|
|
|
|
// initPainter(p);
|
|
if ( isSelected() )
|
|
p.setPen(m_selectedCol);
|
|
|
|
p.drawPolygon(areaPoints());
|
|
p.drawPolyline(areaPoints());
|
|
// deinitPainter(p);
|
|
}
|
|
|
|
|
|
void CNItem::initPainter( TQPainter &p )
|
|
{
|
|
if ( isSelected() )
|
|
p.setPen(m_selectedCol);
|
|
}
|
|
|
|
|
|
void CNItem::updateConnectorPoints( bool add )
|
|
{
|
|
if ( b_deleted || !isVisible() )
|
|
add = false;
|
|
|
|
if ( b_pointsAdded == add )
|
|
return;
|
|
|
|
b_pointsAdded = add;
|
|
|
|
Cells *cells = p_icnDocument->cells();
|
|
if (!cells) return;
|
|
|
|
const int cx = cells->width();
|
|
const int cy = cells->height();
|
|
|
|
if ( cx < 1 || cy < 1 ) {
|
|
return;
|
|
}
|
|
|
|
// Get translation matrix
|
|
// Hackish...
|
|
TQWMatrix m;
|
|
if ( Component *c = dynamic_cast<Component*>(this) )
|
|
m = c->transMatrix( c->angleDegrees(), c->flipped(), int(x()), int(y()), false );
|
|
|
|
// Convention used here: _UM = unmapped by both matrix and cell reference, _M = mapped
|
|
|
|
const TQPoint start_UM = TQPoint( int(x()+offsetX())-cellSize, int(y()+offsetY())-cellSize );
|
|
const TQPoint end_UM = start_UM + TQPoint( width()+2*cellSize, height()+2*cellSize );
|
|
|
|
const TQPoint start_M = m.map(start_UM)/cellSize;
|
|
const TQPoint end_M = m.map(end_UM)/cellSize;
|
|
|
|
|
|
int sx_M = start_M.x();
|
|
int ex_M = end_M.x();
|
|
|
|
int sy_M = start_M.y();
|
|
int ey_M = end_M.y();
|
|
|
|
|
|
// Normalise start and end points
|
|
if ( sx_M > ex_M )
|
|
{
|
|
const int temp = sx_M;
|
|
sx_M = ex_M;
|
|
ex_M = temp;
|
|
}
|
|
if ( sy_M > ey_M )
|
|
{
|
|
const int temp = sy_M;
|
|
sy_M = ey_M;
|
|
ey_M = temp;
|
|
}
|
|
|
|
ex_M++;
|
|
ey_M++;
|
|
|
|
const int mult = add ? 1 : -1;
|
|
|
|
for ( int x = sx_M; x < ex_M; x++ )
|
|
{
|
|
for ( int y = sy_M; y < ey_M; y++ )
|
|
{
|
|
if ( p_icnDocument->isValidCellReference( x, y ) )
|
|
{
|
|
if ( x != sx_M && y != sy_M && x != (ex_M-1) && y != (ey_M-1) )
|
|
{
|
|
(*cells)[x][y].CIpenalty += mult*ICNDocument::hs_item;
|
|
}
|
|
else
|
|
{
|
|
// (*cells)[x][y].CIpenalty += mult*ICNDocument::hs_item/2;
|
|
(*cells)[x][y].CIpenalty += mult*ICNDocument::hs_connector*5;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// And subtract the positions of the node on the border
|
|
NodeMap::iterator end = m_nodeMap.end();
|
|
for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it )
|
|
{
|
|
const int x = (int)((it->second.node->x()-4)/cellSize);
|
|
const int y = (int)((it->second.node->y()-4)/cellSize);
|
|
if ( p_icnDocument->isValidCellReference(x,y) ) {
|
|
(*cells)[x][y].CIpenalty -= mult*ICNDocument::hs_connector*5;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
const TextMap::iterator textMapEnd = m_textMap.end();
|
|
for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it )
|
|
{
|
|
it.data()->updateConnectorPoints(add);
|
|
}
|
|
const WidgetMap::iterator widgetMapEnd = m_widgetMap.end();
|
|
for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it )
|
|
{
|
|
it.data()->updateConnectorPoints(add);
|
|
}
|
|
}
|
|
|
|
|
|
Text* CNItem::addDisplayText( const TQString &id, const TQRect & pos, const TQString &display, bool internal, int flags )
|
|
{
|
|
Text *text = 0l;
|
|
TextMap::iterator it = m_textMap.find(id);
|
|
if ( it != m_textMap.end() )
|
|
{
|
|
// kdWarning() << "CNItem::addDisplayText: removing old text"<<endl;
|
|
delete it.data();
|
|
m_textMap.remove(it);
|
|
}
|
|
|
|
text = new Text( "", this, pos, canvas(), flags );
|
|
text->setZ( z()+(internal?0.1:-0.1) );
|
|
|
|
m_textMap[id] = text;
|
|
|
|
// Calculate the correct size
|
|
setDisplayText( id, display );
|
|
text->show();
|
|
return text;
|
|
}
|
|
|
|
|
|
void CNItem::setDisplayText( const TQString &id, const TQString &display )
|
|
{
|
|
TextMap::iterator it = m_textMap.find(id);
|
|
if ( it == m_textMap.end() )
|
|
{
|
|
kdError() << "CNItem::setDisplayText: Could not find text with id \""<<id<<"\""<<endl;
|
|
return;
|
|
}
|
|
it.data()->setText(display);
|
|
updateAttachedPositioning();
|
|
}
|
|
|
|
|
|
void CNItem::removeDisplayText( const TQString &id )
|
|
{
|
|
TextMap::iterator it = m_textMap.find(id);
|
|
if ( it == m_textMap.end() )
|
|
{
|
|
// kdError() << "CNItem::removeDisplayText: Could not find text with id \""<<id<<"\""<<endl;
|
|
return;
|
|
}
|
|
it.data()->updateConnectorPoints(false);
|
|
delete it.data();
|
|
m_textMap.remove(it);
|
|
}
|
|
|
|
|
|
TQString CNItem::nodeId( const TQString &internalNodeId )
|
|
{
|
|
NodeMap::iterator it = m_nodeMap.find(internalNodeId);
|
|
if ( it == m_nodeMap.end() ) return "";
|
|
else return it.data().id;
|
|
}
|
|
|
|
|
|
Node *CNItem::childNode( const TQString &childId )
|
|
{
|
|
return p_icnDocument->nodeWithID( nodeId(childId) );
|
|
}
|
|
|
|
|
|
NodeInfo::NodeInfo()
|
|
{
|
|
node = 0l;
|
|
x = 0.;
|
|
y = 0.;
|
|
orientation = 0;
|
|
}
|
|
|
|
|
|
#include "cnitem.moc"
|