#include "blockview.h" #include "qad_types.h" #include #include #include #include #include #include #include #include #include #include const QString _BlockView_Mime_ = "_BlockView_copypaste_"; BlockView::BlockView(QWidget * parent): QGraphicsView(parent), tmp_bus(true) { _init(); } BlockView::BlockView(QGraphicsScene * scene, QWidget * parent): QGraphicsView(scene, parent), tmp_bus(true) { _init(); } BlockView::~BlockView() {} void BlockView::_init() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); grid_visible = grid_snap = pm_connect = navigation = m_connect = m_trace_with_buses = prev_tcb = minimap = true; mm_drag = moved = new_branch = new_bus = mm_cancel = iconnect = mm_copy = m_pin_mc = mm_thumb = move_bus_point = wheel_zoom = false; match_bus = bus_from = cur_bus = 0; mm_ci = 0; hpin = 0; ghost_ = 0; grid_step = 10.; grid_points = 1; grid_pen = QPen(palette().color(QPalette::Disabled, QPalette::WindowText), 1, Qt::NoPen); thick = 1; thumb_hide_delay = 500; timer_thumb = 0; smode = BlockView::MultiSelection; cur_scl = thumb_scl = prev_app_scale = 1.; _talpha = 0.; ae_enabled = is_block_anim = is_nav_anim = true; nav_prev_aa = nav_prev_imaa = nav_prev_grid = true; square_node = block_emit_selection = new_bus_started = false; thumb_size = QSizeF(200, 200); if (scene() == 0) { scene_ = new QGraphicsScene; setScene(scene_); } scene_ = scene(); scene_->setSceneRect(-2500, -2500, 5000, 5000); scene_->setItemIndexMethod(QGraphicsScene::NoIndex); scene_->installEventFilter(this); scene_->addItem(&tmp_bus); widget_thumb.setParent(this); widget_thumb.installEventFilter(this); widget_thumb.setMouseTracking(true); widget_thumb.show(); widget_thumb.setWindowOpacity(0.); thumb_anim.setTargetObject(this); thumb_anim.setPropertyName("_thumb"); thumb_anim.setEasingCurve(QEasingCurve::InCubic); nav_anim.setTargetObject(this); nav_anim.setPropertyName("_nav"); nav_anim.setEasingCurve(QEasingCurve::Linear); nav_anim.setDuration(150); connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(checkPaste())); connect(&nav_anim, SIGNAL(finished()), this, SLOT(_navFinished())); connect(scene_, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(adjustThumb())); connect(scene_, SIGNAL(selectionChanged()), this, SLOT(sceneSelectionChanged())); connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrolled())); connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrolled())); centerOn(scene_->sceneRect().center()); setCacheMode(CacheBackground); setTransformationAnchor(QGraphicsView::AnchorUnderMouse); setResizeAnchor(QGraphicsView::AnchorViewCenter); setRenderHint(QPainter::Antialiasing); setRenderHint(QPainter::SmoothPixmapTransform); setMouseTracking(true); sel_rect.setZValue(999.); sel_rect.hide(); QColor sc = palette().color(QPalette::Highlight); sc.setAlphaF(0.6); QPen pen(sc.darker(200), lineThickness(this) + 1., Qt::DotLine); pen.setCosmetic(true); sel_rect.setPen(pen); sc.setAlphaF(0.2); sel_rect.setBrush(QBrush(sc)); checkPaste(true); } void BlockView::_updateBack() { setCacheMode(CacheNone); invalidateScene(); setCacheMode(CacheBackground); } bool BlockView::event(QEvent * e) { if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish) { double cscl = appScale(this); QGraphicsView::scale(cscl / prev_app_scale, cscl / prev_app_scale); prev_app_scale = cscl; } if (e->type() == QEvent::EnabledChange) { _updateBack(); } return QGraphicsView::event(e); } bool BlockView::eventFilter(QObject * o, QEvent * e) { if (o == &widget_thumb) { QMouseEvent * me = (QMouseEvent *)e; if (!me) return true; switch (e->type()) { case QEvent::Paint: drawThumb(); return true; case QEvent::Enter: thumbShow(); break; case QEvent::Leave: restartTimer(timer_thumb, thumb_hide_delay); break; case QEvent::MouseButtonPress: thumb_press = me->pos(); widget_thumb.setCursor(Qt::ClosedHandCursor); if (!thumb_vr.contains(thumb_press)) { thumb_vr.moveCenter(thumb_press); scrollFromThumb(); } break; case QEvent::MouseButtonRelease: widget_thumb.setCursor(Qt::OpenHandCursor); break; case QEvent::MouseMove: if (me->buttons() == 0) widget_thumb.setCursor(thumb_vr.contains(me->pos()) ? Qt::OpenHandCursor : Qt::CrossCursor); if (me->buttons().testFlag(Qt::LeftButton)) { thumb_vr.translate(me->pos() - thumb_press); scrollFromThumb(); thumb_press = me->pos(); } break; default: break; } return QGraphicsView::eventFilter(o, e); } if (o == scene_) { QGraphicsSceneMouseEvent * me = (QGraphicsSceneMouseEvent *)e; QList mil; QPointF mdp; bool fmm_drag = false; int btncnt = 0; switch (e->type()) { case QEvent::GraphicsSceneMouseDoubleClick: scene_point = me->scenePos(); mil = scene_->items(scene_point); foreach(QGraphicsItem * i, mil) { if (i->data(bvidTmpItem).toBool()) continue; if (i->data(bvidType).toInt() == bvitBlock) { QMetaObject::invokeMethod( this, [this, i]() { blockDoubleClicked(qgraphicsitem_cast(i)); }, Qt::QueuedConnection); return true; } if (i->data(bvidType).toInt() == bvitBus) { if (qgraphicsitem_cast(i)->isBusSelected()) { QMetaObject::invokeMethod( this, [this, i]() { busDoubleClicked(qgraphicsitem_cast(i)); }, Qt::QueuedConnection); return true; } } } break; case QEvent::GraphicsSceneMousePress: if (mm_ci != 0) { if (mm_ci->data(bvidTmpItem).toBool()) { mm_ci = 0; break; } } if (me->buttons().testFlag(Qt::LeftButton)) btncnt++; if (me->buttons().testFlag(Qt::RightButton)) btncnt++; if (me->buttons().testFlag(QT_MID_BUTTON)) btncnt++; mm_cancel = btncnt >= 2; match_bus = bus_from = 0; hpin = 0; copy_dp = QPointF(); // qDebug() << mm_cancel << moved << sel_rect.isVisible(); if (sel_rect.isVisible()) { QList gi = scene_->items(); block_emit_selection = true; foreach(QGraphicsItem * i, gi) i->setSelected(i->data(bvidSelected).toBool()); block_emit_selection = false; emit selectionChanged(); } // qDebug() << "cur_bus" << cur_bus; if (cur_bus) { return false; } if (mm_cancel) { if (mm_copy) { deleteCopyTemp(); mm_copy = moved = false; unsetCursor(); } if (new_bus) { new_bus = false; unmarkPins(true); reconnectAll(); hideTmpBuses(); m_trace_with_buses = prev_tcb; } if (new_branch) { new_branch = false; hideTmpBuses(); m_trace_with_buses = prev_tcb; } if (moved) { moved = false; restoreSelState(); hideTmpBuses(); m_trace_with_buses = prev_tcb; } return true; } mm_mods = me->modifiers(); mm_drag = moved = false; screen_point = me->screenPos(); scene_point = me->scenePos(); if ((me->button() == QT_MID_BUTTON) || (me->button() == Qt::RightButton)) { thumbShow(); restartTimer(timer_thumb, thumb_hide_delay); return true; } mil = scene_->items(scene_point); // qDebug() << "mil" << mil; while (!mil.isEmpty()) { mm_ci = mil.front(); if (mm_ci->data(bvidDTHandle).toBool()) return QGraphicsView::eventFilter(o, e); if (mm_ci->data(bvidTmpItem).toBool() || mm_ci->data(bvidItemSelection).toBool()) { mil.pop_front(); } else break; } if (mil.isEmpty()) { mm_ci = 0; return true; } while (mm_ci->data(bvidType).toInt() == bvitBus) { if (qgraphicsitem_cast(mm_ci)) if (qgraphicsitem_cast(mm_ci)->isBusSelected()) break; if (mil.size() > 1) { mm_ci = mil[1]; mil.pop_front(); } else { mm_ci = 0; break; } } if (mm_ci->data(bvidVisualizeSelection).toBool()) { if (mil.size() > 1) { mm_ci = mil[1]; mil.pop_front(); if (mm_ci->data(bvidVisualizeSelection).toBool()) if (mil.size() > 1) mm_ci = mil[1]; } else mm_ci = 0; } if (mm_ci->data(bvidMoveParent).toBool()) { QGraphicsItem * ti = mm_ci; while (mm_ci->parentItem() != 0) mm_ci = mm_ci->parentItem(); if (!ti->data(bvidDecorText).toString().isEmpty()) { // text item, check for rect BlockItem * bi = qgraphicsitem_cast(mm_ci); if (bi) { if (!bi->sceneRect().contains(scene_point)) { // qDebug() << "return"; mm_ci = 0; } } } return true; } if (mm_ci) { if ((mm_ci->data(bvidType).toInt() == bvitPin) && m_connect) { if (qgraphicsitem_cast(mm_ci)->state() == BlockItemPin::Hover) { trace_from = mm_ci->scenePos(); qgraphicsitem_cast(mm_ci)->clearStateStack(); hideTmpBuses(); tmp_bus.setBusType(qgraphicsitem_cast(mm_ci)->busType()); tmp_bus.show(); new_bus = true; qDeleteAll(tmp_buses); tmp_buses.clear(); foreach(BlockItemPin * p, last_multiconnect_pl) { tmp_buses << new BlockBusItem(true); tmp_buses.back()->setBusType(p->busType()); addItem(tmp_buses.back()); } // qDebug() << "new" << ; prev_tcb = m_trace_with_buses; startNewBus(tmp_bus.busType()); if (qgraphicsitem_cast(mm_ci)->alignment() == Qt::AlignLeft || qgraphicsitem_cast(mm_ci)->alignment() == Qt::AlignRight) wavetrace.setPreferredDirection(BlockViewWavetrace::Horizontal); else wavetrace.setPreferredDirection(BlockViewWavetrace::Vertical); } return true; } } cur_scl = qSqrt(transform().determinant()); break; case QEvent::GraphicsSceneMouseMove: // qDebug() << "move" << (mm_ci != 0 ? mm_ci : 0) << mm_mods << mm_cancel << mm_drag; // if (cur_bus) return false; // qDebug() << "move mm_ci" << mm_ci << mm_cancel; if (mm_ci) if (mm_ci->data(bvidTmpItem).toBool()) { mm_ci = 0; break; } if (mm_ci->data(bvidItemSelection).toBool()) break; if (mm_ci->data(bvidDTHandle).toBool()) break; if (mm_cancel) return true; // qDebug() << tmp_bus.isVisible(); if ((me->buttons().testFlag(Qt::LeftButton) && mm_drag && tmp_bus.isVisible()) || new_branch) { mil = scene_->items(me->scenePos()); hpin = 0; foreach(QGraphicsItem * i, mil) if (i->data(bvidType).toInt() == bvitPin) { hpin = qgraphicsitem_cast(i); break; } if (hpin) { if (hpin->state() == BlockItemPin::Accept) { unhoverPins(hpin); hoverAcceptedPin(hpin, true); } else hpin = 0; } else unhoverPins(); if (!cur_bus) { if (new_branch) { matchBus(); break; } trace(trace_from, me->scenePos(), &tmp_bus); for (int i = 0; i < qMin(tmp_buses.size(), last_multiconnect_pl.size()); ++i) { QPointF dp = last_multiconnect_pl[i]->scenePos() - trace_from; // qDebug() << "trace" << i << dp; trace(trace_from + dp, me->scenePos() + dp, tmp_buses[i], false); tmp_buses[i]->show(); } matchBus(); } } if (cur_bus) return false; if (me->buttons().testFlag(Qt::LeftButton)) { if (!mm_drag) { if ((screen_point - me->screenPos()).manhattanLength() >= QApplication::startDragDistance()) { mm_drag = fmm_drag = true; saveBusesState(); } } if (tmp_bus.isVisible()) return true; if (mm_mods.testFlag(Qt::ShiftModifier)) { if (fmm_drag) { fmm_drag = false; if (mm_ci) { if ((mm_ci->data(bvidType).toInt() == bvitBlock) && !mm_ci->data(bvidTmpItem).toBool()) { if (!mm_ci->isSelected()) { clearSelection(); mm_ci->setSelected(true); } } else { mm_ci = 0; break; } } else { break; } sel_items = scene_->selectedItems(); deleteCopyTemp(); QList bi; foreach(QGraphicsItem * i, sel_items) { if ((i->data(bvidType).toInt() == bvitBlock) && !i->data(bvidTmpItem).toBool()) { // qDebug() << "copy"; bi << qgraphicsitem_cast(i); BlockItem * ti = bi.back()->copy(); ti->g_main.setPen(QPen(ti->g_main.pen().color(), ti->g_main.pen().widthF(), Qt::DashLine)); QColor bc = ti->g_main.brush().color(); bc.setAlphaF(bc.alphaF() * 0.5); ti->g_main.setBrush(bc); copy_items << ti; scene_->addItem(ti); } } QList ibi = internalBuses(bi); foreach(BlockBusItem * i, ibi) { i = i->copy(); i->setOpacity(i->opacity() * 0.5); copy_buses << i; scene_->addItem(i); } mm_copy = true; setCursor(Qt::DragCopyCursor); } } else { // qDebug() << "move smode" << smode; if (smode == BlockView::SingleSelection) { if (fmm_drag) { clearSelection(); if (mm_ci != 0) { mm_ci->setSelected(true); } saveSelState(); } } if (smode == BlockView::MultiSelection) { sel_rect.setRect(QRectF(scene_point, me->scenePos()).normalized()); if (fmm_drag) { if (mm_ci == 0) { scene_->addItem(&sel_rect); sel_rect.show(); if (!mm_mods.testFlag(Qt::ControlModifier)) clearSelection(); } else { if (!mm_mods.testFlag(Qt::ControlModifier) && !mm_ci->isSelected()) { clearSelection(); mm_ci->setSelected(true); } } saveSelState(); if (mm_ci != 0) if (!sel_items.contains(mm_ci)) mm_ci = 0; } } } if (sel_rect.isVisible()) applySelRect(me); else { if (mm_drag && mm_ci != 0) { mdp = (me->scenePos() - scene_point); if (grid_snap) mdp = quantize(mdp, grid_step); if (!mdp.isNull()) { scene_point += mdp; copy_dp += mdp; } if (mm_copy) { if (!mdp.isNull()) moved = true; foreach(QGraphicsItem * i, copy_items) i->setPos(i->pos() + mdp); foreach(BlockBusItem * i, copy_buses) i->movePolyline(mdp); } if (!mm_mods.testFlag(Qt::ControlModifier) && !mm_mods.testFlag(Qt::ShiftModifier)) { if (!mdp.isNull()) moved = true; bool is_correct = true; if (!me->modifiers().testFlag(Qt::AltModifier)) is_correct = moveBuses(sel_items, mdp); if (is_correct) { for (auto * i: sel_items) if (i->flags().testFlag(QGraphicsItem::ItemIsMovable)) i->setPos(i->pos() + mdp); } else { scene_point -= mdp; } setCursor(Qt::ClosedHandCursor); } return true; } } if (mm_ci) if (mm_ci->data(bvidBlockDecor).toBool()) return true; } if (me->modifiers().testFlag(Qt::ControlModifier) && me->buttons() != 0 && mm_ci == 0) return true; // qDebug() << "scene mouse"; break; case QEvent::GraphicsSceneMouseRelease: if (me->buttons().testFlag(Qt::LeftButton)) btncnt++; if (me->buttons().testFlag(Qt::RightButton)) btncnt++; if (me->buttons().testFlag(QT_MID_BUTTON)) btncnt++; maybeEndNewBus(); cur_bus = 0; mm_cancel = btncnt > 0; if (mm_cancel || (me->button() == QT_MID_BUTTON) || (me->button() == Qt::RightButton)) { mm_ci = 0; return true; } if (mm_ci) if (mm_ci->data(bvidTmpItem).toBool()) { mm_ci = 0; break; } if (mm_ci->data(bvidItemSelection).toBool()) break; if (mm_copy) { QList ai; blockSignals(true); if (moved) { QList ci; QList bi; foreach(QGraphicsItem * b, sel_items) if ((b->data(bvidType).toInt() == bvitBlock) && !b->data(bvidTmpItem).toBool()) { ci << qgraphicsitem_cast(b); ai << qgraphicsitem_cast(b); } bi = internalBuses(ci); if (!ci.isEmpty()) copyBlocks(ci, copy_dp); if (!bi.isEmpty()) copyBuses(bi, copy_dp); } deleteCopyTemp(); blockSignals(false); if (moved) { moved = false; reconnectAll(); emitActionEvent(BlockItemBase::BlockCopy, ai); } } if (new_branch) { hideTmpBuses(false); } if (moved && pm_connect) { QList ci; foreach(QGraphicsItem * b, sel_items) if (b->data(bvidType).toInt() == bvitBlock) ci << b; simplifyBuses(); emitActionEvent(BlockItemBase::BlockMove, ci); reconnectAll(); } moved = mm_copy = false; if (tmp_bus.isVisible()) { if (!tmp_bus.pol.isEmpty()) { if (match_bus == 0) { BlockBusItem * nb = new BlockBusItem(tmp_bus); addItem(nb, tmp_buses.isEmpty()); newBusCreated(nb); foreach(BlockBusItem * b, tmp_buses) { nb = new BlockBusItem(*b); addItem(nb, b == tmp_buses.back()); newBusCreated(nb); } } else { if (connectTmpToBus(match_bus)) { newBusCreated(match_bus); emitActionEvent(BlockItemBase::BusAdd, QList() << match_bus); emit connectionsChanged(); } } } unmarkPins(); hideTmpBuses(); reconnectAll(); BlockItemPin * pin = getPin(scene_->items(me->scenePos())); if (pin) pin->hoverEnterEvent(0); m_trace_with_buses = prev_tcb; } clearBusStates(); if (!mm_drag) { switch (smode) { case SingleSelection: clearSelection(); if (mm_ci) mm_ci->setSelected(true); break; case MultiSelection: if (mm_ci == 0 || !me->modifiers().testFlag(Qt::ControlModifier)) { clearSelection(); if (mm_ci) mm_ci->setSelected(true); } else { if (mm_ci != 0) { if (me->modifiers().testFlag(Qt::ControlModifier)) { if (mm_ci->data(bvidType).toInt() == bvitBlock) mm_ci->setSelected(!mm_ci->isSelected()); } else mm_ci->setSelected(true); } } break; default: clearSelection(); break; } } sel_rect.hide(); if (sel_rect.scene()) scene_->removeItem(&sel_rect); mm_drag = false; mm_ci = 0; unsetCursor(); break; default: break; } } return QGraphicsView::eventFilter(o, e); } void BlockView::stopTimer(int & tid) { if (tid > 0) killTimer(tid); tid = 0; } void BlockView::restartTimer(int & tid, int msecs) { stopTimer(tid); tid = startTimer(msecs); } void BlockView::timerEvent(QTimerEvent * e) { if (e->timerId() == timer_thumb) { thumbHide(); stopTimer(timer_thumb); return; } QGraphicsView::timerEvent(e); } void BlockView::wheelEvent(QWheelEvent * e) { if (!navigation) return; if (wheel_zoom || e->modifiers().testFlag(Qt::ControlModifier)) { #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) double scl = 1. - e->delta() / 500.; #else double scl = 1. - e->angleDelta().y() / 500.; #endif if (!is_nav_anim || (nav_anim.state() != QPropertyAnimation::Running)) nav_target = _nav(); QRectF r = nav_target; double vw = viewport()->width(), vh = viewport()->height(); #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) double cx = double(e->pos().x()) / vw, cy = double(e->pos().y()) / vh; #else double cx = double(e->position().x()) / vw, cy = double(e->position().y()) / vh; #endif double pw = r.width(), ph = r.height(); r.setWidth(r.width() * scl); r.setHeight(r.width() * vh / vw); r.translate(cx * (pw - r.width()), cy * (ph - r.height())); animateNav(r, scl); return; } if (!wheel_zoom) QGraphicsView::wheelEvent(e); } void BlockView::mousePressEvent(QMouseEvent * event) { press_point = event->pos(); if (event->buttons().testFlag(QT_MID_BUTTON) || event->buttons().testFlag(Qt::RightButton)) { setCursor(Qt::ClosedHandCursor); if (sel_rect.scene()) scene_->removeItem(&sel_rect); } QGraphicsView::mousePressEvent(event); } void BlockView::mouseReleaseEvent(QMouseEvent * event) { unsetCursor(); QGraphicsView::mouseReleaseEvent(event); } void BlockView::updateNavRect() { QPointF t = mapToScene(viewport()->rect().topLeft()); QPointF b = mapToScene(viewport()->rect().bottomRight()); nav_rect = QRectF(t, b); } void BlockView::scrolled() { updateNavRect(); } void BlockView::mouseMoveEvent(QMouseEvent * event) { if (!event) return; if (navigation) { if (event->buttons().testFlag(QT_MID_BUTTON) || event->buttons().testFlag(Qt::RightButton)) { QPoint dp = (press_point - event->pos()); horizontalScrollBar()->setValue(horizontalScrollBar()->value() + dp.x()); verticalScrollBar()->setValue(verticalScrollBar()->value() + dp.y()); press_point = event->pos(); updateNavRect(); QRectF nr = nav_anim.endValue().toRectF(); nr.moveCenter(_nav().center()); nav_anim.setEndValue(nr); return; } } QGraphicsView::mouseMoveEvent(event); } void BlockView::mouseDoubleClickEvent(QMouseEvent * event) { if (event->buttons().testFlag(QT_MID_BUTTON) || event->buttons().testFlag(Qt::RightButton)) { fitInView(); return; } QGraphicsView::mouseDoubleClickEvent(event); } void BlockView::keyPressEvent(QKeyEvent * e) { BlockItemPin * pin = getPin(items(mapFromGlobal(QCursor::pos()))); if (pin) { // qDebug() << "pin" << pin->state(); if (pin->state() == BlockItemPin::Hover) { highlightNearPins(pin, e->modifiers()); } } if (tmp_bus.isVisible()) { bool retrace = false; if (e->key() == Qt::Key_Shift) { retrace = true; switch (wavetrace.preferredDirection()) { case BlockViewWavetrace::NoTrace: wavetrace.setPreferredDirection(BlockViewWavetrace::Horizontal); break; case BlockViewWavetrace::Horizontal: wavetrace.setPreferredDirection(BlockViewWavetrace::Vertical); break; case BlockViewWavetrace::Vertical: wavetrace.setPreferredDirection(BlockViewWavetrace::NoTrace); break; } } if (e->key() == Qt::Key_Alt) { retrace = true; m_trace_with_buses = !m_trace_with_buses; } if (retrace) { trace(last_trace_from, trace_to, &tmp_bus); for (int i = 0; i < qMin(tmp_buses.size(), last_multiconnect_pl.size()); ++i) { QPointF dp = last_multiconnect_pl[i]->scenePos() - last_trace_from; // qDebug() << "trace" << i << dp; trace(last_trace_from + dp, trace_to + dp, tmp_buses[i], false); tmp_buses[i]->show(); } return; } } QGraphicsView::keyPressEvent(e); } void BlockView::keyReleaseEvent(QKeyEvent * e) { BlockItemPin * pin = getPin(items(mapFromGlobal(QCursor::pos()))); if (pin) { // qDebug() << "pin" << pin->state(); if (pin->state() == BlockItemPin::Hover) { highlightNearPins(pin, e->modifiers()); } } QGraphicsView::keyReleaseEvent(e); } void BlockView::resizeEvent(QResizeEvent * event) { QGraphicsView::resizeEvent(event); thick = lineThickness(this); #if QT_VERSION_MAJOR >= 6 thick *= devicePixelRatioF(); #endif adjustThumb(); updateNavRect(); nav_target = _nav(); } void BlockView::scrollContentsBy(int dx, int dy) { QGraphicsView::scrollContentsBy(dx, dy); if (isHidden()) return; thumbShow(); restartTimer(timer_thumb, thumb_hide_delay); QWidget * w = &widget_thumb; QMetaObject::invokeMethod( this, [w]() { w->update(); }, Qt::QueuedConnection); } void BlockView::drawBackground(QPainter * painter, const QRectF & rect) { if (mm_thumb) return; float rx, ry, sx = grid_step, sy = grid_step; double scl = qRound(1. / qSqrt(transform().determinant())); painter->fillRect(rect, palette().brush(QPalette::Base)); painter->setRenderHint(QPainter::Antialiasing, false); if (scl > 0.) { sx *= scl; sy *= scl; } if (!grid_visible) return; rx = quantize(rect.left(), sx); ry = quantize(rect.top(), sy); bool gp = grid_points > 0.5; if (gp) { QPen pp(grid_pen.color(), qMax(grid_points, thick), Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); pp.setCosmetic(true); painter->setPen(pp); for (int i = 0; i < qCeil(rect.width() / sx) + 1; ++i) for (int j = 0; j < qCeil(rect.height() / sy) + 1; ++j) painter->drawPoint(rx + i * sx, ry + j * sy); } if (grid_pen.style() == Qt::NoPen) return; QPen pen = grid_pen; pen.setWidth(qMax(pen.width(), thick)); painter->setPen(grid_pen); for (int i = 0; i < qCeil(rect.width() / sx) + 1; ++i) { double cx = rx + i * sx; painter->drawLine(cx, ry, cx, ry + rect.height()); } for (int j = 0; j < qCeil(rect.height() / sy) + 1; ++j) { double cy = ry + j * sy; painter->drawLine(rx, cy, rx + rect.width(), cy); } } void BlockView::scrollFromThumb() { QRect r(thumb_vr); double scl(thumb_scl); horizontalScrollBar()->setValue(r.x() / scl + horizontalScrollBar()->minimum()); verticalScrollBar()->setValue(r.y() / scl + verticalScrollBar()->minimum()); } void BlockView::deleteCopyTemp() { qDeleteAll(copy_items); copy_items.clear(); qDeleteAll(copy_buses); copy_buses.clear(); } void BlockView::emitActionEvent(BlockItemBase::Action action, QList items) { if (!ae_enabled) return; emit schemeAction(action, items); } void BlockView::setGhost(BlockItem * item) { clearGhost(); if (!item) return; ghost_ = item; ghost_->setOpacity(0.5); addItem(item, false); } void BlockView::clearGhost() { if (!ghost_) return; delete ghost_; ghost_ = 0; } void BlockView::startNewBus(int bus_type) { newBusStarted(bus_type); markPins(bus_type); new_bus_started = true; } void BlockView::maybeEndNewBus() { if (!new_bus_started) return; new_bus_started = false; QMetaObject::invokeMethod( this, [this]() { newBusFinished(); }, Qt::QueuedConnection); } void BlockView::getPinMC(bool * v) { if (v) *v = m_pin_mc; } void BlockView::drawThumb() { if (!minimap) return; QPainter p(&widget_thumb); QRect wr = widget_thumb.rect().adjusted(0, 0, -1, -1); QSizeF sr = sceneRect().size(), tr; if (sr.width() >= sr.height()) thumb_scl = thumb_size.width() / sr.width(); else thumb_scl = thumb_size.height() / sr.height(); tr = sr * thumb_scl; cur_scl = qSqrt(transform().determinant()); thumb_scl /= cur_scl; QSizeF vs(size().width() - verticalScrollBar()->width(), size().height() - horizontalScrollBar()->height()); QRectF vr(QPointF(horizontalScrollBar()->value() - horizontalScrollBar()->minimum(), verticalScrollBar()->value() - verticalScrollBar()->minimum()) * thumb_scl, vs * thumb_scl); vr.adjust(0, 0, -1, -1); p.setBrush(Qt::lightGray); p.setOpacity(0.5 * _talpha); p.drawRect(wr); p.drawImage(0, 0, im_scene); p.setBrush(Qt::NoBrush); p.drawRect(wr); p.setBrush(Qt::white); p.setRenderHint(QPainter::Antialiasing); p.drawRect(vr); thumb_sr = wr; thumb_vr = vr.toRect(); } void BlockView::drawSceneThumb() { if (!minimap) return; QRect wr = widget_thumb.rect().adjusted(0, 0, -1, -1); im_scene = QImage(wr.size(), QImage::Format_ARGB32); im_scene.fill(Qt::transparent); QPainter p(&im_scene); p.setRenderHint(QPainter::Antialiasing); mm_thumb = true; scene_->render(&p, im_scene.rect()); mm_thumb = false; } void BlockView::thumbHide() { if (!minimap) { widget_thumb.hide(); return; } if (QApplication::widgetAt(QCursor::pos()) == &widget_thumb) return; thumb_anim.stop(); thumb_anim.setDuration(1000); thumb_anim.setStartValue(_thumb()); thumb_anim.setEndValue(0.); thumb_anim.start(); // qDebug() << "hide" << thumb_anim.startValue() << thumb_anim.endValue(); } void BlockView::thumbShow() { if (!minimap) return; if (widget_thumb.isHidden() || (_talpha < 0.1)) drawSceneThumb(); thumb_anim.stop(); _setThumb(1.); // qDebug() << "show" << thumb_anim.startValue() << thumb_anim.endValue(); } void BlockView::clearSelection() { bool pb = block_emit_selection; block_emit_selection = true; sel_items.clear(); QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) if (i->flags().testFlag(QGraphicsItem::ItemIsSelectable)) i->setSelected(false); block_emit_selection = pb; if (!block_emit_selection) emit selectionChanged(); } void BlockView::addItem(QGraphicsItem * item, bool emit_action) { scene_->addItem(item); applyGridStep(); if (item->data(bvidType).toInt() == bvitBus) { loadBus(qgraphicsitem_cast(item)); ((BlockBusItem *)item)->setSquareNodes(square_node); connect((BlockBusItem *)item, SIGNAL(destroyed(QObject *)), this, SLOT(removedBus(QObject *)), Qt::UniqueConnection); if (emit_action) emitActionEvent(BlockItemBase::BusAdd, QList() << item); emit connectionsChanged(); return; } if (item->data(bvidType).toInt() == bvitBlock) { connect((BlockItem *)item, SIGNAL(destroyed(QObject *)), this, SLOT(removedBlock(QObject *)), Qt::UniqueConnection); connect((BlockItem *)item, SIGNAL(blockHoverEnter(BlockItem *)), this, SIGNAL(blockHoverEnter(BlockItem *)), Qt::UniqueConnection); connect((BlockItem *)item, SIGNAL(blockHoverLeave(BlockItem *)), this, SIGNAL(blockHoverLeave(BlockItem *)), Qt::UniqueConnection); if (emit_action) emitActionEvent(BlockItemBase::BlockAdd, QList() << item); return; } item->setData(bvidType, bvitDecor); } QList BlockView::buses() const { QList ret; QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) if ((i->data(bvidType).toInt() == bvitBus) && !i->data(bvidTmpItem).toBool()) if (!copy_buses.contains((BlockBusItem *)i)) ret << qgraphicsitem_cast(i); return ret; } QList BlockView::wrongConnectedBuses() const { QList sl = buses(), ret; QList bl = blocks(); foreach(BlockItem * b, bl) { QVector pins = b->pins(); foreach(BlockItemPin * p, pins) if (p->state() == BlockItemPin::Reject) { QPointF pp = p->scenePos(); foreach(BlockBusItem * s, sl) if (s->pol.contains(pp)) if (!ret.contains(s)) ret << s; } } return ret; } QList BlockView::blocks() const { QList ret; QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) if ((i->data(bvidType).toInt() == bvitBlock) && !i->data(bvidTmpItem).toBool()) ret << qgraphicsitem_cast(i); return ret; } QList BlockView::decors() const { QList ret, gi = scene_->items(); foreach(QGraphicsItem * i, gi) if ((i->data(bvidType).toInt() == bvitDecor) && !i->data(bvidTmpItem).toBool() && !i->data(bvidItemSelection).toBool() && (i->parentItem() == 0) && (i != &sel_rect) && (i != &tmp_bus) && !tmp_buses.contains((BlockBusItem *)i)) ret << i; return ret; } BlockBusItem * BlockView::connectionBus(BlockItem * b0, BlockItem * b1) const { QList cbl = connectionBuses(b0, b1); if (cbl.isEmpty()) return 0; return cbl.front(); } QList BlockView::connectionBuses(BlockItem * b0, BlockItem * b1) const { if (!b0 || !b1) return QList(); QSet bs0 = QList2QSet(b0->connectedBuses()), bs1 = QList2QSet(b1->connectedBuses()); return (bs0 & bs1).values(); } bool BlockView::connectPins(BlockItemPin * p0, BlockItemPin * p1) { if (!p0 || !p1) return false; if (p0 == p1) return false; if (p0->busType() != p1->busType()) return false; QList bl0 = p0->connectedBuses(), bl1 = p1->connectedBuses(); if (!(QList2QSet(bl0) & QList2QSet(bl1)).isEmpty()) return true; BlockBusItem * nb = new BlockBusItem(); nb->setBusType(p0->busType()); loadBus(nb); if (!bl0.isEmpty() && !bl1.isEmpty()) { // connect two existing buses } else { if ((bl0.isEmpty() && !bl1.isEmpty()) || (bl1.isEmpty() && !bl0.isEmpty())) { // connect empty pin to existing bus BlockItemPin * ep = 0; BlockBusItem * eb = 0; if (bl0.isEmpty()) { ep = p0; eb = bl1[0]; } else { ep = p1; eb = bl0[0]; } double md = -1; int mi = -1; QPointF sp = ep->scenePos(); if (eb->pol.size() == 2) { eb->selSegment = 0; eb->addPoint((eb->pol[0] + eb->pol[1]) / 2.); eb->selPoint = -1; mi = 2; } else { for (int i = 0; i < eb->pol.size(); ++i) { if (eb->ends.contains(i)) continue; double cd = QVector2D(sp - eb->pol[i]).lengthSquared(); if (md < 0 || md > cd) { md = cd; mi = i; } } } if (mi < 0) { return false; } trace(ep->scenePos(), eb->pol[mi], nb); if (nb->pol.size() < 2) { delete nb; return false; } nb->pol.pop_back(); int lp = eb->pol.size(); eb->pol << nb->pol; for (int i = 0; i < nb->pol.size() - 1; ++i) eb->segments << QPair(lp + i, lp + i + 1); eb->segments << QPair(mi, lp + nb->pol.size() - 1); eb->updateGeometry(); delete nb; } else { // connect two empty pins trace(p0->scenePos(), p1->scenePos(), nb); if (nb->pol.isEmpty()) { delete nb; return false; } addItem(nb); } } reconnectAll(); newBusCreated(nb); emitActionEvent(BlockItemBase::BusAdd, QList() << nb); emit connectionsChanged(); return true; } void BlockView::setTransform(const QTransform & matrix, bool combine) { QGraphicsView::setTransform(matrix, combine); updateNavRect(); nav_target = _nav(); } void BlockView::centerOn(const QPointF & pos) { QGraphicsView::centerOn(pos); updateNavRect(); nav_target = _nav(); } void BlockView::centerOn(qreal x, qreal y) { QGraphicsView::centerOn(x, y); updateNavRect(); nav_target = _nav(); } void BlockView::centerOn(const QGraphicsItem * item) { QGraphicsView::centerOn(item); updateNavRect(); nav_target = _nav(); } void BlockView::fitInView(const QRectF & rect, Qt::AspectRatioMode aspectRatioMode) { QGraphicsView::fitInView(rect, aspectRatioMode); updateNavRect(); nav_target = _nav(); } void BlockView::fitInView(qreal x, qreal y, qreal w, qreal h, Qt::AspectRatioMode aspectRatioMode) { QGraphicsView::fitInView(x, y, w, h, aspectRatioMode); updateNavRect(); nav_target = _nav(); } void BlockView::fitInView(const QGraphicsItem * item, Qt::AspectRatioMode aspectRatioMode) { QGraphicsView::fitInView(item, aspectRatioMode); updateNavRect(); nav_target = _nav(); } void BlockView::fitInView() { QRectF r = _nav(); QGraphicsView::fitInView(itemsBoundingRect(), Qt::KeepAspectRatio); updateNavRect(); QRectF t = _nav(); QGraphicsView::fitInView(r, Qt::KeepAspectRatio); updateNavRect(); animateNav(t); } QRectF BlockView::itemsBoundingRect() const { QList gi = scene_->items(); if (gi.isEmpty()) return QRectF(); bool f = true; QRectF ret; foreach(QGraphicsItem * i, gi) if (i->isVisible() && (i != &tmp_bus) && !tmp_buses.contains((BlockBusItem *)i)) { if (!(i->data(bvidItemSelection).toBool()) && !i->data(bvidTmpItem).toBool()) { QRectF br = i->mapRectToScene(i->boundingRect()); if (br.width() <= 1 || br.height() <= 1) continue; if (f) ret = br; else ret |= br; f = false; } } return ret; } void BlockView::restoreSelState() { // qDebug() << "restoreSelState"; foreach(QGraphicsItem * i, sel_items) { i->setPos(i->data(bvidItemPos).toPointF()); } QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) if (i->data(bvidType).toInt() == bvitBus) { BlockBusItem * bi = qgraphicsitem_cast(i); bi->pol = bi->bpol; bi->prepareGeometryChange(); } } void BlockView::saveSelState() { // qDebug() << "saveSelState"; QList gi = scene_->items(); sel_items = scene_->selectedItems(); foreach(QGraphicsItem * i, gi) { i->setData(bvidSelected, i->isSelected()); i->setData(bvidItemPos, i->pos()); if (i->data(bvidType).toInt() == bvitBus) qgraphicsitem_cast(i)->bpol = qgraphicsitem_cast(i)->pol; } } void BlockView::saveBusesState() { QList bl = buses(); foreach(BlockBusItem * b, bl) b->saveState(); } void BlockView::restoreBusesState() { QList bl = buses(); foreach(BlockBusItem * b, bl) b->restoreState(); } void BlockView::applySelRect(QGraphicsSceneMouseEvent * me) { QList ci = sel_rect.collidingItems(Qt::IntersectsItemBoundingRect); QList gi = scene_->items(); bool add = me->modifiers().testFlag(Qt::ControlModifier); QList sil = scene_->selectedItems(); block_emit_selection = true; if (!add) clearSelection(); else { foreach(QGraphicsItem * i, gi) i->setSelected(i->data(bvidSelected).toBool()); } foreach(QGraphicsItem * i, ci) { i->setSelected(!i->isSelected()); } block_emit_selection = false; if (sil != scene_->selectedItems()) emit selectionChanged(); } void BlockView::applyGridStep() { QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) if (i->type() == QGraphicsItem::UserType + 2) qgraphicsitem_cast(i)->setGridStep(grid_step); } void BlockView::trace(QPointF scene_pos_from, QPointF scene_pos_to, BlockBusItem * bus, bool primary) { if (primary) { if (hpin) scene_pos_to = hpin->scenePos(); last_trace_from = scene_pos_from; trace_to = scene_pos_to; } QRect sr = scene_->sceneRect().toRect(); int dx = sr.left() / grid_step, dy = sr.top() / grid_step; QPoint dp(-dx, -dy), qpt = quantize(scene_pos_to, grid_step).toPoint() / grid_step + dp; QElapsedTimer tm; tm.restart(); wavetrace.resize(sr.size() / grid_step); wavetrace.fill(BlockViewWavetrace::Empty); QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) if (i->data(bvidType).toInt() == bvitBlock) { QRect ir = i->mapRectToScene(i->boundingRect()).toRect().normalized(); wavetrace.fill(QRect(ir.topLeft() / grid_step + dp, ir.bottomRight() / grid_step + dp), BlockViewWavetrace::Blocked); QVector pins = qgraphicsitem_cast(i)->pins(); foreach(BlockItemPin * p, pins) { if (p->busType() == bus->busType()) wavetrace.fill(quantize(p->scenePos(), grid_step).toPoint() / grid_step + dp, BlockViewWavetrace::Empty); if (!m_pin_mc && !p->connectedBuses().isEmpty()) wavetrace.fill(quantize(p->scenePos(), grid_step).toPoint() / grid_step + dp, BlockViewWavetrace::Blocked); } } if (m_trace_with_buses) { foreach(QGraphicsItem * i, gi) if (i->data(bvidType).toInt() == bvitBus) { BlockBusItem * b = qgraphicsitem_cast(i); if (!b) continue; for (int s = 0; s < b->segments.size(); ++s) { QPointF p0 = b->pol[b->segments[s].first], p1 = b->pol[b->segments[s].second], cp = p0; double sx(0.), sy(0.), dx = qAbs(p1.x() - p0.x()), dy = qAbs(p1.y() - p0.y()); double signx = (p1.x() >= p0.x() ? 1. : -1.), signy = (p1.y() >= p0.y() ? 1. : -1.); int steps(0); if ((dx + dy) < grid_step) continue; BlockViewWavetrace::CellState cs = BlockViewWavetrace::Blocked; if (dx >= dy) { // by x sx = grid_step; sy = sx * dy / dx; steps = qRound(dx / grid_step); cs = BlockViewWavetrace::HorizontalBus; } else { sy = grid_step; sx = sy * dx / dy; steps = qRound(dy / grid_step); cs = BlockViewWavetrace::VerticalBus; } sx *= signx; sy *= signy; // qDebug() << "fill" << p0 << "->" << p1 << "in" << steps << sx << sy; for (int j = 0; j <= steps; ++j) { QPoint tp = quantize(cp, grid_step).toPoint() / grid_step + dp; if (tp != qpt) wavetrace.fill(tp, (j > 0 && j < steps) ? cs : BlockViewWavetrace::Blocked); // qDebug() << " set" << cp << ((j > 0 && j < steps) ? cs : BlockViewWavetrace::Blocked); cp += QPointF(sx, sy); } } } } bus->clear(); if (wavetrace.trace(quantize(scene_pos_from, grid_step).toPoint() / grid_step + dp, qpt)) { wavetrace.gatherPath(); foreach(const QPoint & p, wavetrace.path()) { bus->appendPoint((p - dp) * grid_step); } } // qDebug() << quantize(scene_pos_from, grid_step).toPoint() / grid_step + dp << qpt; scene_->update(); } void BlockView::clearBusStates() { QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) if (i->data(bvidType).toInt() == bvitBus) { BlockBusItem * b = qgraphicsitem_cast(i); b->clearBusState(); } } void BlockView::matchBus() { match_bus = 0; bool bv = tmp_bus.isVisible(); QList gi = scene_->items(); QList buses; QList blockl; int sp = -1, ss = -1; QPointF point; iconnect = false; if (!tmp_bus.pol.isEmpty()) point = tmp_bus.pol.back(); foreach(QGraphicsItem * i, gi) { if (i != bus_from) { if (i->data(bvidType).toInt() == bvitBus) buses << qgraphicsitem_cast(i); if (i->data(bvidType).toInt() == bvitBlock) blockl << qgraphicsitem_cast(i); } } foreach(BlockBusItem * b, buses) { b->clearBusState(); b->selPoint = b->selSegment = -1; } if (!bv) return; BlockBusItem * b(0); if (m_pin_mc) { foreach(BlockItem * b_, blockl) foreach(BlockItemPin * p_, b_->pins()) if (p_->scenePos() == point) { return; } } // qDebug() << "1" << buses.size() << tmp_bus.pol; for (int i = 0; i < buses.size(); ++i) { b = buses[i]; b->testPoint(point, &sp, &ss, true); // qDebug() << i << sp << ss; if (sp >= 0 || ss >= 0) break; } if ((sp < 0 && ss < 0) || b == 0) return; // qDebug("2"); match_bus = b; if ((b->busType() != tmp_bus.busType()) || b->connections_.value(sp, 0) != 0) { b->setBusState(false); } else { if (b->max_ep >= 2) { if (bus_from == 0) { if (ss >= 0) { if (b->endpointCount() >= b->max_ep) { b->setBusState(false); return; } } if (sp >= 0) { if (b->endpointCount() + b->pointSegmentsCount(sp) - 2 >= b->max_ep) { b->setBusState(false); return; } } } else { int sep = b->endpointCount() + bus_from->endpointCount(); if (b->pointSegmentsCount(sp) == 1) sep--; if (bus_from->selPoint >= 0) if (bus_from->pointSegmentsCount(bus_from->selPoint) == 1) sep--; if (sep > b->max_ep) { b->setBusState(false); return; } } } iconnect = true; b->setBusState(true); b->selPoint = sp; b->selSegment = ss; } } bool BlockView::connectTmpToBus(BlockBusItem * bus) { if (bus == 0) return false; if (!bus->busState()) return false; if (tmp_bus.pol.size() < 2) return false; int np = bus->selPoint; if (np < 0) np = bus->addPoint(tmp_bus.pol.back()); if (np < 0) return false; tmp_bus.pol.pop_back(); int lp = bus->pol.size(); bus->pol << tmp_bus.pol; for (int i = 0; i < tmp_bus.pol.size() - 1; ++i) bus->segments << QPair(lp + i, lp + i + 1); bus->segments << QPair(np, lp + tmp_bus.pol.size() - 1); bus->updateGeometry(); tmp_bus.clear(); return true; } void BlockView::markPins(int bus_type) { unhoverPins(); QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) { if (i->data(bvidType).toInt() == bvitPin) { BlockItemPin * p = qgraphicsitem_cast(i); p->saveState(); if (m_pin_mc) { if (p->busType() == bus_type) p->setState(BlockItemPin::Accept); } else { if (p->busType() == bus_type) { if (p->state() == BlockItemPin::Disconnected) p->setState(BlockItemPin::Accept); } } } } } void BlockView::unmarkPins(bool to_normal) { unhoverPins(); QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) { if (i->data(bvidType).toInt() == bvitPin) { qgraphicsitem_cast(i)->restoreState(); if (to_normal) while (qgraphicsitem_cast(i)->restoreState()) ; } } } void BlockView::hoverAcceptedPin(BlockItemPin * pin, bool hover) { if (!pin) return; pin->enlargePin(hover); } void BlockView::unhoverPins(BlockItemPin * excl_pin) { QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) { if (excl_pin == ((BlockItemPin *)i)) continue; if (i->data(bvidType).toInt() == bvitPin) { ((BlockItemPin *)i)->enlargePin(false); } } } void BlockView::simplifyBuses() { QList bl = buses(); foreach(BlockBusItem * b, bl) b->simplify(); } bool BlockView::moveBuses(const QList & items, QPointF dp) { if (dp.isNull()) return true; QList gi = scene_->items(); QVector pins; QList buses_m_part, buses_m_all; QMap saved_pols; // qDebug() << "move" << dp; for (auto * i: items) if ((i->data(bvidType).toInt() == bvitBlock) && i->flags().testFlag(QGraphicsItem::ItemIsMovable)) pins << qgraphicsitem_cast(i)->pins(); for (auto * i: gi) { if (i->data(bvidType).toInt() == bvitBus) { auto * b = qgraphicsitem_cast(i); QList bpins = b->connectedPins(); if (bpins.isEmpty()) continue; bool affect = false; for (auto * i: pins) { if (bpins.contains(i)) { affect = true; break; } } if (!affect) continue; for (auto * p: pins) bpins.removeAll(p); if (bpins.isEmpty()) buses_m_all << b; else { buses_m_part << b; saved_pols[b] = b->pol; } } } bool incorrect_move = false; for (auto * b: buses_m_part) { for (auto * p: pins) { QList ends = b->connections_.keys(p); for (int i = 0; i < ends.size(); ++i) { QPointF pdp = dp; double ang = 0.; switch (p->alignment()) { case Qt::AlignRight: pdp.setX(0.); ang = 0.; break; case Qt::AlignTop: pdp.setY(0.); ang = 90.; break; case Qt::AlignLeft: pdp.setX(0.); ang = 180.; break; case Qt::AlignBottom: pdp.setY(0.); ang = 270.; break; default: break; } QVector epl = b->endpointLine(ends[i], ang); for (int e: epl) b->movePoint(e, pdp); b->movePoint(ends[i], dp); if (b->hasNullSegment()) { incorrect_move = true; break; } } if (incorrect_move) break; } if (incorrect_move) break; } if (incorrect_move) { for (auto * b: buses_m_part) b->pol = saved_pols.value(b); return false; } for (auto * b: buses_m_all) b->movePolyline(dp); return true; } QList BlockView::internalBuses(const QList & items) { QList ret; if (items.isEmpty()) return ret; QList sbl = buses(); QSet sis = QList2QSet(items); foreach(BlockBusItem * bi, sbl) { if (bi->connectedBlocks().isEmpty()) continue; QSet bis = QList2QSet(bi->connectedBlocks()); if ((bis - sis).isEmpty()) ret << bi; } return ret; } QList BlockView::nearPins(BlockItemPin * pin, Qt::KeyboardModifiers km) { QList ret; bool up = km.testFlag(Qt::ShiftModifier); bool down = km.testFlag(Qt::ControlModifier); bool ab = km.testFlag(Qt::AltModifier); // qDebug() << "nearPins" << km; if (!pin || (!up && !down)) return ret; const BlockItem * src = pin->parent(); if (!src) return ret; bool dirs[2] = {up, down}; double dy[2] = {-1, 1}; for (int i = 0; i < 2; ++i) { if (!dirs[i]) continue; const BlockItem * cb = src; BlockItemPin * cpin = pin; // qDebug() << "dir" << dirs[i]; while (cpin) { // qDebug() << "cur" << cpin; QList il = scene_->items(cpin->scenePos() + QPointF(0., dy[i] * cb->pinsMargin())); BlockItemPin * np = getPin(il); if (np == cpin) break; cpin = np; if (cpin) { if (((cb != cpin->parent()) && !ab) || (cpin->state() != BlockItemPin::Disconnected)) { break; } cb = cpin->parent(); ret << cpin; } } } return ret; } BlockItemPin * BlockView::getPin(const QList & list) const { foreach(QGraphicsItem * i, list) { if (i->data(bvidType).toInt() == bvitPin) return qgraphicsitem_cast(i); } return 0; } void BlockView::highlightNearPins(BlockItemPin * pin, Qt::KeyboardModifiers km) { // qDebug() << "restore for" << last_multiconnect_pl.size(); foreach(BlockItemPin * p, last_multiconnect_pl) p->restoreState(); QList pl = nearPins(pin, km); foreach(BlockItemPin * p, pl) { p->saveState(); p->setState(BlockItemPin::Hover); } last_multiconnect_pl = pl; } void BlockView::hideTmpBuses(bool clear) { tmp_bus.hide(); if (clear) { tmp_bus.clear(); qDeleteAll(tmp_buses); tmp_buses.clear(); } } QList BlockView::selectedBlocks() const { QList ret; QList sil = scene()->selectedItems(); foreach(QGraphicsItem * b, sil) if ((b->data(bvidType).toInt() == bvitBlock) && !b->data(bvidTmpItem).toBool()) ret << qgraphicsitem_cast(b); return ret; } QList BlockView::selectedDecors() const { QList ret, sil = decors(); foreach(QGraphicsItem * b, sil) if (b->isSelected()) ret << b; return ret; } void BlockView::animateNav(QRectF d, double scl) { nav_target = d; if (is_nav_anim) { if (qAbs(scl - 1.) <= 0.1 && (nav_anim.state() != QAbstractAnimation::Running)) { _setNav(d); return; } if (nav_anim.state() != QAbstractAnimation::Running) { nav_prev_aa = renderHints().testFlag(QPainter::Antialiasing); nav_prev_imaa = renderHints().testFlag(QPainter::SmoothPixmapTransform); nav_prev_grid = isGridVisible(); setRenderHint(QPainter::Antialiasing, false); setRenderHint(QPainter::SmoothPixmapTransform, false); setGridVisible(false); } nav_anim.stop(); nav_anim.setStartValue(_nav()); nav_anim.setEndValue(d); nav_anim.start(); } else _setNav(d); } void BlockView::adjustThumb() { if (!scene()) return; QSizeF sr = sceneRect().size(), tr; double scl; if (sr.width() >= sr.height()) scl = thumb_size.width() / sr.width(); else scl = thumb_size.height() / sr.height(); tr = sr * scl; int sx = 0, sy = 0; if (verticalScrollBar()->isVisible() && (verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)) sx = verticalScrollBar()->width(); if (horizontalScrollBar()->isVisible() && (horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)) sy = horizontalScrollBar()->height(); widget_thumb.setGeometry(QRect(QPoint(width() - tr.width() - 10 - sx, height() - tr.height() - 10 - sy), tr.toSize())); } void BlockView::newBranch(BlockBusItem * item) { bus_from = item; prev_tcb = m_trace_with_buses; startNewBus(item->busType()); new_branch = true; tmp_bus.setBusType(item->busType()); if (item->selSegment >= 0) { QPointF ds(item->pol[item->segments[item->selSegment].first] - item->pol[item->segments[item->selSegment].second]); if (ds.x() == 0.) wavetrace.setPreferredDirection(BlockViewWavetrace::Horizontal); if (ds.y() == 0.) wavetrace.setPreferredDirection(BlockViewWavetrace::Vertical); } } void BlockView::startBusPointMove(int bus_type) { move_bus_point = true; prev_tcb = m_trace_with_buses; startNewBus(bus_type); } void BlockView::pinHoverInOut(BlockItemPin * pin) { // qDebug() << "pinHoverInOut" << pin << pin->state(); highlightNearPins(pin, QApplication::keyboardModifiers()); } void BlockView::checkPaste(bool queued) { const QMimeData * mime = QApplication::clipboard()->mimeData(); bool ret = false; if (mime) ret = mime->hasFormat(_BlockView_Mime_); if (queued) QMetaObject::invokeMethod( this, [this, ret]() { pasteEnabledChanged(ret); }, Qt::QueuedConnection); else emit pasteEnabledChanged(ret); } void BlockView::setBusSquareNodes(bool yes) { square_node = yes; QList sbl = buses(); foreach(BlockBusItem * b, sbl) { b->setSquareNodes(square_node); } } void BlockView::newBranchTrace(BlockBusItem * item, QPointF to) { trace(item->press_pos, to, &tmp_bus); tmp_bus.show(); } void BlockView::newBranchAccept(BlockBusItem * item) { unmarkPins(); if (!new_branch) return; new_branch = false; tmp_bus.hide(); if (tmp_bus.pol.size() < 2) return; tmp_bus.pol.pop_front(); if ((tmp_bus.pol.size() == 1) && (tmp_bus.pol[0] == item->press_pos)) return; int np = item->addPoint(item->press_pos); if (np < 0) return; if (match_bus) { if (iconnect) tmp_bus.pol.pop_back(); else return; } if (item == match_bus) return; int snp = np; int lp = item->pol.size(); if (!tmp_bus.pol.isEmpty()) { item->pol << tmp_bus.pol; item->segments << QPair(np, lp); for (int i = 0; i < tmp_bus.pol.size() - 1; ++i) item->segments << QPair(lp + i, lp + i + 1); } if (match_bus != 0) { if (!iconnect) return; np = match_bus->selPoint; if (np < 0) np = match_bus->addPoint(trace_to); if (np < 0) return; lp = item->pol.size(); item->pol << match_bus->pol; for (int i = 0; i < match_bus->segments.size(); ++i) item->segments << QPair(match_bus->segments[i].first + lp, match_bus->segments[i].second + lp); if (tmp_bus.pol.isEmpty()) item->segments << QPair(lp + np, snp); else item->segments << QPair(lp + np, lp - 1); match_bus->setProperty("_nodelete_", true); match_bus->deleteLater(); match_bus = 0; } item->updateGeometry(); newBusCreated(item); tmp_bus.clear(); } void BlockView::newBranchCancel() { unmarkPins(); // qDebug() << "cancel"; new_branch = false; hideTmpBuses(); } void BlockView::removedBus(QObject * o) { mm_ci = 0; reconnectAll(); BlockBusItem * bus = (BlockBusItem *)o; if (bus->property("_nodelete_").toBool()) return; emitActionEvent(BlockItemBase::BusRemove, QList() << bus); emit connectionsChanged(); } void BlockView::removedBlock(QObject * o) { if (o == ghost_) return; emit blockRemoved((BlockItem *)o); emitActionEvent(BlockItemBase::BlockRemove, QList() << qgraphicsitem_cast((BlockItem *)o)); } void BlockView::removeJunk() { QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) { if (i->data(bvidType).toInt() != bvitBus) continue; BlockBusItem * b = qgraphicsitem_cast(i); if (b->pol.size() <= 1) { b->deleteLater(); } } } void BlockView::sceneSelectionChanged() { bool ie = scene()->selectedItems().isEmpty(); emit copyEnabledChanged(!ie); if (!block_emit_selection) emit selectionChanged(); } void BlockView::_setThumb(double v) { _talpha = v; QWidget * w = &widget_thumb; QMetaObject::invokeMethod( this, [w]() { w->update(); }, Qt::QueuedConnection); if (_talpha <= 0.01) widget_thumb.hide(); else widget_thumb.show(); } void BlockView::_setNav(QRectF v) { double vw = viewport()->width(), vh = viewport()->height(); if (vw < 1. || vh < 1. || v.isEmpty()) return; QTransform matrix; double scl = qMin(vw / v.width(), vh / v.height()); double ascl = appScale(this); if (scl > 0.02 * ascl && scl < 50.0 * ascl) { matrix.scale(scl, scl); nav_rect = v; QGraphicsView::setTransform(matrix); QGraphicsView::centerOn(v.center()); } } QRectF BlockView::_nav() const { return nav_rect; } void BlockView::_navFinished() { setRenderHint(QPainter::Antialiasing, nav_prev_aa); setRenderHint(QPainter::SmoothPixmapTransform, nav_prev_imaa); setGridVisible(nav_prev_grid); } void BlockView::reconnectAll() { // qDebug() << "reconnect"; removeJunk(); QList gi = scene_->items(); QList pins; QList buses; foreach(QGraphicsItem * i, gi) { if (i->data(bvidType).toInt() == bvitPin) pins << qgraphicsitem_cast(i); if (i->data(bvidType).toInt() == bvitBus) buses << qgraphicsitem_cast(i); } foreach(BlockItemPin * p, pins) { p->clearStateStack(); p->setState(BlockItemPin::Disconnected); p->buses_.clear(); } foreach(BlockBusItem * b, buses) { b->connections_.clear(); QVector conns(b->endpoints()); for (int c = 0; c < conns.size(); ++c) { QPointF cp = b->pol[conns[c]]; for (int j = 0; j < pins.size(); ++j) { BlockItemPin * pin = pins[j]; if (!pin->isVisible()) continue; QPointF pp = pin->scenePos(); if ((cp - pp).manhattanLength() <= (grid_step / 2.)) { // qDebug() << "found"; if (b->busType() == pin->busType()) { b->connections_[conns[c]] = pin; if (!pin->buses_.contains(b)) pin->buses_ << b; if (m_pin_mc) { pin->setState(BlockItemPin::Connected); } else { pin->setState(pin->buses_.size() == 1 ? BlockItemPin::Connected : BlockItemPin::Reject); } } else pin->setState(BlockItemPin::Reject); break; } } } } // qDebug() << pins.size() << buses.size(); } void BlockView::zoom(double factor) { if (!is_nav_anim || (nav_anim.state() != QPropertyAnimation::Running)) nav_target = _nav(); QRectF r = nav_target; QPoint mp; if (underMouse()) mp = mapFromGlobal(QCursor::pos()); else mp = QPoint(viewport()->width() / 2, viewport()->height() / 2); double cx = double(mp.x()) / viewport()->width(), cy = double(mp.y()) / viewport()->height(); double pw = r.width(), ph = r.height(); r.setWidth(r.width() / factor); r.setHeight(r.height() / factor); r.translate(cx * (pw - r.width()), cy * (ph - r.height())); animateNav(r); } void BlockView::zoomReset() { cur_scl = qSqrt(transform().determinant()); zoom(prev_app_scale / cur_scl); } void BlockView::copyToClipboard() { QList bll = selectedBlocks(); QList del = selectedDecors(); // qDebug() << "copy" << bll.size() << del.size(); if (bll.isEmpty() && del.isEmpty()) return; QList bul = internalBuses(bll); QByteArray ba; QDataStream s(&ba, QIODevice::ReadWrite); s << uint(0x89abcdef) << bll << bul << del; QMimeData * mime = new QMimeData(); mime->setData(_BlockView_Mime_, ba); QApplication::clipboard()->setMimeData(mime); } void BlockView::pasteFromClipboard() { const QMimeData * mime = QApplication::clipboard()->mimeData(); if (!mime) return; if (!mime->hasFormat(_BlockView_Mime_)) return; QByteArray ba = mime->data(_BlockView_Mime_); // qDebug() << "paste" << ba.size(); if (ba.size() < 4) return; QList bll; QList bul; QList del, gl; uint hdr = 0; QDataStream s(ba); s >> hdr; if (hdr != 0x89abcdef) return; s >> bll >> bul >> del; int all = bll.size() + bul.size() + del.size(); if (all == 0) return; QRectF br; foreach(BlockItem * b, bll) { br |= b->boundingRect().translated(b->pos()); gl << b; } foreach(BlockBusItem * b, bul) gl << b; foreach(QGraphicsItem * b, del) br |= b->boundingRect().translated(b->pos()); gl << del; QPointF copy_dp; if (underMouse()) copy_dp = mapToScene(mapFromGlobal(QCursor::pos())); else copy_dp = mapToScene(rect().center()); copy_dp -= br.center(); copy_dp = quantize(copy_dp, grid_step); ae_enabled = false; if (!bll.isEmpty()) copyBlocks(bll, copy_dp); if (!bul.isEmpty()) copyBuses(bul, copy_dp); foreach(QGraphicsItem * i, del) i->setPos(i->pos() + copy_dp); addItems(del); ae_enabled = true; emitActionEvent(BlockItemBase::Paste, gl); } void BlockView::selectNone() { bool pb = block_emit_selection; block_emit_selection = true; QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) i->setSelected(false); block_emit_selection = pb; if (!block_emit_selection) emit selectionChanged(); } void BlockView::selectAll() { bool pb = block_emit_selection; block_emit_selection = true; QList gi = scene_->items(); foreach(QGraphicsItem * i, gi) if (i->flags().testFlag(QGraphicsItem::ItemIsSelectable)) i->setSelected(true); block_emit_selection = pb; if (!block_emit_selection) emit selectionChanged(); } void BlockView::removeSelected() { QList gi = scene_->selectedItems(), ai; blockSignals(true); QList sbuses = buses(), dbuses, wbuses = wrongConnectedBuses(); foreach(BlockBusItem * i, sbuses) if (i->connectedBlocks().isEmpty()) dbuses << i; foreach(QGraphicsItem * i, gi) { if (i->data(bvidTmpItem).toBool()) continue; if (i->data(bvidType).toInt() == bvitBlock) ai << qgraphicsitem_cast(i); if ((i->data(bvidType).toInt() == bvitBlock) || (i->data(bvidType).toInt() == bvitBus) || (i->data(bvidType).toInt() == bvitDecor)) { scene_->sendEvent(i, new QGraphicsSceneEvent(QEvent::Close)); delete i; } } reconnectAll(); foreach(BlockBusItem * i, sbuses) if (i->connectedBlocks().isEmpty()) if (!dbuses.contains(i)) delete i; blockSignals(false); foreach(QGraphicsItem * i, ai) emit blockRemoved((BlockItem *)i); emitActionEvent(BlockItemBase::BlockRemove, ai); } void BlockView::removeAll() { last_multiconnect_pl.clear(); QList gi = scene_->items(), ai; blockSignals(true); match_bus = bus_from = cur_bus = nullptr; mm_ci = nullptr; hpin = nullptr; ghost_ = nullptr; foreach(QGraphicsItem * i, gi) { if (i->data(bvidTmpItem).toBool()) continue; if (i->data(bvidType).toInt() == bvitBlock) ai << qgraphicsitem_cast(i); if ((i->data(bvidType).toInt() == bvitBlock) || (i->data(bvidType).toInt() == bvitBus) || (i->data(bvidType).toInt() == bvitDecor)) { if ((i != &sel_rect) && (i != &tmp_bus) && (i->parentItem() == 0) && !(i->data(bvidItemSelection).toBool())) { // qDebug() << "delete" << i->data(1005).toInt(); scene_->sendEvent(i, new QGraphicsSceneEvent(QEvent::Close)); delete i; } } } blockSignals(false); foreach(QGraphicsItem * i, ai) emit blockRemoved((BlockItem *)i); }