#include "blockbusitem.h" BlockBusItem::BlockBusItem(bool temp): QGraphicsObject(), PropertyStorage() { temp_ = temp; _init(); if (!temp) setData(1005, "connection"); else hide(); } BlockBusItem::BlockBusItem(const BlockBusItem & other): QGraphicsObject(), PropertyStorage() { temp_ = false; _init(); setData(1005, "connection"); setBusType(other.busType()); max_ep = other.max_ep; pol = other.pol; segments = other.segments; updateGeometry(); } void BlockBusItem::_init() { setBusType(-1); setAcceptHoverEvents(true); ph.setColor(Qt::blue); ph.setJoinStyle(Qt::MiterJoin); bh.setColor(Qt::blue); bh.setStyle(Qt::SolidPattern); pu = pa = pr = ph; bu = ba = br = bh; grid_step = 10.; pu.setWidth(1); pu.setColor(Qt::black); bu.setColor(Qt::black); pa.setColor(Qt::darkGreen); ba.setColor(Qt::darkGreen); pr.setColor(Qt::darkRed); br.setColor(Qt::darkRed); pn.setColor(Qt::gray); pn.setStyle(Qt::DashLine); if (temp_) { pu.setStyle(Qt::DashLine); pu.setColor(Qt::darkGray); bu.setColor(Qt::darkGray); } setPen(pu); setBrush(bu); max_ep = 0; selPoint = selSegment = state_ = -1; pen_width = 2.; moved = deleted = mark_in = mark_out = new_segment = mm_cancel = lm_point = false; } void BlockBusItem::reconnect() { if (temp_) return; if (!scene()) return; if (scene()->views().isEmpty()) return; QMetaObject::invokeMethod(scene()->views().back(), "reconnectAll"); } bool BlockBusItem::sceneEvent(QEvent * e) { if (temp_) return QGraphicsObject::sceneEvent(e); switch (e->type()) { case QEvent::GraphicsSceneHoverEnter: hoverEnterEvent((QGraphicsSceneHoverEvent * )e); break; case QEvent::GraphicsSceneHoverMove: hoverMoveEvent((QGraphicsSceneHoverEvent * )e); break; case QEvent::GraphicsSceneHoverLeave: hoverLeaveEvent((QGraphicsSceneHoverEvent * )e); break; case QEvent::GraphicsSceneMousePress: mousePressEvent((QGraphicsSceneMouseEvent * )e); break; case QEvent::GraphicsSceneMouseMove: mouseMoveEvent((QGraphicsSceneMouseEvent * )e); break; case QEvent::GraphicsSceneMouseRelease: mouseReleaseEvent((QGraphicsSceneMouseEvent * )e); break; default: break; } return QGraphicsObject::sceneEvent(e); } int BlockBusItem::addPoint(const QPointF & point, bool update) { int ei = pol.indexOf(point); if (ei >= 0) return ei; if (selSegment < 0 || selSegment >= pol.size() - 1) return -1; pol << quantize(nearestPointOnLine(pol[segments[selSegment].first], pol[segments[selSegment].second], point), grid_step); selPoint = pol.size() - 1; segments << QPair(selPoint, segments[selSegment].second); segments[selSegment].second = selPoint; selSegment = -1; updateGeometry(); if (scene() != 0 && update) scene()->update(); return pol.size() - 1; } int BlockBusItem::segmentPointPair(int point) const { for (int i = 0; i < segments.size(); ++i) { if (segments[i].first == point) return segments[i].second; if (segments[i].second == point) return segments[i].first; } return -1; } void BlockBusItem::removePoint(int index) { if (index < 0 || index > pol.size() - 1) return; int sc = 0, fs = -1, ss = -1; for (int i = 0; i < segments.size(); ++i) if (segments[i].first == index || segments[i].second == index) { sc++; if (fs < 0) fs = i; else ss = i; } int ei(0); switch (sc) { case 1: segments.removeAt(fs); break; case 2: if (segments[ss].first == index) ei = segments[ss].second; else ei = segments[ss].first; if (segments[fs].first == index) segments[fs].first = ei; else segments[fs].second = ei; segments.removeAt(ss); break; default: return; } pol.remove(index); for (int i = 0; i < segments.size(); ++i) { if (segments[i].first >= index) segments[i].first--; if (segments[i].second >= index) segments[i].second--; } selPoint = -1; checkDelete(); updateGeometry(); if (scene() != 0) scene()->update(); } void BlockBusItem::removeSegment(int index) { if (index < 0 || index > segments.size() - 1) return; int pif = segments[index].first, pis = segments[index].second; if (pif > pis) qSwap(pif, pis); int scf = 0, scs = 0; for (int i = 0; i < segments.size(); ++i) { if (segments[i].first == pif || segments[i].second == pif) scf++; if (segments[i].first == pis || segments[i].second == pis) scs++; } if (scs <= 2) removePoint(pis); if (scf <= 2) removePoint(pif); if (scs <= 2 || scf <= 2) selSegment = -1; if (scene() != 0) scene()->update(); } void BlockBusItem::appendPoint(const QPointF & p) { pol << p; if (pol.size() > 1) segments << QPair(pol.size() - 2, pol.size() - 1); updateGeometry(); } void BlockBusItem::clear() { pol.clear(); segments.clear(); updateGeometry(); } void BlockBusItem::markAsInput() { mark_in = true; mark_out = false; } void BlockBusItem::markAsOutput() { mark_in = false; mark_out = true; } void BlockBusItem::unmark() { mark_in = mark_out = false; } void BlockBusItem::simplify() { int pcnt = pol.size(); for (int i = 0; i < pol.size() - 1; ++i) { if (pol[i] == pol[i + 1]) pol.remove(i--); } if (pol.size() < 3) return; //PIMathVectorT2d cl, pl; for (int i = 0; i < pol.size() - 2; ++i) { //pl = Q2PIVector2(pol[i]) - Q2PIVector2(pol[i + 1]); //cl = Q2PIVector2(pol[i + 1]) - Q2PIVector2(pol[i + 2]); //if (pl.turnTo<3, double>() || cl.turnTo<3, double>()) // pol.remove(i-- + 1); } if (pcnt == pol.size()) return; //GBFrom->emitAddToHistory(History::ConnectionSimplify, ""); updateGeometry(); if (scene() != 0) scene()->update(); } void BlockBusItem::adjustLine() { } int BlockBusItem::endpointCount() const { return endpoints().size(); } QList BlockBusItem::connectedBlocks() const { QList pins = connections_.values(); QSet ret; foreach (BlockItemPin * p, pins) ret << p->parent(); return ret.toList(); } QList BlockBusItem::connectedPins() const { return connections_.values(); } void BlockBusItem::setBusState(bool state) { int s = state ? 1 : 0; if (state_ == s) return; state_ = s; update(); } void BlockBusItem::clearBusState() { if (state_ == -1) return; state_ = -1; update(); } QByteArray BlockBusItem::save() const { ChunkStream cs; cs << cs.chunk(1, busType()) << cs.chunk(2, busName()) << cs.chunk(3, width()) << cs.chunk(4, pen()) << cs.chunk(5, brush()) << cs.chunk(6, pol) << cs.chunk(7, segments) << cs.chunk(8, props); return cs.data(); } void BlockBusItem::load(const QByteArray & data) { clear(); if (data.isEmpty()) return; ChunkStream cs(data); while (!cs.atEnd()) { switch (cs.read()) { case 1: setBusType(cs.getData()); break; case 2: setBusName(cs.getData()); break; case 3: setWidth(cs.getData()); break; case 4: setPen(cs.getData()); break; case 5: setBrush(cs.getData()); break; case 6: pol = cs.getData(); break; case 7: segments = cs.getData > >(); break; case 8: props = cs.getData >(); break; } } updateGeometry(); } BlockBusItem * BlockBusItem::copy() const { return new BlockBusItem(*this); } void BlockBusItem::updateGeometry() { ends = endpoints(); ends_ind.clear(); for (int e = 0; e < ends.size(); ++e) { int ce = ends[e]; for (int s = 0; s < segments.size(); ++s) { if (segments[s].first == ce) { ends_ind << QPair(segments[s].first, segments[s].second); break; } if (segments[s].second == ce) { ends_ind << QPair(segments[s].second, segments[s].first); break; } } } reconnect(); prepareGeometryChange(); } void BlockBusItem::checkDelete() { if (pol.size() < 2 || segments.size() < 1) deleteLater(); } void BlockBusItem::emitAction(BlockItemBase::Action a) { QMetaObject::invokeMethod(scene()->views().back(), "actionEvent", Q_ARG(BlockItemBase::Action, a), Q_ARG(QList, QList() << this)); QMetaObject::invokeMethod(scene()->views().back(), "connectionsChanged"); } QVector BlockBusItem::endpoints() const { QVector counts(pol.size(), 0), ret; for (int i = 0; i < segments.size(); ++i) { counts[segments[i].first]++; counts[segments[i].second]++; } for (int i = 0; i < counts.size(); ++i) { if (counts[i] == 1) ret << i; } return ret; } int BlockBusItem::pointSegments(int point) const { int ret = 0; for (int i = 0; i < segments.size(); ++i) if (segments[i].first == point || segments[i].second == point) ret++; return ret; } void BlockBusItem::testPoint(QPointF pos, int * sel_point, int * sel_segment) { for (int i = 0; i < pol.size(); ++i) { if ((pol[i] - pos).manhattanLength() <= 10.) { // Point *sel_point = i; *sel_segment = -1; return; } } for (int i = 0; i < segments.size(); ++i) { if (distPointToLine(pol[segments[i].first], pol[segments[i].second], pos) <= 7.) { // Segment *sel_point = -1; *sel_segment = i; return; } } *sel_point = -1; *sel_segment = -1; } void BlockBusItem::hoverEnterEvent(QGraphicsSceneHoverEvent * e) { tt = bus_name + (bus_name.isEmpty() ? "" : "\n\n") + tr("Add point: Ctrl + LeftClick\nRemove point\\segment: Ctrl + RightClick\nNew branch: Shift + LeftClick\nRemove connection: Shift + RightClick"); } void BlockBusItem::hoverMoveEvent(QGraphicsSceneHoverEvent * e) { if (temp_) return; QPointF sp = e->scenePos(); testPoint(sp, &selPoint, &selSegment); if (selPoint >= 0 || selSegment >= 0) { setToolTip(tt); update(); return; } setToolTip(QString()); QList il = scene()->items(sp, Qt::ContainsItemBoundingRect, Qt::DescendingOrder), bil; bil << this; for (int i = 0; i < il.size(); ++i) { QGraphicsItem * b = il[i]; if (b->data(1005) == "connection" && b != this) { int tp = -1, ts = -1; ((BlockBusItem*)b)->testPoint(sp, &tp, &ts); if (tp >= 0 || ts >= 0) { foreach (QGraphicsItem * b2, bil) b2->stackBefore(b); break; } bil << b; } } update(); } void BlockBusItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * e) { if (temp_) return; selPoint = selSegment = -1; setPen(pu); setBrush(bu); setToolTip(QString()); update(); QGraphicsObject::hoverLeaveEvent(e); } void BlockBusItem::mousePressEvent(QGraphicsSceneMouseEvent * e) { if (temp_) return; lp = quantize(e->scenePos(), grid_step); if (new_segment) { QMetaObject::invokeMethod(scene()->views().back(), "newBranchCancel"); } new_segment = false; if ((selPoint < 0 || selPoint > pol.size() - 1) && (selSegment < 0)) { QGraphicsObject::mousePressEvent(e); return; } int btncnt = 0; if (e->buttons().testFlag(Qt::LeftButton)) btncnt++; if (e->buttons().testFlag(Qt::RightButton)) btncnt++; if (e->buttons().testFlag(Qt::MidButton)) btncnt++; if (btncnt > 0) mm_mods = e->modifiers(); //qDebug() << "press" << e; if (btncnt >= 2 && e->button() == Qt::RightButton) { mm_cancel = true; moved = false; QPointF lp = qp - press_pos; //qDebug() << lp; if (selPoint >= 0 && selPoint <= pol.size() - 1) pol[selPoint] += lp; if (selSegment >= 0 && selSegment <= segments.size() - 1) { pol[segments[selSegment].first] += lp; pol[segments[selSegment].second] += lp; } moved = true; prepareGeometryChange(); return; } if (e->modifiers().testFlag(Qt::ShiftModifier)) { if (e->buttons().testFlag(Qt::LeftButton)) { if (selSegment >= 0) press_pos = quantize(nearestPointOnLine(pol[segments[selSegment].first], pol[segments[selSegment].second], e->scenePos()), grid_step); else { if (selPoint >= 0) press_pos = pol[selPoint]; else return; } if (max_ep >= 2) { if (endpointCount() >= max_ep) if (pointSegments(selPoint) >= 2 || selSegment >= 0) return; } QMetaObject::invokeMethod(scene()->views().back(), "newBranch", Q_ARG(BlockBusItem * , this)); new_segment = true; return; } if (e->buttons().testFlag(Qt::RightButton)) { deleteLater(); } } if (e->modifiers().testFlag(Qt::ControlModifier)) { if (e->buttons().testFlag(Qt::RightButton)) { if (selPoint >= 0 && selPoint <= pol.size() - 1) { removePoint(selPoint); emitAction(BlockItemBase::BusPointRemove); return; } if (selSegment >= 0 && selSegment <= segments.size() - 1) { removeSegment(selSegment); emitAction(BlockItemBase::BusSegmentRemove); return; } } if (e->buttons().testFlag(Qt::LeftButton) && selSegment >= 0) { if (addPoint(e->scenePos()) >= 0) emitAction(BlockItemBase::BusPointAdd); return; } } if (e->modifiers().testFlag(Qt::ShiftModifier)) { if (e->buttons().testFlag(Qt::RightButton)) { if (deleted) return; deleted = true; } } } void BlockBusItem::mouseMoveEvent(QGraphicsSceneMouseEvent * e) { if (temp_ || mm_cancel) return; if (((selPoint < 0 || selPoint > pol.size() - 1) && (selSegment < 0)) && !new_segment) { QGraphicsObject::mouseMoveEvent(e); return; } qp = quantize(e->scenePos(), grid_step); lp = qp - lp; if (e->buttons().testFlag(Qt::LeftButton) && mm_mods.testFlag(Qt::ShiftModifier) && new_segment) { QMetaObject::invokeMethod(scene()->views().back(), "newBranchTrace", Q_ARG(BlockBusItem * , this), Q_ARG(QPointF, e->scenePos())); return; } if (new_segment) { new_end = qp; prepareGeometryChange(); } else { if (e->buttons().testFlag(Qt::LeftButton)) { lm_point = selPoint >= 0; if (selPoint >= 0 && selPoint <= pol.size() - 1) pol[selPoint] += lp; if (selSegment >= 0 && selSegment <= segments.size() - 1) { pol[segments[selSegment].first] += lp; pol[segments[selSegment].second] += lp; } moved = true; prepareGeometryChange(); } } lp = qp; } void BlockBusItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * e) { mm_mods = 0; int btncnt = 0; if (e->buttons().testFlag(Qt::LeftButton)) btncnt++; if (e->buttons().testFlag(Qt::RightButton)) btncnt++; if (e->buttons().testFlag(Qt::MidButton)) btncnt++; if (btncnt == 0) mm_cancel = false; if (new_segment) { QMetaObject::invokeMethod(scene()->views().back(), "newBranchAccept", Q_ARG(BlockBusItem * , this)); updateGeometry(); selPoint = selSegment = -1; } if (moved) { reconnect(); emitAction(lm_point ? BlockItemBase::BusPointMove : BlockItemBase::BusSegmentMove); } moved = new_segment = false; QGraphicsObject::mouseReleaseEvent(e); } void BlockBusItem::paint(QPainter * p, const QStyleOptionGraphicsItem * o, QWidget * w) { ph.setWidthF(pen_width + 1.); pu.setWidthF(pen_width); pa.setWidthF(pen_width); pr.setWidthF(pen_width); pn.setWidthF(pen_width); if (pol.size() < 2) return; if (selPoint >= 0 || selSegment >= 0) { p->setPen(pa); p->setBrush(ba); } else { p->setPen(pu); p->setBrush(bu); } //if (mark_in) {p->setPen(pa); p->setBrush(ba);} //if (mark_out) {p->setPen(pr); p->setBrush(br);} if (im_bus.isNull()) { for (int i = 0; i < segments.size(); ++i) { p->drawLine(pol[segments[i].first], pol[segments[i].second]); } } else { QBrush br; br.setTextureImage(im_bus); for (int i = 0; i < segments.size(); ++i) { QPointF sp(pol[segments[i].first]), ep(pol[segments[i].second]); QTransform tf; tf.translate(sp.x(), sp.y()); tf.rotate(-QLineF(sp, ep).angle()); tf.translate(0., -im_bus.height() / 2.); /* br.setTransform(tf); p->setPen(QPen(br, im_bus.height(), Qt::SolidLine, Qt::FlatCap, Qt::BevelJoin)); p->drawLine(sp, ep); */ p->save(); p->setTransform(tf, true); p->setPen(Qt::NoPen); p->setBrush(br); //p->drawLine(QPointF(0., 0.), QPointF(QLineF(sp, ep).length(), 0.)); p->drawRect(QRectF(0., 0., QLineF(sp, ep).length(), im_bus.height())); p->restore(); } } if (!im_end.isNull()) { for (int i = 0; i < ends_ind.size(); ++i) { QPointF sp = pol[ends_ind[i].first], ep = pol[ends_ind[i].second]; QTransform tf; tf.translate(sp.x(), sp.y()); tf.rotate(-QLineF(sp, ep).angle()); tf.translate(-pen_width, -im_end.height() / 2.); p->save(); p->setTransform(tf, true); p->drawImage(0, 0, im_end); p->restore(); } } if (state_ >= 0) { if (state_ == 0) { pr.setWidthF(pen_width + 1.); p->setPen(pr); } else if (state_ == 1) { pa.setWidthF(pen_width + 1.); p->setPen(pa); } p->setOpacity(0.5); for (int i = 0; i < segments.size(); ++i) { p->drawLine(pol[segments[i].first], pol[segments[i].second]); } p->setOpacity(1.); } if (!im_bus.isNull() && (selPoint >= 0 || selSegment >= 0)) { p->setPen(pa); p->setBrush(ba); for (int i = 0; i < segments.size(); ++i) { p->drawLine(pol[segments[i].first], pol[segments[i].second]); } } //if (mark_in) {p->setPen(pa); p->setBrush(ba);} //if (mark_out) {p->setPen(pr); p->setBrush(br);} if (selPoint >= 0) { p->save(); p->setPen(ph); p->setBrush(bh); p->translate(pol[selPoint]); p->drawEllipse(QPointF(0, 0), 3, 3); p->restore(); } if (selSegment >= 0) { p->save(); p->setPen(ph); p->drawLine(pol[segments[selSegment].first], pol[segments[selSegment].second]); p->restore(); } } QRectF BlockBusItem::boundingRect() const { QPolygonF p(pol); p << new_end; return enlargedRect(p.boundingRect(), 0, 0, 10.f); }