#include "blockview.h" #include #define BLOCKITEM_DEFAULT_PIN_MARGIN 20 BlockItem::BlockItem(QGraphicsItem * parent): QGraphicsObject(parent), PropertyStorage(), g_main(this), g_selection(this) { setData(bvidType, bvitBlock); setZValue(2.); setAcceptHoverEvents(true); setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); g_main.setData(bvidMoveParent, true); g_selection.setData(bvidItemSelection, true); g_selection.setAcceptedMouseButtons(Qt::MouseButtons()); g_selection.setZValue(10.); g_selection.hide(); g_selection.setData(bvidVisualizeSelection, true); col = Qt::lightGray; _resize(QSizeF(100., 60.)); QPen p(QColor(128, 128, 255), lineThickness(), Qt::DotLine); p.setCosmetic(true); g_selection.setPen(p); g_selection.setBrush(QColor(128, 128, 255, 32)); pins_margin = BLOCKITEM_DEFAULT_PIN_MARGIN; anim_thick.setTargetObject(this); anim_thick.setPropertyName("_thickness"); anim_thick.setEasingCurve(QEasingCurve::OutQuad); anim_thick.setDuration(300); anim_sel.setTargetObject(this); anim_sel.setPropertyName("_selRect"); anim_sel.setEasingCurve(QEasingCurve::OutCubic); anim_sel.setDuration(400); t_sel.start(); } BlockItem::~BlockItem() { clearDecors(); } void BlockItem::_resize(QSizeF s) { g_main.setRect(QRectF(QPointF(), s)); g_main.setPos(QPointF(-g_main.rect().center().x(), -10.)); g_selection.setRect(enlargedRect(g_main.rect(), 0, 0, 8)); g_selection.setPos(g_main.pos()); QRadialGradient g(g_main.rect().width() / 2., 0, qMax(g_main.rect().width() / 2., g_main.rect().height())); g.setSpread(QGradient::PadSpread); g.setCoordinateMode(QGradient::LogicalMode); g.setColorAt(0., col.darker(120.)); g.setColorAt(1., col.lighter(120.)); g_main.setBrush(QBrush(g)); prepareGeometryChange(); arrangePins(); } void BlockItem::_moveToTop(bool only_decors) { qreal dy = -g_main.rect().center().y() + 10; if (!only_decors) moveBy(0., dy); foreach(QGraphicsItem * d, decors_) d->moveBy(0., -dy); } BlockItemPin * BlockItem::addPin(BlockItemPin * pin, bool update_) { pin->setParentItem(this); if (!pins_[pin->alignment()].contains(pin)) pins_[pin->alignment()] << pin; pin->parent_ = this; if (update_) arrangePins(); return pin; } BlockItemPin * BlockItem::addPin(Qt::Alignment align, int bus_type, const QString & text, bool update_) { BlockItemPin * pin = new BlockItemPin(align, bus_type, text, this); pin->parent_ = this; pins_[pin->alignment()] << pin; if (update_) arrangePins(); return pin; } void BlockItem::removePin(BlockItemPin * pin) { if (!pin) return; QMutableMapIterator> it(pins_); while (it.hasNext()) { it.next(); QVector & pv(it.value()); if (pv.contains(pin)) pv.remove(it.value().indexOf(pin)); } delete pin; arrangePins(); } void BlockItem::addDecor(QGraphicsItem * item) { if (decors_.contains(item)) return; if (qgraphicsitem_cast(item)) qgraphicsitem_cast(item)->setTransformationMode(Qt::SmoothTransformation); if (qgraphicsitem_cast(item)) qgraphicsitem_cast(item)->setData(bvidDecorText, qgraphicsitem_cast(item)->text()); if (qgraphicsitem_cast(item)) qgraphicsitem_cast(item)->setData(bvidDecorText, qgraphicsitem_cast(item)->text()); item->setData(bvidMoveParent, true); item->setData(bvidBlockDecor, true); decors_ << item; item->setParentItem(this); } void BlockItem::addDecor(QGraphicsItem & item) { if (decors_.contains(&item)) return; if (qgraphicsitem_cast(&item)) qgraphicsitem_cast(&item)->setTransformationMode(Qt::SmoothTransformation); if (qgraphicsitem_cast(&item)) qgraphicsitem_cast(&item)->setData(bvidDecorText, qgraphicsitem_cast(&item)->text()); if (qgraphicsitem_cast(&item)) qgraphicsitem_cast(&item)->setData(bvidDecorText, qgraphicsitem_cast(&item)->text()); item.setData(bvidMoveParent, true); item.setData(bvidBlockDecor, true); item.setParentItem(this); } void BlockItem::removeDecor(QGraphicsItem * item) { if (scene() && item) scene()->sendEvent(item, new QGraphicsSceneEvent(QEvent::Close)); decors_.removeAll(item); delete item; } QVector BlockItem::takePins() { QVector ret = pins(); pins_.clear(); return ret; } void BlockItem::clearPins() { QList> mp = pins_.values(); for (int i = 0; i < mp.size(); ++i) qDeleteAll(mp[i]); pins_.clear(); } void BlockItem::clearDecors() { bool pbs = false; if (scene()) pbs = scene()->blockSignals(true); if (scene()) { foreach(QGraphicsItem * i, decors_) scene()->sendEvent(i, new QGraphicsSceneEvent(QEvent::Close)); } qDeleteAll(decors_); decors_.clear(); if (scene()) { scene()->blockSignals(pbs); scene()->selectionChanged(); } } QVector BlockItem::pins() const { QList> mp = pins_.values(); QVector ret; for (int i = 0; i < mp.size(); ++i) ret << mp[i]; return ret; } QVector BlockItem::pinsOnSide(Qt::Alignment al) const { return pins_.value(al); } QByteArray BlockItem::saveModel() { ChunkStream cs; cs << cs.chunk(1, pos()) << cs.chunk(2, rotation()) << cs.chunk(3, size()) << cs.chunk(4, color()) << cs.chunk(5, pins()) << cs.chunk(6, decors_) << cs.chunk(7, pins_margin) << cs.chunk(0xFF, _blockitem_current_version_); return cs.data(); } void BlockItem::loadModel(const QByteArray & data) { // qDebug() << "load from" << data.size() << "bytes"; clearPins(); clearDecors(); col = Qt::lightGray; _resize(QSizeF(100., 60.)); if (data.isEmpty()) return; ChunkStream cs(data); QVector tp; QList dl; int version = -1; while (!cs.atEnd()) { switch (cs.read()) { case 1: /*setPos(cs.getData());*/ break; case 2: /*setRotation(cs.getData());*/ break; case 3: setSize(cs.getData()); break; case 4: setColor(cs.getData()); break; case 5: cs.get(tp); foreach(BlockItemPin * p, tp) addPin(p); break; case 6: cs.get(dl); foreach(QGraphicsItem * d, dl) addDecor(d); break; case 7: setPinsMargin(cs.getData()); break; case 0xFF: cs.get(version); break; } } if (version <= 0) _moveToTop(true); } QByteArray BlockItem::save() const { ChunkStream cs; QMap> pp; foreach(BlockItemPin * p, pins()) { // qDebug() << "save pin" << p->text() << "->" << p->properties().size(); pp[p->text()] = p->properties(); } cs << cs.chunk(1, pos()) << cs.chunk(2, rotation()) << cs.chunk(3, props) << cs.chunk(5, pp) << cs.chunk(6, size()); cs << cs.chunk(10, data(2000)) << cs.chunk(11, data(2001)) << cs.chunk(12, prop_bindings) << cs.chunk(0xFF, _blockitem_current_version_); return cs.data(); } void BlockItem::load(const QByteArray & data) { if (data.isEmpty()) return; ChunkStream cs(data); QMap> _p; int version = -1; while (!cs.atEnd()) { switch (cs.read()) { case 1: setPos(cs.getData()); break; case 2: setRotation(cs.getData()); break; case 3: cs.get(props); break; case 5: cs.get(_p); // qDebug() << "load pins" << _p.size(); foreach(BlockItemPin * p, pins()) { // qDebug() << "load pin" << p->text() << "->" << _p.contains(p->text()); if (_p.contains(p->text())) p->properties() = _p[p->text()]; } break; case 6: setSize(cs.getData()); break; case 10: setData(2000, cs.getData()); break; case 11: setData(2001, cs.getData()); break; case 12: prop_bindings = cs.getData>>(); break; case 0xFF: cs.get(version); break; } } if (version <= 0) { _moveToTop(); } } BlockItem * BlockItem::copy() const { BlockItem * ret = new BlockItem(); ret->setPos(pos()); ret->setSize(size()); ret->setColor(color()); ret->setSelected(false); ret->props = props; ret->setPinsMargin(pinsMargin()); QVector mp = pins(); foreach(BlockItemPin * p, mp) { BlockItemPin * np = new BlockItemPin(); np->setBusType(p->busType()); np->setAlignment(p->alignment()); np->setText(p->text()); np->setToolTip(p->toolTip()); np->properties() = p->properties(); ret->addPin(np); } QByteArray ba; foreach(QGraphicsItem * i, decors_) { ba.clear(); QGraphicsItem * ni = 0; QDataStream s(&ba, QIODevice::ReadWrite); s << i; QDataStream s2(ba); s2 >> ni; if (ni) ret->addDecor(ni); } return ret; } QList BlockItem::connectedBuses() const { QList ret; foreach(BlockItemPin * p, pins()) ret << p->connectedBuses(); return ret; } BlockItemPin * BlockItem::pinByText(const QString & t) const { foreach(BlockItemPin * p, pins()) if (p->text() == t) return p; return 0; } BlockItemPin * BlockItem::pinAtBus(BlockBusItem * bus) const { if (bus == 0) return 0; foreach(BlockItemPin * p, pins()) if (p->connectedBuses().contains(bus)) return p; return 0; } QRectF BlockItem::sceneRect() const { return g_main.mapRectToScene(g_main.boundingRect()); } QRectF BlockItem::boundingRect() const { return g_main.mapRectToParent(g_main.boundingRect()); } void BlockItem::hoverEnterEvent(QGraphicsSceneHoverEvent * e) { bool anim = ((BlockView *)scene()->views().back())->isBlockAnimationEnabled(); if (anim) { anim_thick.stop(); anim_thick.setStartValue(thickness()); anim_thick.setEndValue(2.5); anim_thick.start(); } else setThickness(2.5); emit blockHoverEnter(this); } void BlockItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * e) { bool anim = ((BlockView *)scene()->views().back())->isBlockAnimationEnabled(); if (anim) { anim_thick.stop(); anim_thick.setStartValue(thickness()); anim_thick.setEndValue(1); anim_thick.start(); } else setThickness(1); emit blockHoverLeave(this); } #define _POS(m) (i - ((cp.size() - 1) / 2)) * m void BlockItem::arrangePins() { QVector pl = pins(); pins_.clear(); foreach(BlockItemPin * p, pl) pins_[p->alignment()] << p; QVector cp = pins_.value(Qt::AlignBottom); for (int i = 0; i < cp.size(); ++i) cp[i]->setPos(_POS(pins_margin), bottom()); cp = pins_.value(Qt::AlignTop); for (int i = 0; i < cp.size(); ++i) cp[i]->setPos(_POS(pins_margin), top()); cp = pins_.value(Qt::AlignLeft); for (int i = 0; i < cp.size(); ++i) cp[i]->setPos(left(), i * pins_margin); cp = pins_.value(Qt::AlignRight); for (int i = 0; i < cp.size(); ++i) cp[i]->setPos(right(), i * pins_margin); } #undef _POS void BlockItem::removeBindings(const QString & bind_name) { for (int i = 0; i < prop_bindings.size(); ++i) { if (prop_bindings[i].second == bind_name) { prop_bindings.removeAt(i); i--; } } } void BlockItem::removeBindingByProperty(const QString & prop_name) { for (int i = 0; i < prop_bindings.size(); ++i) { if (prop_bindings[i].first == prop_name) { prop_bindings.removeAt(i); i--; } } } void BlockItem::addBinding(const QString & prop_name, const QString & bind_name) { QPair pb(prop_name, bind_name); prop_bindings.removeAll(pb); prop_bindings << pb; } void BlockItem::applyBinding(const QString & bind_name, const QVariant & bind_value) { for (int i = 0; i < prop_bindings.size(); ++i) { if (prop_bindings[i].second == bind_name) { setPropertyValue(prop_bindings[i].first, bind_value); } } } void BlockItem::applyBindings(const PropertyStorage & bindings) { for (int i = 0; i < prop_bindings.size(); ++i) { if (bindings.isPropertyExists(prop_bindings[i].second)) { setPropertyValue(prop_bindings[i].first, bindings.propertyValueByName(prop_bindings[i].second)); } } } void BlockItem::setBindings(const QList> & bindings) { prop_bindings = bindings; } QList> BlockItem::getBindings() { return prop_bindings; } QString BlockItem::getBindName(const QString & prop_name) const { for (int i = 0; i < prop_bindings.size(); ++i) { if (prop_bindings[i].first == prop_name) { return prop_bindings[i].second; } } return QString(); } QStringList BlockItem::getBindNames() const { QStringList ret; for (int i = 0; i < prop_bindings.size(); ++i) if (!ret.contains(prop_bindings[i].second)) ret << prop_bindings[i].second; return ret; } QStringList BlockItem::getBindProps() const { QStringList ret; for (int i = 0; i < prop_bindings.size(); ++i) ret << prop_bindings[i].first; return ret; } QVariant BlockItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant & value) { if (change == QGraphicsItem::ItemSelectedChange) { // qDebug() << "select" << value.toBool(); if (value.toBool() && !isSelected() && ((BlockView *)scene()->views().back())->isBlockAnimationEnabled() && t_sel.elapsed() > 50) { g_selection.setRect(enlargedRect(g_main.rect(), 0, 0, 16)); anim_sel.setStartValue(selectionRect()); anim_sel.setEndValue(enlargedRect(g_main.rect(), 0, 0, 8)); anim_sel.start(); } t_sel.restart(); g_selection.setVisible(value.toBool()); } return QGraphicsItem::itemChange(change, value); } double BlockItem::thickness() const { return g_main.pen().widthF(); } void BlockItem::setThickness(double w) { QPen p = g_main.pen(); p.setWidthF(w); g_main.setPen(p); } QRectF BlockItem::selectionRect() const { return g_selection.rect(); } void BlockItem::setSelectionRect(const QRectF & r) { g_selection.setRect(r); }