This repository has been archived on 2020-09-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
libs/qad/blockview/blockview.cpp

2055 lines
58 KiB
C++

#include "blockview.h"
#include <qmath.h>
#include <QScrollBar>
#include <QGraphicsSceneMouseEvent>
#include <QApplication>
#include <QAction>
#include <QShortcut>
#include <QVector2D>
#include <QClipboard>
#include <QMimeData>
#include <QElapsedTimer>
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<BlockItem*>();
qRegisterMetaType<BlockItemPin*>();
qRegisterMetaType<BlockBusItem*>();
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 = 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 = 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.setAutoFillBackground(true);
//widget_thumb.setStyleSheet("background-color: rgb(255, 0, 0);");
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() + 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;
}
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<QGraphicsItem * > mil;
QPointF mdp;
bool fmm_drag = false;
int btncnt = 0;
switch (e->type()) {
case QEvent::GraphicsSceneMouseDoubleClick:
mil = scene_->items(scene_point);
foreach (QGraphicsItem * i, mil) {
if (i->data(1006) == "item") {
//emit blockDoubleClicked((BlockItem * )i);
QMetaObject::invokeMethod(this, "blockDoubleClicked", Qt::QueuedConnection, Q_ARG(BlockItem * , (BlockItem*)i));
return true;
}
if (i->data(1005) == "connection") {
if (qgraphicsitem_cast<BlockBusItem*>(i)->isBusSelected()) {
//emit busDoubleClicked(qgraphicsitem_cast<BlockBusItem*>(i));
QMetaObject::invokeMethod(this, "busDoubleClicked", Qt::QueuedConnection, Q_ARG(BlockBusItem * , (BlockBusItem*)i));
return true;
}
}
}
//return true;
break;
/*case QEvent::GraphicsSceneHoverMove:
mil = scene_->items(scene_point);
mm_ci = (mil.isEmpty() ? 0 : mil.front());
if (mm_ci != 0) {
while (mm_ci->data(1005).toString() == "connection") {
if (qgraphicsitem_cast<BlockBusItem*>(mm_ci))
if (qgraphicsitem_cast<BlockBusItem*>(mm_ci)->isBusSelected())
break;
if (mil.size() > 1) {
mm_ci = mil[1];
mil.pop_front();
} else {
mm_ci = 0;
break;
}
}
}
break;*/
case QEvent::GraphicsSceneMousePress:
if (mm_ci != 0) {
if (mm_ci->data(1008).toBool()) {
mm_ci = 0;
break;
}
//if (mm_ci->data(1007).toBool()) break;
}
//qDebug() << "press";
if (me->buttons().testFlag(Qt::LeftButton)) btncnt++;
if (me->buttons().testFlag(Qt::RightButton)) btncnt++;
if (me->buttons().testFlag(Qt::MidButton)) btncnt++;
mm_cancel = btncnt >= 2;
match_bus = bus_from = 0;
hpin = 0;
copy_dp = QPointF();
//qDebug() << mm_cancel << mm_copy << mm_drag << new_branch << new_bus;
if (mm_copy && mm_cancel) {
deleteCopyTemp();
mm_copy = moved = false;
unsetCursor();
}
if (new_bus && mm_cancel) {
new_bus = false;
unmarkPins(true);
reconnectAll();
hideTmpBuses();
m_trace_with_buses = prev_tcb;
}
if (new_branch && mm_cancel) {
new_branch = false;
hideTmpBuses();
m_trace_with_buses = prev_tcb;
}
if (moved && mm_cancel) {
moved = false;
restoreSelState();
hideTmpBuses();
m_trace_with_buses = prev_tcb;
}
if (mm_cancel) return true;
mm_mods = me->modifiers();
mm_drag = moved = false;
screen_point = me->screenPos();
scene_point = me->scenePos();
if ((me->button() == Qt::MidButton)) {
thumbShow();
restartTimer(timer_thumb, thumb_hide_delay);
return true;
}
mil = scene_->items(scene_point);
//qDebug() << mil;
while (!mil.isEmpty()) {
mm_ci = mil.front();
if (mm_ci->data(bvidDTHandle).toBool()) return QGraphicsView::eventFilter(o, e);
if (mm_ci->data(1008).toBool() || mm_ci->data(1007).toBool()) {
mil.pop_front();
} else break;
}
if (mil.isEmpty()) {
mm_ci = 0;
return true;
}
while (mm_ci->data(1005).toString() == "connection") {
if (qgraphicsitem_cast<BlockBusItem*>(mm_ci))
if (qgraphicsitem_cast<BlockBusItem*>(mm_ci)->isBusSelected())
break;
if (mil.size() > 1) {
mm_ci = mil[1];
mil.pop_front();
} else {
mm_ci = 0;
break;
}
}
if (mm_ci->data(1003).toBool()) {
if (mil.size() > 1) {
mm_ci = mil[1];
mil.pop_front();
if (mm_ci->data(1003).toBool())
if (mil.size() > 1)
mm_ci = mil[1];
} else
mm_ci = 0;
}
if (mm_ci->data(1002).toBool()) {
QGraphicsItem * ti = mm_ci;
while (mm_ci->parentItem() != 0)
mm_ci = mm_ci->parentItem();
if (!ti->data(1010).toString().isEmpty()) { // text item, check for rect
BlockItem * bi = qgraphicsitem_cast<BlockItem*>(mm_ci);
if (bi) {
if (!bi->sceneRect().contains(scene_point)) {
//qDebug() << "return";
mm_ci = 0;
}
}
}
return true;
}
if (mm_ci) {
if (mm_ci->data(1004) == "pin" && m_connect) {
if (qgraphicsitem_cast<BlockItemPin*>(mm_ci)->state() == BlockItemPin::Hover) {
trace_from = mm_ci->scenePos();
qgraphicsitem_cast<BlockItemPin*>(mm_ci)->clearStateStack();
hideTmpBuses();
tmp_bus.setBusType(qgraphicsitem_cast<BlockItemPin*>(mm_ci)->busType());
tmp_bus.setEndpointsNumber(3);
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;
newBusStarted(tmp_bus.busType());
markPins(tmp_bus.busType());
if (qgraphicsitem_cast<BlockItemPin*>(mm_ci)->alignment() == Qt::AlignLeft ||
qgraphicsitem_cast<BlockItemPin*>(mm_ci)->alignment() == Qt::AlignRight)
wavetrace.setPreferredDirection(BlockViewWavetrace::Horizontal);
else
wavetrace.setPreferredDirection(BlockViewWavetrace::Vertical);
return true;
}
}
}
cur_scl = qSqrt(transform().determinant());
//return true;
break;
case QEvent::GraphicsSceneMouseMove:
//qDebug() << "move" << (mm_ci != 0 ? mm_ci : 0) << mm_mods << mm_cancel << mm_drag;
/*if (ghost_) {
ghost_->setPos(quantize(me->scenePos(), grid_step));
}*/
if (mm_ci)
if (mm_ci->data(1008).toBool()) {
mm_ci = 0;
break;
}
if (mm_ci->data(1007).toBool()) break;
if (mm_ci->data(bvidDTHandle).toBool()) break;
if (mm_cancel) return true;
if (me->buttons().testFlag(Qt::LeftButton)) {
if (!mm_drag) {
if ((screen_point - me->screenPos()).manhattanLength() >= QApplication::startDragDistance()) {
mm_drag = fmm_drag = true;
saveBusesState();
}
} else {
if (tmp_bus.isVisible()) {
mil = scene_->items(me->scenePos());
hpin = 0;
foreach (QGraphicsItem * i, mil)
if (i->data(1004) == "pin") {
hpin = qgraphicsitem_cast<BlockItemPin*>(i);
break;
}
if (hpin) {
if (hpin->state() == BlockItemPin::Accept) {
unhoverPins(hpin);
hoverAcceptedPin(hpin, true);
} else hpin = 0;
} else unhoverPins();
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 (tmp_bus.isVisible()) return true;
if (mm_mods.testFlag(Qt::ShiftModifier)) {
if (fmm_drag) {
fmm_drag = false;
if (mm_ci) {
if ((mm_ci->data(1006) == "item")) {
if (!mm_ci->isSelected() && sel_items.isEmpty()) {
clearSelection();
mm_ci->setSelected(true);
}
} else {
mm_ci = 0;
break;
}
} else {
break;
}
sel_items = scene_->selectedItems();
deleteCopyTemp();
QList<BlockItem * > bi;
foreach (QGraphicsItem * i, sel_items) {
if (i->data(1006) == "item") {
//qDebug() << "copy";
bi << qgraphicsitem_cast<BlockItem*>(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<BlockBusItem * > 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 {
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;
foreach (QGraphicsItem * i, sel_items)
if (i->flags().testFlag(QGraphicsItem::ItemIsMovable))
i->setPos(i->pos() + mdp);
if (!me->modifiers().testFlag(Qt::AltModifier))
moveBuses(sel_items, mdp);
setCursor(Qt::ClosedHandCursor);
}
return true;
}
}
if (mm_ci)
if (mm_ci->data(1100).toBool())
return true;
}
if (me->modifiers().testFlag(Qt::ControlModifier) && me->buttons() != 0 && mm_ci == 0)
return true;
//qDebug() << "scene mouse";
//return true;
break;
case QEvent::GraphicsSceneMouseRelease:
if (me->buttons().testFlag(Qt::LeftButton)) btncnt++;
if (me->buttons().testFlag(Qt::RightButton)) btncnt++;
if (me->buttons().testFlag(Qt::MidButton)) btncnt++;
mm_cancel = btncnt > 0;
if (mm_cancel || (me->button() == Qt::MidButton) || (me->button() == Qt::RightButton)) {
mm_ci = 0;
return true;
}
if (mm_ci)
if (mm_ci->data(1008).toBool()) {
mm_ci = 0;
break;
}
if (mm_ci->data(1007).toBool()) break;
if (mm_copy) {
QList<QGraphicsItem*> ai;
blockSignals(true);
if (moved) {
QList<BlockItem*> ci;
QList<BlockBusItem*> bi;
foreach (QGraphicsItem * b, sel_items)
if (b->data(1006) == "item") {
ci << qgraphicsitem_cast<BlockItem*>(b);
ai << qgraphicsitem_cast<QGraphicsItem*>(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<QGraphicsItem*> ci;
foreach (QGraphicsItem * b, sel_items)
if (b->data(1006) == "item")
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());
foreach (BlockBusItem * b, tmp_buses) {
nb = new BlockBusItem(*b);
addItem(nb, b == tmp_buses.back());
}
} else {
if (connectTmpToBus(match_bus)) {
emitActionEvent(BlockItemBase::BusAdd, QList<QGraphicsItem*>() << 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(1006) == "item")
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)) {
double scl = 1. - e->delta() / 500.;
if (!is_nav_anim || (nav_anim.state() != QPropertyAnimation::Running))
nav_target = _nav();
QRectF r = nav_target;
double vw = viewport()->width(), vh = viewport()->height();
double cx = double(e->pos().x()) / vw, cy = double(e->pos().y()) / vh;
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::MidButton) || event->buttons().testFlag(Qt::RightButton)) {
setCursor(Qt::ClosedHandCursor);
sel_rect.hide();
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::MidButton) || 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::MidButton) || 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();
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);
QMetaObject::invokeMethod(&widget_thumb, "update", Qt::QueuedConnection);
}
void BlockView::drawBackground(QPainter * painter, const QRectF & rect) {
// QGraphicsView::drawBackground(painter, 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<int>(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<int>(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<QGraphicsItem * > 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::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();
//widget_thumb.hide();
}
void BlockView::thumbShow() {
if (!minimap) return;
if (widget_thumb.isHidden() || (_talpha < 0.1)) drawSceneThumb();
thumb_anim.stop();
/*thumb_anim.setDuration(100);
thumb_anim.setStartValue(_thumb());
thumb_anim.setEndValue(1.);
thumb_anim.start();*/
_setThumb(1.);
//qDebug() << "show" << thumb_anim.startValue() << thumb_anim.endValue();
//widget_thumb.show();
}
void BlockView::clearSelection() {
sel_items.clear();
QList<QGraphicsItem*> gi = scene_->items();
foreach (QGraphicsItem * i, gi)
if (i->flags().testFlag(QGraphicsItem::ItemIsSelectable)) i->setSelected(false);
}
void BlockView::addItem(QGraphicsItem * item, bool emit_action) {
scene_->addItem(item);
applyGridStep();
if (item->data(1005) == "connection") {
loadBus(qgraphicsitem_cast<BlockBusItem*>(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<QGraphicsItem*>() << item);
emit connectionsChanged();
return;
}
if (item->data(1006) == "item") {
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<QGraphicsItem*>() << item);
return;
}
item->setData(1009, "decor");
}
QList<BlockBusItem * > BlockView::buses() const {
QList<BlockBusItem * > ret;
QList<QGraphicsItem*> gi = scene_->items();
foreach (QGraphicsItem * i, gi)
if (i->data(1005) == "connection")
if (!copy_buses.contains((BlockBusItem*)i))
ret << qgraphicsitem_cast<BlockBusItem*>(i);
return ret;
}
QList<BlockBusItem * > BlockView::wrongConnectedBuses() const {
QList<BlockBusItem * > sl = buses(), ret;
QList<BlockItem * > bl = blocks();
foreach (BlockItem * b, bl) {
QVector<BlockItemPin * > 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<BlockItem * > BlockView::blocks() const {
QList<BlockItem * > ret;
QList<QGraphicsItem*> gi = scene_->items();
foreach (QGraphicsItem * i, gi)
if (i->data(1006) == "item")
ret << qgraphicsitem_cast<BlockItem*>(i);
return ret;
}
QList<QGraphicsItem * > BlockView::decors() const {
QList<QGraphicsItem*> ret, gi = scene_->items();
foreach (QGraphicsItem * i, gi)
if ((i->data(1009) == "decor") && !i->data(1008).toBool() && !i->data(1007).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<BlockBusItem * > cbl = connectionBuses(b0, b1);
if (cbl.isEmpty()) return 0;
return cbl.front();
}
QList<BlockBusItem * > BlockView::connectionBuses(BlockItem * b0, BlockItem * b1) const {
if (!b0 || !b1) return QList<BlockBusItem * >();
QSet<BlockBusItem * > bs0 = b0->connectedBuses().toSet(), bs1 = b1->connectedBuses().toSet();
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<BlockBusItem * > bl0 = p0->connectedBuses(), bl1 = p1->connectedBuses();
if (!(bl0.toSet() & bl1.toSet()).isEmpty()) return true;
BlockBusItem * nb = new BlockBusItem();
nb->setBusType(p0->busType());
//nb->setEndpointsNumber(3);
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<int, int>(lp + i, lp + i + 1);
eb->segments << QPair<int, int>(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();
emitActionEvent(BlockItemBase::BusAdd, QList<QGraphicsItem*>() << 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<QGraphicsItem*> 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(1007).toBool()) && !i->data(1008).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() {
foreach (QGraphicsItem * i, sel_items) {
i->setPos(i->data(1001).toPointF());
}
QList<QGraphicsItem*> gi = scene_->items();
foreach (QGraphicsItem * i, gi)
if (i->data(1005) == "connection") {
BlockBusItem * bi = qgraphicsitem_cast<BlockBusItem*>(i);
bi->pol = bi->bpol;
bi->prepareGeometryChange();
}
}
void BlockView::saveSelState() {
QList<QGraphicsItem*> gi = scene_->items();
sel_items = scene_->selectedItems();
foreach (QGraphicsItem * i, gi) {
i->setData(1000, i->isSelected());
i->setData(1001, i->pos());
if (i->data(1005) == "connection")
qgraphicsitem_cast<BlockBusItem*>(i)->bpol = qgraphicsitem_cast<BlockBusItem*>(i)->pol;
}
}
void BlockView::saveBusesState() {
QList<BlockBusItem*> bl = buses();
foreach (BlockBusItem * b, bl)
b->saveState();
}
void BlockView::restoreBusesState() {
QList<BlockBusItem*> bl = buses();
foreach (BlockBusItem * b, bl)
b->restoreState();
}
void BlockView::applySelRect(QGraphicsSceneMouseEvent * me) {
QList<QGraphicsItem*> ci = sel_rect.collidingItems(Qt::IntersectsItemBoundingRect);
QList<QGraphicsItem*> gi = scene_->items();
bool add = me->modifiers().testFlag(Qt::ControlModifier);
if (!add) clearSelection();
else {
foreach (QGraphicsItem * i, gi)
i->setSelected(i->data(1000).toBool());
}
foreach (QGraphicsItem * i, ci) {
i->setSelected(!i->isSelected());
}
}
void BlockView::applyGridStep() {
QList<QGraphicsItem*> gi = scene_->items();
foreach (QGraphicsItem * i, gi)
if (i->type() == QGraphicsItem::UserType + 2)
qgraphicsitem_cast<BlockBusItem*>(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;
//qDebug() << dp;
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<QGraphicsItem*> gi = scene_->items();
foreach (QGraphicsItem * i, gi)
if (i->data(1006) == "item") {
QRect ir = i->mapRectToScene(i->boundingRect()).toRect().normalized();
wavetrace.fill(QRect(ir.topLeft() / grid_step + dp, ir.bottomRight() / grid_step + dp), BlockViewWavetrace::Blocked);
QVector<BlockItemPin * > pins = qgraphicsitem_cast<BlockItem * >(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_trace_with_buses) {
foreach (QGraphicsItem * i, gi)
if (i->data(1005) == "connection") {
BlockBusItem * b = qgraphicsitem_cast<BlockBusItem * >(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, cs);
//qDebug() << " set" << cp;
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<QGraphicsItem * > gi = scene_->items();
foreach (QGraphicsItem * i, gi)
if (i->data(1005) == "connection") {
BlockBusItem * b = qgraphicsitem_cast<BlockBusItem*>(i);
b->clearBusState();
// if (i != bus_from && i != match_bus)
//b->selPoint = b->selSegment = -1;
}
}
void BlockView::matchBus() {
match_bus = 0;
bool bv = tmp_bus.isVisible();
QList<QGraphicsItem * > gi = scene_->items();
QList<BlockBusItem * > buses;
QList<BlockItem * > 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(1005) == "connection")
buses << qgraphicsitem_cast<BlockBusItem*>(i);
if (i->data(1006) == "item")
blockl << qgraphicsitem_cast<BlockItem*>(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);
//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;
}
}
}
//b->press_point = point;
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<int, int>(lp + i, lp + i + 1);
bus->segments << QPair<int, int>(np, lp + tmp_bus.pol.size() - 1);
bus->updateGeometry();
tmp_bus.clear();
return true;
}
void BlockView::markPins(int bus_type) {
unhoverPins();
QList<QGraphicsItem * > gi = scene_->items();
foreach (QGraphicsItem * i, gi) {
if (i->data(1004) == "pin") {
BlockItemPin * p = qgraphicsitem_cast<BlockItemPin*>(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);
// else
// p->animAccept();
}
}
}
}
}
void BlockView::unmarkPins(bool to_normal) {
unhoverPins();
QList<QGraphicsItem * > gi = scene_->items();
foreach (QGraphicsItem * i, gi) {
if (i->data(1004) == "pin") {
qgraphicsitem_cast<BlockItemPin*>(i)->restoreState();
if (to_normal)
while (qgraphicsitem_cast<BlockItemPin*>(i)->restoreState());
}
}
}
void BlockView::hoverAcceptedPin(BlockItemPin * pin, bool hover) {
if (!pin) return;
pin->enlargePin(hover);
}
void BlockView::unhoverPins(BlockItemPin* excl_pin) {
QList<QGraphicsItem * > gi = scene_->items();
foreach (QGraphicsItem * i, gi) {
if (excl_pin == ((BlockItemPin*)i)) continue;
if (i->data(1004) == "pin") {
((BlockItemPin*)i)->enlargePin(false);
}
}
}
void BlockView::simplifyBuses() {
QList<BlockBusItem*> bl = buses();
foreach (BlockBusItem * b, bl)
b->simplify();
}
void BlockView::moveBuses(const QList<QGraphicsItem * > & items, QPointF dp) {
if (dp.isNull()) return;
QList<QGraphicsItem * > gi = scene_->items();
QVector<BlockItemPin * > pins;
QList<BlockBusItem * > buses;
//qDebug() << "move" << dp;
foreach (QGraphicsItem * i, items)
if (i->data(1006) == "item" && i->flags().testFlag(QGraphicsItem::ItemIsMovable))
pins << qgraphicsitem_cast<BlockItem*>(i)->pins();
foreach (QGraphicsItem * i, gi)
if (i->data(1005) == "connection")
buses << qgraphicsitem_cast<BlockBusItem*>(i);
foreach (BlockBusItem * b, buses) {
QList<BlockItemPin * > bpins = b->connections_.values();
if (!bpins.isEmpty()) {
foreach (BlockItemPin * p, pins)
bpins.removeAll(p);
if (bpins.isEmpty()) {
b->movePolyline(dp);
continue;
}
}
foreach (BlockItemPin * p, pins) {
QList<int> ends = b->connections_.keys(p);
for (int i = 0; i < ends.size(); ++i) {
/*int isp = b->segmentPointPair(ends[i]);
QPointF sdp;
if (isp >= 0 && b->pol.size() > 2) {
sdp = b->pol[isp] - b->pol[ends[i]];
if (!sdp.isNull()) {
if (sdp.x() == 0. && dp.x() != 0.)
b->movePoint(isp, QPointF(dp.x(), 0.));
if (sdp.y() == 0. && dp.y() != 0.)
b->movePoint(isp, QPointF(0., dp.y()));
} else {
if (p->alignment() == Qt::AlignTop || p->alignment() == Qt::AlignBottom)
b->movePoint(isp, QPointF(dp.x(), 0.));
if (p->alignment() == Qt::AlignLeft || p->alignment() == Qt::AlignRight)
b->movePoint(isp, QPointF(0., dp.y()));
}
}*/
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<int> epl = b->endpointLine(ends[i], ang);
foreach (int e, epl)
b->movePoint(e, pdp);
b->movePoint(ends[i], dp);
}
}
}
}
QList<BlockBusItem * > BlockView::internalBuses(const QList<BlockItem * > & items) {
QList<BlockBusItem * > ret;
if (items.isEmpty()) return ret;
QList<BlockBusItem * > sbl = buses();
QSet<BlockItem * > sis = QSet<BlockItem * >::fromList(items);
foreach (BlockBusItem * bi, sbl) {
if (bi->connectedBlocks().isEmpty()) continue;
QSet<BlockItem * > bis = QSet<BlockItem * >::fromList(bi->connectedBlocks());
if ((bis - sis).isEmpty())
ret << bi;
}
return ret;
}
QList<BlockItemPin * > BlockView::nearPins(BlockItemPin * pin, Qt::KeyboardModifiers km) {
QList<BlockItemPin * > 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<QGraphicsItem * > 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<QGraphicsItem *> & list) const {
foreach (QGraphicsItem * i, list) {
if (i->data(1004) == "pin")
return qgraphicsitem_cast<BlockItemPin*>(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<BlockItemPin * > 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<BlockItem * > BlockView::selectedBlocks() const {
QList<BlockItem * > ret;
QList<QGraphicsItem * > sil = scene()->selectedItems();
foreach (QGraphicsItem * b, sil)
if (b->data(1006) == "item")
ret << qgraphicsitem_cast<BlockItem*>(b);
return ret;
}
QList<QGraphicsItem * > BlockView::selectedDecors() const {
QList<QGraphicsItem * > 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<double>(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;
newBusStarted(item->busType());
markPins(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;
newBusStarted(bus_type);
markPins(bus_type);
}
void BlockView::endBusPointMove() {
move_bus_point = false;
unmarkPins();
reconnectAll();
}
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, "pasteEnabledChanged", Qt::QueuedConnection, Q_ARG(bool, ret));
else emit pasteEnabledChanged(ret);
}
void BlockView::setBusSquareNodes(bool yes) {
square_node = yes;
QList<BlockBusItem * > 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();
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<int, int>(np, lp);
for (int i = 0; i < tmp_bus.pol.size() - 1; ++i)
item->segments << QPair<int, int>(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<int, int>(match_bus->segments[i].first + lp, match_bus->segments[i].second + lp);
if (tmp_bus.pol.isEmpty())
item->segments << QPair<int, int>(lp + np, snp);
else
item->segments << QPair<int, int>(lp + np, lp - 1);
match_bus->setProperty("_nodelete_", true);
match_bus->deleteLater();
match_bus = 0;
}
item->updateGeometry();
emitActionEvent(BlockItemBase::BusAdd, QList<QGraphicsItem*>() << item);
emit connectionsChanged();
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<QGraphicsItem*>() << bus);
emit connectionsChanged();
}
void BlockView::removedBlock(QObject * o) {
if (o == ghost_) return;
emit blockRemoved((BlockItem*)o);
emitActionEvent(BlockItemBase::BlockRemove, QList<QGraphicsItem*>() << qgraphicsitem_cast<QGraphicsItem*>((BlockItem*)o));
}
void BlockView::removeJunk() {
QList<QGraphicsItem * > gi = scene_->items();
foreach (QGraphicsItem * i, gi) {
if (i->data(1005) != "connection") continue;
BlockBusItem * b = qgraphicsitem_cast<BlockBusItem*>(i);
if (b->pol.size() <= 1) {
b->deleteLater();
}
}
}
void BlockView::sceneSelectionChanged() {
bool ie = scene()->selectedItems().isEmpty();
emit copyEnabledChanged(!ie);
}
void BlockView::_setThumb(double v) {
_talpha = v;
QMetaObject::invokeMethod(&widget_thumb, "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<QGraphicsItem * > gi = scene_->items();
QList<BlockItemPin * > pins;
QList<BlockBusItem * > buses;
foreach (QGraphicsItem * i, gi) {
if (i->data(1004) == "pin")
pins << qgraphicsitem_cast<BlockItemPin*>(i);
if (i->data(1005) == "connection")
buses << qgraphicsitem_cast<BlockBusItem*>(i);
}
foreach (BlockItemPin * p, pins) {
p->clearStateStack();
p->setState(BlockItemPin::Disconnected);
p->buses_.clear();
}
foreach (BlockBusItem * b, buses) {
b->connections_.clear();
QVector<int> 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) {
if (!pins[j]->isVisible()) continue;
QPointF pp = pins[j]->scenePos();
if ((cp - pp).manhattanLength() <= (grid_step / 2.)) {
//qDebug() << "found";
if (b->busType() == pins[j]->busType()) {
b->connections_[conns[c]] = pins[j];
if (!pins[j]->buses_.contains(b))
pins[j]->buses_ << b;
pins[j]->setState(BlockItemPin::Connected);
} else
pins[j]->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<BlockItem*> bll = selectedBlocks();
QList<QGraphicsItem*> del = selectedDecors();
//qDebug() << "copy" << bll.size() << del.size();
if (bll.isEmpty() && del.isEmpty()) return;
QList<BlockBusItem*> 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<BlockItem*> bll;
QList<BlockBusItem*> bul;
QList<QGraphicsItem*> 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() {
QList<QGraphicsItem*> gi = scene_->items();
foreach (QGraphicsItem * i, gi)
i->setSelected(false);
}
void BlockView::selectAll() {
QList<QGraphicsItem*> gi = scene_->items();
foreach (QGraphicsItem * i, gi)
if (i->flags().testFlag(QGraphicsItem::ItemIsSelectable))
i->setSelected(true);
}
void BlockView::removeSelected() {
QList<QGraphicsItem*> gi = scene_->selectedItems(), ai;
blockSignals(true);
QList<BlockBusItem * > sbuses = buses(), dbuses, wbuses = wrongConnectedBuses();
//foreach (BlockBusItem * i, wbuses)
// sbuses.removeOne(i);
foreach (BlockBusItem * i, sbuses)
if (i->connectedBlocks().isEmpty())
dbuses << i;
foreach (QGraphicsItem * i, gi) {
if (i->data(1006) == "item")
ai << qgraphicsitem_cast<QGraphicsItem*>(i);
if ((i->data(1006) == "item") || (i->data(1005) == "connection") || (i->data(1009) == "decor")) {
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<QGraphicsItem*> gi = scene_->items(), ai;
blockSignals(true);
foreach (QGraphicsItem * i, gi) {
if (i->data(1006) == "item")
ai << qgraphicsitem_cast<QGraphicsItem*>(i);
if ((i->data(1006) == "item") || (i->data(1005) == "connection") || (i->data(1009) == "decor")) {
if ((i != &sel_rect) && (i != &tmp_bus) && (i->parentItem() == 0) && !(i->data(1008).toBool()) && !(i->data(1007).toBool())) {
//qDebug() << "delete" << i->data(1004);
scene_->sendEvent(i, new QGraphicsSceneEvent(QEvent::Close));
delete i;
}
}
}
blockSignals(false);
foreach (QGraphicsItem * i, ai)
emit blockRemoved((BlockItem*)i);
}