diff options
Diffstat (limited to 'src/item.cpp')
-rw-r--r-- | src/item.cpp | 599 |
1 files changed, 599 insertions, 0 deletions
diff --git a/src/item.cpp b/src/item.cpp new file mode 100644 index 0000000..1cbf7e3 --- /dev/null +++ b/src/item.cpp @@ -0,0 +1,599 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * [email protected] * + * * + * 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 <qbitarray.h> +#include <qlayout.h> +#include <qtimer.h> + +const int minPrefixExp = -24; +const int maxPrefixExp = 24; +const int numPrefix = int((maxPrefixExp-minPrefixExp)/3)+1; +const QString SIprefix[] = {"y","z","a","f","p","n",QChar(0xB5),"m","","k","M","G","T","P","E","Z","Y"}; + + +Item::Item( ItemDocument *itemDocument, bool newItem, const QString &id ) + : QObject(), QCanvasPolygon( itemDocument->canvas() ) +{ + m_bDynamicContent = false; + m_bIsRaised = false; + m_bDoneCreation = false; + p_parentItem = 0l; + b_deleted = false; + p_itemDocument = itemDocument; + m_baseZ = -1; + + if ( QFontInfo(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() ); + + QCanvasPolygon::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 ) +{ + QCanvasPolygon::moveBy(dx,dy); + emit movedBy( dx, dy ); +} + + +void Item::setChanged() +{ + if (b_deleted) + return; + + if (canvas()) + canvas()->setChanged(boundingRect()); +} + + +void Item::setItemPoints( const QPointArray & pa, bool setSizeFromPoints ) +{ + m_itemPoints = pa; + if (setSizeFromPoints) + setSize( m_itemPoints.boundingRect() ); + itemPointsChanged(); +} + + +void Item::itemPointsChanged() +{ + setPoints(m_itemPoints); +} + + +void Item::setSize( QRect 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( QPointArray( 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 QStringMap::const_iterator stringEnd = itemData.dataString.end(); + for ( QStringMap::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 QColorMap::const_iterator colorEnd = itemData.dataColor.end(); + for ( QColorMap::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( QVariant( it.data(), 0 ) ); + } + + const QBitArrayMap::const_iterator rawEnd = itemData.dataRaw.end(); + for ( QBitArrayMap::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 QValueList<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; +// QString 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() ); + QFrame *frame = dlg->makeMainWidget(); + QVBoxLayout *layout = new QVBoxLayout( 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, SIGNAL( user1Clicked() ), textEdit, 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; + } + QCanvasPolygon::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, SIGNAL(removed(Item*)), this, SLOT(removeItem()) ); + oldParentItem->removeChild(this); + } + + if (newParentItem) + { + if ( newParentItem->contains(this) ); +// kdError() << k_funcinfo << "Already a child of " << newParentItem << endl; + else + { + connect( newParentItem, SIGNAL(removed(Item*)), this, 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, SIGNAL(removed(Item* )), this, 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, SIGNAL(removed(Item* )), this, 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)); +} + +QString 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 QString &_mag ) +{ + QString 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 = QChar(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 QString & id ) const +{ + Variant * variant = property(id); + return variant ? variant->value().toDouble() : 0.0; +} + + +int Item::dataInt( const QString & id ) const +{ + Variant * variant = property(id); + return variant ? variant->value().toInt() : 0; +} + + +bool Item::dataBool( const QString & id ) const +{ + Variant * variant = property(id); + return variant ? variant->value().toBool() : false; +} + + +QString Item::dataString( const QString & id ) const +{ + Variant * variant = property(id); + return variant ? variant->value().toString() : QString::null; +} + + +QColor Item::dataColor( const QString & id ) const +{ + Variant * variant = property(id); + return variant ? variant->value().toColor() : Qt::black; +} + + +Variant * Item::createProperty( const QString & id, Variant::Type::Value type ) +{ + if ( !m_variantData.contains(id) ) + { + m_variantData[id] = new Variant(type); + if (m_bDoneCreation) + connect( m_variantData[id], SIGNAL(valueChanged(QVariant,QVariant)), this, SLOT(dataChanged()) ); + } + + return m_variantData[id]; +} + + +Variant * Item::property( const QString & 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 QString & 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(), SIGNAL(valueChanged(QVariant,QVariant)), this, SLOT(dataChanged()) ); + dataChanged(); +} +//END Data stuff + +#include "item.moc" |