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.
600 lines
14 KiB
600 lines
14 KiB
/***************************************************************************
|
|
* Copyright (C) 2004-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 "itemdocument.h"
|
|
#include "itemdocumentdata.h"
|
|
#include "core/ktlconfig.h"
|
|
|
|
#include <cmath>
|
|
#include <kdebug.h>
|
|
#include <kdialogbase.h>
|
|
#include <ktextedit.h>
|
|
#include <tqbitarray.h>
|
|
#include <tqlayout.h>
|
|
#include <tqtimer.h>
|
|
|
|
const int minPrefixExp = -24;
|
|
const int maxPrefixExp = 24;
|
|
const int numPrefix = int((maxPrefixExp-minPrefixExp)/3)+1;
|
|
const TQString SIprefix[] = {"y","z","a","f","p","n",TQChar(0xB5),"m","","k","M","G","T","P","E","Z","Y"};
|
|
|
|
|
|
Item::Item( ItemDocument *itemDocument, bool newItem, const TQString &id )
|
|
: TQObject(), TQCanvasPolygon( itemDocument->canvas() )
|
|
{
|
|
m_bDynamicContent = false;
|
|
m_bIsRaised = false;
|
|
m_bDoneCreation = false;
|
|
p_parentItem = 0l;
|
|
b_deleted = false;
|
|
p_itemDocument = itemDocument;
|
|
m_baseZ = -1;
|
|
|
|
if ( TQFontInfo(m_font).pixelSize() > 11 ) // It has to be > 11, not > 12, as (I think) pixelSize() rounds off the actual size
|
|
m_font.setPixelSize(12);
|
|
|
|
if (newItem)
|
|
m_id = p_itemDocument->generateUID(id);
|
|
|
|
else
|
|
{
|
|
m_id = id;
|
|
p_itemDocument->registerUID(id);
|
|
}
|
|
}
|
|
|
|
|
|
Item::~Item()
|
|
{
|
|
p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems );
|
|
p_itemDocument->unregisterUID( id() );
|
|
|
|
TQCanvasPolygon::hide();
|
|
|
|
const VariantDataMap::iterator variantDataEnd = m_variantData.end();
|
|
for ( VariantDataMap::iterator it = m_variantData.begin(); it != variantDataEnd; ++it )
|
|
delete it.data();
|
|
m_variantData.clear();
|
|
}
|
|
|
|
|
|
void Item::removeItem()
|
|
{
|
|
if (b_deleted)
|
|
return;
|
|
b_deleted = true;
|
|
|
|
hide();
|
|
setCanvas(0l);
|
|
emit removed(this);
|
|
p_itemDocument->appendDeleteList(this);
|
|
}
|
|
|
|
|
|
void Item::moveBy( double dx, double dy )
|
|
{
|
|
TQCanvasPolygon::moveBy(dx,dy);
|
|
emit movedBy( dx, dy );
|
|
}
|
|
|
|
|
|
void Item::setChanged()
|
|
{
|
|
if (b_deleted)
|
|
return;
|
|
|
|
if (canvas())
|
|
canvas()->setChanged(boundingRect());
|
|
}
|
|
|
|
|
|
void Item::setItemPoints( const TQPointArray & pa, bool setSizeFromPoints )
|
|
{
|
|
m_itemPoints = pa;
|
|
if (setSizeFromPoints)
|
|
setSize( m_itemPoints.boundingRect() );
|
|
itemPointsChanged();
|
|
}
|
|
|
|
|
|
void Item::itemPointsChanged()
|
|
{
|
|
setPoints(m_itemPoints);
|
|
}
|
|
|
|
|
|
void Item::setSize( TQRect sizeRect, bool forceItemPoints )
|
|
{
|
|
if ( m_sizeRect == sizeRect && !forceItemPoints )
|
|
return;
|
|
|
|
if ( !preResize(sizeRect) )
|
|
return;
|
|
|
|
canvas()->setChanged(areaPoints().boundingRect());
|
|
m_sizeRect = sizeRect;
|
|
if ( m_itemPoints.isEmpty() || forceItemPoints )
|
|
{
|
|
setItemPoints( TQPointArray( m_sizeRect ), false );
|
|
}
|
|
canvas()->setChanged(areaPoints().boundingRect());
|
|
postResize();
|
|
emit resized();
|
|
}
|
|
|
|
|
|
ItemData Item::itemData() const
|
|
{
|
|
ItemData itemData;
|
|
|
|
itemData.type = m_type;
|
|
itemData.x = x();
|
|
itemData.y = y();
|
|
|
|
if ( !parentItem() )
|
|
itemData.z = m_baseZ;
|
|
|
|
itemData.size = m_sizeRect;
|
|
itemData.setSize = canResize();
|
|
|
|
if (p_parentItem)
|
|
itemData.parentId = p_parentItem->id();
|
|
|
|
const VariantDataMap::const_iterator end = m_variantData.end();
|
|
for ( VariantDataMap::const_iterator it = m_variantData.begin(); it != end; ++it )
|
|
{
|
|
switch( it.data()->type() )
|
|
{
|
|
case Variant::Type::String:
|
|
case Variant::Type::FileName:
|
|
case Variant::Type::Port:
|
|
case Variant::Type::Pin:
|
|
case Variant::Type::VarName:
|
|
case Variant::Type::Combo:
|
|
case Variant::Type::Select:
|
|
case Variant::Type::Multiline:
|
|
case Variant::Type::SevenSegment:
|
|
case Variant::Type::KeyPad:
|
|
{
|
|
itemData.dataString[it.key()] = it.data()->value().toString();
|
|
break;
|
|
}
|
|
case Variant::Type::Int:
|
|
case Variant::Type::Double:
|
|
{
|
|
itemData.dataNumber[it.key()] = it.data()->value().toDouble();
|
|
break;
|
|
}
|
|
case Variant::Type::Color:
|
|
{
|
|
itemData.dataColor[it.key()] = it.data()->value().toColor();
|
|
break;
|
|
}
|
|
case Variant::Type::Bool:
|
|
{
|
|
itemData.dataBool[it.key()] = it.data()->value().toBool();
|
|
break;
|
|
}
|
|
case Variant::Type::Raw:
|
|
{
|
|
itemData.dataRaw[it.key()] = it.data()->value().toBitArray();
|
|
break;
|
|
}
|
|
case Variant::Type::PenStyle:
|
|
case Variant::Type::PenCapStyle:
|
|
{
|
|
// These types are only created from DrawPart, and that class
|
|
// deals with these, so we can ignore them
|
|
break;
|
|
}
|
|
case Variant::Type::None:
|
|
{
|
|
// ? Maybe obsoleted data...
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return itemData;
|
|
}
|
|
|
|
|
|
void Item::restoreFromItemData( const ItemData &itemData )
|
|
{
|
|
move( itemData.x, itemData.y );
|
|
if ( canResize() )
|
|
setSize( itemData.size );
|
|
|
|
Item *parentItem = p_itemDocument->itemWithID( itemData.parentId );
|
|
if (parentItem)
|
|
setParentItem(parentItem);
|
|
else
|
|
m_baseZ = itemData.z;
|
|
|
|
//BEGIN Restore data
|
|
const TQStringMap::const_iterator stringEnd = itemData.dataString.end();
|
|
for ( TQStringMap::const_iterator it = itemData.dataString.begin(); it != stringEnd; ++it )
|
|
{
|
|
if ( hasProperty(it.key()) )
|
|
property( it.key() )->setValue( it.data() );
|
|
}
|
|
|
|
const DoubleMap::const_iterator numberEnd = itemData.dataNumber.end();
|
|
for ( DoubleMap::const_iterator it = itemData.dataNumber.begin(); it != numberEnd; ++it )
|
|
{
|
|
if ( hasProperty(it.key()) )
|
|
property( it.key() )->setValue( it.data() );
|
|
}
|
|
|
|
const TQColorMap::const_iterator colorEnd = itemData.dataColor.end();
|
|
for ( TQColorMap::const_iterator it = itemData.dataColor.begin(); it != colorEnd; ++it )
|
|
{
|
|
if ( hasProperty(it.key()) )
|
|
property( it.key() )->setValue( it.data() );
|
|
}
|
|
|
|
const BoolMap::const_iterator boolEnd = itemData.dataBool.end();
|
|
for ( BoolMap::const_iterator it = itemData.dataBool.begin(); it != boolEnd; ++it )
|
|
{
|
|
if ( hasProperty(it.key()) )
|
|
property( it.key() )->setValue( TQVariant( it.data(), 0 ) );
|
|
}
|
|
|
|
const TQBitArrayMap::const_iterator rawEnd = itemData.dataRaw.end();
|
|
for ( TQBitArrayMap::const_iterator it = itemData.dataRaw.begin(); it != rawEnd; ++it )
|
|
{
|
|
if ( hasProperty(it.key()) )
|
|
property( it.key() )->setValue( it.data() );
|
|
}
|
|
//END Restore Data
|
|
}
|
|
|
|
|
|
bool Item::mousePressEvent( const EventInfo &eventInfo )
|
|
{
|
|
Q_UNUSED(eventInfo);
|
|
return false;
|
|
}
|
|
bool Item::mouseReleaseEvent( const EventInfo &eventInfo )
|
|
{
|
|
Q_UNUSED(eventInfo);
|
|
return false;
|
|
}
|
|
bool Item::mouseMoveEvent( const EventInfo &eventInfo )
|
|
{
|
|
Q_UNUSED(eventInfo);
|
|
return false;
|
|
}
|
|
bool Item::wheelEvent( const EventInfo &eventInfo )
|
|
{
|
|
Q_UNUSED(eventInfo);
|
|
return false;
|
|
}
|
|
void Item::enterEvent()
|
|
{
|
|
}
|
|
void Item::leaveEvent()
|
|
{
|
|
}
|
|
|
|
bool Item::mouseDoubleClickEvent( const EventInfo &eventInfo )
|
|
{
|
|
Q_UNUSED(eventInfo);
|
|
|
|
typedef TQValueList<Variant*> VarPtrLst;
|
|
VarPtrLst list;
|
|
const VariantDataMap::iterator variantDataEnd = m_variantData.end();
|
|
for ( VariantDataMap::iterator it = m_variantData.begin(); it != variantDataEnd; ++it )
|
|
{
|
|
if ( it.data()->type() == Variant::Type::Multiline ) {
|
|
list.append(it.data());
|
|
}
|
|
}
|
|
if ( list.count() > 1 )
|
|
{
|
|
kdWarning() << "Item::mouseDoubleClickEvent: Can't handle more than one multiline data"<<endl;
|
|
return false;
|
|
}
|
|
else if ( list.isEmpty() )
|
|
return false;
|
|
|
|
Variant *v = *(list.at(0));
|
|
|
|
/// @todo Replace this with KInputDialog::getMultiLineText for KDE 3.3
|
|
// bool ok;
|
|
// TQString text = KInputDialog::getMultiLineText( v->caption(), "", v->getValue(), ok );
|
|
|
|
KDialogBase *dlg = new KDialogBase( 0l, "", true, v->editorCaption(), KDialogBase::Ok|KDialogBase::Cancel|KDialogBase::User1, KDialogBase::Ok, false, KStdGuiItem::clear() );
|
|
TQFrame *frame = dlg->makeMainWidget();
|
|
TQVBoxLayout *layout = new TQVBoxLayout( frame, 0, dlg->spacingHint() );
|
|
KTextEdit *textEdit = new KTextEdit( frame );
|
|
textEdit->setTextFormat( PlainText );
|
|
textEdit->setText( v->value().toString() );
|
|
layout->addWidget( textEdit, 10 );
|
|
textEdit->setFocus();
|
|
connect( dlg, TQT_SIGNAL( user1Clicked() ), textEdit, TQT_SLOT( clear() ) );
|
|
dlg->setMinimumWidth( 600 );
|
|
if ( dlg->exec() == KDialogBase::Accepted )
|
|
{
|
|
v->setValue( textEdit->text() );
|
|
dataChanged();
|
|
p_itemDocument->setModified(true);
|
|
}
|
|
delete dlg;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void Item::setSelected( bool yes )
|
|
{
|
|
if ( isSelected() == yes ) {
|
|
return;
|
|
}
|
|
TQCanvasPolygon::setSelected(yes);
|
|
yes ? (emit selected(this)) : (emit unselected(this));
|
|
}
|
|
|
|
|
|
void Item::setParentItem( Item *newParentItem )
|
|
{
|
|
// kdDebug() << k_funcinfo << "this = "<<this<<" newParentItem = "<<newParentItem<<endl;
|
|
if ( newParentItem == p_parentItem )
|
|
return;
|
|
|
|
Item *oldParentItem = p_parentItem;
|
|
|
|
if (oldParentItem)
|
|
{
|
|
disconnect( oldParentItem, TQT_SIGNAL(removed(Item*)), this, TQT_SLOT(removeItem()) );
|
|
oldParentItem->removeChild(this);
|
|
}
|
|
|
|
if (newParentItem)
|
|
{
|
|
if ( newParentItem->contains(this) );
|
|
// kdError() << k_funcinfo << "Already a child of " << newParentItem << endl;
|
|
else
|
|
{
|
|
connect( newParentItem, TQT_SIGNAL(removed(Item*)), this, TQT_SLOT(removeItem()) );
|
|
newParentItem->addChild(this);
|
|
}
|
|
}
|
|
|
|
p_parentItem = newParentItem;
|
|
(void)level();
|
|
reparented( oldParentItem, newParentItem );
|
|
p_itemDocument->slotUpdateZOrdering();
|
|
}
|
|
|
|
|
|
int Item::level() const
|
|
{
|
|
return p_parentItem ? p_parentItem->level()+1 : 0;
|
|
}
|
|
|
|
|
|
ItemList Item::children( bool includeGrandChildren ) const
|
|
{
|
|
if (!includeGrandChildren)
|
|
return m_children;
|
|
|
|
ItemList children = m_children;
|
|
ItemList::const_iterator end = m_children.end();
|
|
for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it )
|
|
{
|
|
if (!*it)
|
|
continue;
|
|
|
|
children += (*it)->children(true);
|
|
}
|
|
|
|
return children;
|
|
}
|
|
|
|
|
|
void Item::addChild( Item *child )
|
|
{
|
|
if ( !child )
|
|
return;
|
|
|
|
if ( child->contains(this) )
|
|
{
|
|
// kdError() << k_funcinfo << "Attempting to add a child to this item that is already a parent of this item. Incest results in stack overflow." << endl;
|
|
return;
|
|
}
|
|
|
|
if ( contains( child, true ) )
|
|
{
|
|
// kdError() << k_funcinfo << "Already have child " << child << endl;
|
|
return;
|
|
}
|
|
|
|
m_children.append(child);
|
|
connect( child, TQT_SIGNAL(removed(Item* )), this, TQT_SLOT(removeChild(Item* )) );
|
|
|
|
child->setParentItem(this);
|
|
childAdded(child);
|
|
p_itemDocument->slotUpdateZOrdering();
|
|
}
|
|
|
|
|
|
void Item::removeChild( Item *child )
|
|
{
|
|
if ( !child || !m_children.contains(child) )
|
|
return;
|
|
|
|
m_children.remove(child);
|
|
disconnect( child, TQT_SIGNAL(removed(Item* )), this, TQT_SLOT(removeChild(Item* )) );
|
|
|
|
childRemoved(child);
|
|
p_itemDocument->slotUpdateZOrdering();
|
|
}
|
|
|
|
|
|
bool Item::contains( Item *item, bool direct ) const
|
|
{
|
|
const ItemList::const_iterator end = m_children.end();
|
|
for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it )
|
|
{
|
|
if ( (Item*)*it == item || ( !direct && (*it)->contains( item, false ) ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void Item::setRaised( bool isRaised )
|
|
{
|
|
m_bIsRaised = isRaised;
|
|
// We'll get called later to update our Z
|
|
}
|
|
|
|
|
|
void Item::updateZ( int baseZ )
|
|
{
|
|
m_baseZ = baseZ;
|
|
double z = ItemDocument::Z::Item + (ItemDocument::Z::DeltaItem)*baseZ;
|
|
|
|
if ( isRaised() )
|
|
z += ItemDocument::Z::RaisedItem - ItemDocument::Z::Item;
|
|
|
|
setZ(z);
|
|
|
|
const ItemList::const_iterator end = m_children.end();
|
|
for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it )
|
|
{
|
|
if (*it)
|
|
(*it)->updateZ(baseZ+1);
|
|
}
|
|
}
|
|
|
|
|
|
int Item::getNumberPre( double num )
|
|
{
|
|
return (int)(num/getMultiplier(num));
|
|
}
|
|
|
|
TQString Item::getNumberMag( double num )
|
|
{
|
|
if ( num == 0. ) return "";
|
|
const double exp_n = std::log10(std::abs(num));
|
|
if ( exp_n < minPrefixExp+3 ) return SIprefix[0];
|
|
else if ( exp_n >= maxPrefixExp ) return SIprefix[numPrefix-1];
|
|
else return SIprefix[(int)std::floor((double)(exp_n/3))-(int)floor(double(minPrefixExp/3))];
|
|
}
|
|
|
|
double Item::getMultiplier( double num )
|
|
{
|
|
if ( num == 0. ) return 1.;
|
|
else return std::pow( 10, 3*std::floor(std::log10(std::abs(num))/3) );
|
|
}
|
|
|
|
double Item::getMultiplier( const TQString &_mag )
|
|
{
|
|
TQString mag;
|
|
// Allow the user to enter in "u" instead of mu, as unfortunately many keyboards don't have the mu key
|
|
if ( _mag == "u" )
|
|
mag = TQChar(0xB5);
|
|
else
|
|
mag = _mag;
|
|
|
|
for ( int i=0; i<numPrefix; ++i )
|
|
{
|
|
if ( mag == SIprefix[i] )
|
|
{
|
|
return std::pow( 10., (i*3)+minPrefixExp );
|
|
}
|
|
}
|
|
|
|
// I think it is safer to return '1' if the unit is unknown
|
|
return 1.;
|
|
// return pow( 10., maxPrefixExp+3. );
|
|
}
|
|
|
|
|
|
|
|
//BEGIN Data stuff
|
|
double Item::dataDouble( const TQString & id ) const
|
|
{
|
|
Variant * variant = property(id);
|
|
return variant ? variant->value().toDouble() : 0.0;
|
|
}
|
|
|
|
|
|
int Item::dataInt( const TQString & id ) const
|
|
{
|
|
Variant * variant = property(id);
|
|
return variant ? variant->value().toInt() : 0;
|
|
}
|
|
|
|
|
|
bool Item::dataBool( const TQString & id ) const
|
|
{
|
|
Variant * variant = property(id);
|
|
return variant ? variant->value().toBool() : false;
|
|
}
|
|
|
|
|
|
TQString Item::dataString( const TQString & id ) const
|
|
{
|
|
Variant * variant = property(id);
|
|
return variant ? variant->value().toString() : TQString();
|
|
}
|
|
|
|
|
|
TQColor Item::dataColor( const TQString & id ) const
|
|
{
|
|
Variant * variant = property(id);
|
|
return variant ? variant->value().toColor() : TQt::black;
|
|
}
|
|
|
|
|
|
Variant * Item::createProperty( const TQString & id, Variant::Type::Value type )
|
|
{
|
|
if ( !m_variantData.contains(id) )
|
|
{
|
|
m_variantData[id] = new Variant(type);
|
|
if (m_bDoneCreation)
|
|
connect( m_variantData[id], TQT_SIGNAL(valueChanged(TQVariant,TQVariant)), this, TQT_SLOT(dataChanged()) );
|
|
}
|
|
|
|
return m_variantData[id];
|
|
}
|
|
|
|
|
|
Variant * Item::property( const TQString & id ) const
|
|
{
|
|
if ( m_variantData.contains(id) )
|
|
return m_variantData[id];
|
|
|
|
kdError() << k_funcinfo << " No such property with id " << id << endl;
|
|
return 0l;
|
|
}
|
|
|
|
|
|
bool Item::hasProperty( const TQString & id ) const
|
|
{
|
|
return m_variantData.contains(id);
|
|
}
|
|
|
|
|
|
void Item::finishedCreation( )
|
|
{
|
|
m_bDoneCreation = true;
|
|
const VariantDataMap::iterator end = m_variantData.end();
|
|
for ( VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it )
|
|
connect( it.data(), TQT_SIGNAL(valueChanged(TQVariant,TQVariant)), this, TQT_SLOT(dataChanged()) );
|
|
dataChanged();
|
|
}
|
|
//END Data stuff
|
|
|
|
#include "item.moc"
|