File indexing completed on 2024-05-19 05:42:18
0001 // ct_lvtqtc_graphicsview.cpp -*-C++-*- 0002 0003 /* 0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk> 0005 // SPDX-License-Identifier: Apache-2.0 0006 // 0007 // Licensed under the Apache License, Version 2.0 (the "License"); 0008 // you may not use this file except in compliance with the License. 0009 // You may obtain a copy of the License at 0010 // 0011 // http://www.apache.org/licenses/LICENSE-2.0 0012 // 0013 // Unless required by applicable law or agreed to in writing, software 0014 // distributed under the License is distributed on an "AS IS" BASIS, 0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0016 // See the License for the specific language governing permissions and 0017 // limitations under the License. 0018 */ 0019 0020 #include <ct_lvtqtc_graphicsview.h> 0021 0022 #include <ct_lvtprj_projectfile.h> 0023 #include <ct_lvtqtc_graphicsscene.h> 0024 #include <ct_lvtqtc_lakosentity.h> 0025 #include <ct_lvtqtc_lakosrelation.h> 0026 #include <ct_lvtqtc_minimap.h> 0027 #include <ct_lvtqtc_tooltip.h> 0028 #include <ct_lvtqtc_undo_manager.h> 0029 0030 #include <QBrush> 0031 #include <QCursor> 0032 #include <QDropEvent> 0033 #include <QMenu> 0034 #include <QMimeData> 0035 #include <QPainter> 0036 #include <QPainterPath> 0037 #include <QScrollBar> 0038 #include <QToolTip> 0039 #include <QTransform> 0040 0041 #include <qnamespace.h> 0042 #include <set> 0043 0044 #include <preferences.h> 0045 0046 using namespace Codethink::lvtldr; 0047 0048 namespace Codethink::lvtqtc { 0049 0050 struct GraphicsView::Private { 0051 QString fullyQualifiedName; 0052 GraphicsScene *scene = nullptr; 0053 Minimap *minimap = nullptr; 0054 ToolTipItem *toolTipItem = nullptr; 0055 int zoomFactor = 100; 0056 bool initialized = false; 0057 ITool *currentTool = nullptr; 0058 UndoManager *undoManager = nullptr; 0059 0060 struct { 0061 bool isActive = false; 0062 QPoint start; 0063 QPoint end; 0064 const QPen pen = QPen(QBrush(Qt::black), 1, Qt::PenStyle::DashLine); 0065 const QBrush brush = QBrush(QColor(0, 0, 0, 30)); 0066 } multiSelect; 0067 0068 bool isMultiDragging = false; 0069 0070 struct { 0071 QString text; 0072 lvtshr::SearchMode mode = lvtshr::SearchMode::CaseInsensitive; 0073 int current = 0; 0074 QList<LakosEntity *> searchResult; 0075 } search; 0076 }; 0077 0078 // -------------------------------------------- 0079 // class GraphicsView 0080 // -------------------------------------------- 0081 0082 GraphicsView::GraphicsView(NodeStorage& nodeStorage, lvtprj::ProjectFile const& projectFile, QWidget *parent): 0083 QGraphicsView(parent), d(std::make_unique<GraphicsView::Private>()) 0084 { 0085 d->scene = new GraphicsScene(nodeStorage, projectFile, this); 0086 setScene(d->scene); 0087 setBackgroundBrush(QBrush(Qt::white)); 0088 setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::TextAntialiasing 0089 | QPainter::RenderHint::SmoothPixmapTransform); 0090 0091 connect(d->scene, &GraphicsScene::graphLoadStarted, this, &GraphicsView::graphLoadStarted); 0092 connect(d->scene, &GraphicsScene::graphLoadFinished, this, &GraphicsView::graphLoadFinished); 0093 0094 setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); 0095 setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); 0096 d->minimap = new Minimap(d->scene, this); 0097 d->minimap->setVisible(false); 0098 0099 connect(d->minimap, &Minimap::focusSceneAt, this, [this](const QPointF& p) { 0100 centerOn(p); 0101 }); 0102 0103 d->toolTipItem = new ToolTipItem(this); 0104 d->toolTipItem->addToolTip("Is A"); 0105 d->toolTipItem->addToolTip("Uses in the Implementation"); 0106 d->toolTipItem->addToolTip("Uses in the Interface"); 0107 d->toolTipItem->move(10, 110); 0108 d->toolTipItem->setVisible(false); 0109 0110 connect(verticalScrollBar(), &QScrollBar::valueChanged, this, [this] { 0111 d->minimap->setSceneRect(mapToScene(rect()).boundingRect()); 0112 }); 0113 0114 connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, [this] { 0115 d->minimap->setSceneRect(mapToScene(rect()).boundingRect()); 0116 }); 0117 0118 connect(Preferences::self(), &Preferences::backgroundColorChanged, this, [this] { 0119 setBackgroundBrush(QBrush(Preferences::backgroundColor())); 0120 }); 0121 setBackgroundBrush(QBrush(Preferences::backgroundColor())); 0122 0123 setCacheMode(QGraphicsView::CacheNone); 0124 setViewportUpdateMode(QGraphicsView::FullViewportUpdate); 0125 setAcceptDrops(true); 0126 setTransformationAnchor(ViewportAnchor::AnchorUnderMouse); 0127 } 0128 0129 GraphicsView::~GraphicsView() noexcept = default; 0130 0131 void GraphicsView::undoCommandReceived(QUndoCommand *command) 0132 { 0133 if (!d->undoManager) { 0134 return; 0135 } 0136 d->undoManager->addUndoCommand(command); 0137 Q_EMIT onUndoCommandReceived(this, command); 0138 } 0139 0140 void GraphicsView::toggleMinimap(bool toggle) 0141 { 0142 d->minimap->setVisible(toggle); 0143 Preferences::setShowMinimap(toggle); 0144 } 0145 0146 void GraphicsView::toggleLegend(bool toggle) 0147 { 0148 d->toolTipItem->setVisible(toggle); 0149 Preferences::setShowLegend(toggle); 0150 } 0151 0152 void GraphicsView::setUndoManager(UndoManager *undoManager) 0153 { 0154 d->undoManager = undoManager; 0155 } 0156 0157 void GraphicsView::debugVisibleScreen() 0158 { 0159 qDebug() << d->scene->toJson(); 0160 } 0161 0162 void GraphicsView::setColorManagement(const std::shared_ptr<lvtclr::ColorManagement>& colorManagement) 0163 { 0164 assert(colorManagement); 0165 d->scene->setColorManagement(colorManagement); 0166 } 0167 0168 void GraphicsView::fitAllInView() 0169 { 0170 // Note: Do not use `d->scene->itemsBoundingRect()` to fit all in view, because it takes in consideration hidden 0171 // items, and we do not update the position of hidden items. Thus, using the aforementioned method will compute a 0172 // wrong rect for the view. 0173 auto boundingRect = QRectF{}; 0174 for (auto const& entity : d->scene->allEntities()) { 0175 if (entity->isVisible()) { 0176 boundingRect |= entity->mapRectToScene(entity->boundingRect()); 0177 } 0178 } 0179 fitRectInView(boundingRect); 0180 } 0181 0182 void GraphicsView::fitItemInView(QGraphicsItem *entity) 0183 { 0184 auto w = entity->boundingRect().width(); 0185 auto h = entity->boundingRect().height(); 0186 auto x = entity->scenePos().x() - w / 2.; 0187 auto y = entity->scenePos().y() - h / 2.; 0188 0189 auto r = QRectF{x, y, w, h}; 0190 fitRectInView(r); 0191 } 0192 0193 void GraphicsView::fitRectInView(QRectF const& r) 0194 { 0195 constexpr qreal PAD = 100; 0196 constexpr qreal PREFERRED_ZOOM_FACTOR = 100; // percent 0197 0198 QRectF boundingRect = r.adjusted(-PAD / 2, -PAD / 2, PAD / 2, PAD / 2); 0199 fitInView(boundingRect, Qt::AspectRatioMode::KeepAspectRatio); 0200 calculateCurrentZoomFactor(); 0201 0202 // If the zoom factor is higher than the preferred factor, use the preferred factor to avoid zooming in too much. 0203 if (d->zoomFactor > PREFERRED_ZOOM_FACTOR) { 0204 setZoomFactor(PREFERRED_ZOOM_FACTOR); 0205 } 0206 } 0207 0208 void GraphicsView::wheelEvent(QWheelEvent *event) 0209 { 0210 // Handle Zoom 0211 auto zoomModifier = Preferences::zoomModifier(); 0212 if (event->modifiers() & zoomModifier || zoomModifier == Qt::KeyboardModifier::NoModifier) { 0213 if (event->angleDelta().y() > 0) { 0214 setZoomFactor(d->zoomFactor + 2); 0215 } else { 0216 setZoomFactor(d->zoomFactor - 2); 0217 } 0218 return; 0219 } 0220 0221 QGraphicsView::wheelEvent(event); 0222 } 0223 0224 void GraphicsView::setZoomFactor(int zoomFactorInPercent) 0225 { 0226 if (d->zoomFactor == zoomFactorInPercent) { 0227 return; 0228 } 0229 0230 // don't let the zoom factor go below 1%: 0231 // 0% looks like a divide by zero and displays nothing 0232 // negative zoom flips the diagram 0233 constexpr int zoomLimit = 1; 0234 if (zoomFactorInPercent < zoomLimit) { 0235 zoomFactorInPercent = zoomLimit; 0236 } 0237 0238 resetTransform(); 0239 double scaleFactor = zoomFactorInPercent / 100.0; 0240 scale(scaleFactor, scaleFactor); 0241 d->zoomFactor = zoomFactorInPercent; 0242 Q_EMIT zoomFactorChanged(zoomFactorInPercent); 0243 0244 d->scene->setBlockNodeResizeOnHover(zoomFactorInPercent >= 100); 0245 } 0246 0247 int GraphicsView::zoomFactor() const 0248 { 0249 return d->zoomFactor; 0250 } 0251 0252 void GraphicsView::calculateCurrentZoomFactor() 0253 { 0254 d->zoomFactor = static_cast<int>(transform().m11() * 100); 0255 Q_EMIT zoomFactorChanged(d->zoomFactor); 0256 } 0257 0258 void GraphicsView::mouseMoveEvent(QMouseEvent *event) 0259 { 0260 assert(event); 0261 0262 const bool mouseHasNewPosition = d->multiSelect.end != event->pos(); 0263 0264 if (d->multiSelect.isActive && mouseHasNewPosition) { 0265 d->multiSelect.end = event->pos(); 0266 auto selection = QRect(d->multiSelect.start, d->multiSelect.end).normalized(); 0267 QSet<LakosEntity *> currentSelection; 0268 static QSet<LakosEntity *> oldSelection; 0269 0270 // Fill currentSelection with all LakosEntities in selection 0271 const auto itemsInSelection = items(selection, Qt::IntersectsItemBoundingRect); 0272 for (QGraphicsItem *item : itemsInSelection) { 0273 if (auto *lEntity = qgraphicsitem_cast<LakosEntity *>(item)) { 0274 currentSelection.insert(lEntity); 0275 } 0276 } 0277 0278 // Update selection 0279 for (const auto lEntity : currentSelection) { 0280 if (!lEntity->isSelected()) { 0281 lEntity->setSelected(true); 0282 } 0283 } 0284 0285 auto toUnselect = oldSelection; 0286 toUnselect.subtract(currentSelection); 0287 for (const auto lEntity : toUnselect) { 0288 lEntity->setSelected(false); 0289 } 0290 0291 if (oldSelection != currentSelection) { 0292 std::deque<LakosianNode *> selectedNodes; 0293 for (const auto& lEntity : currentSelection) { 0294 selectedNodes.push_back(lEntity->internalNode()); 0295 } 0296 Q_EMIT newSelectionMade(selectedNodes); 0297 oldSelection = currentSelection; 0298 } 0299 0300 viewport()->update(); 0301 } 0302 0303 if (d->isMultiDragging && mouseHasNewPosition) { 0304 for (const auto& entity : d->scene->selectedEntities()) { 0305 entity->doDrag(mapToScene(event->pos())); 0306 } 0307 } 0308 0309 QList<QGraphicsItem *> underMouse = items(event->pos()); 0310 0311 d->toolTipItem->clear(); 0312 for (QGraphicsItem *item : underMouse) { 0313 if (auto *relation = qgraphicsitem_cast<LakosRelation *>(item)) { 0314 QString label = QString::fromStdString(relation->legendText()); 0315 d->toolTipItem->addToolTip(label); 0316 break; 0317 } 0318 if (auto *entity = qgraphicsitem_cast<LakosEntity *>(item)) { 0319 QString label = QString::fromStdString(entity->legendText()); 0320 d->toolTipItem->addToolTip(label); 0321 break; 0322 } 0323 } 0324 QGraphicsView::mouseMoveEvent(event); 0325 } 0326 0327 void GraphicsView::zoomIntoRect(const QPoint& topLeft, const QPoint& bottomRight) 0328 { 0329 auto mapTopLeft = mapToScene(topLeft); 0330 auto mapBottomRight = mapToScene(bottomRight); 0331 fitInView(QRectF(mapTopLeft, mapBottomRight), Qt::AspectRatioMode::KeepAspectRatio); 0332 0333 d->zoomFactor = static_cast<int>(transform().m11() * 100); 0334 // m11 is the matrix coordinate that takes care of the 0335 // scale factor. 0336 0337 Q_EMIT zoomFactorChanged(d->zoomFactor); 0338 } 0339 0340 void GraphicsView::keyPressEvent(QKeyEvent *event) 0341 { 0342 if (event->modifiers() & Preferences::panModifier()) { 0343 setDragMode(QGraphicsView::DragMode::ScrollHandDrag); 0344 event->accept(); 0345 return; 0346 } 0347 0348 QGraphicsView::keyPressEvent(event); 0349 } 0350 0351 void GraphicsView::keyReleaseEvent(QKeyEvent *event) 0352 { 0353 Qt::Key modifier = Qt::Key_Alt; 0354 switch (Preferences::panModifier()) { 0355 case Qt::AltModifier: 0356 modifier = Qt::Key_Alt; 0357 break; 0358 case Qt::ControlModifier: 0359 modifier = Qt::Key_Control; 0360 break; 0361 case Qt::ShiftModifier: 0362 modifier = Qt::Key_Shift; 0363 break; 0364 default: 0365 modifier = Qt::Key_Alt; 0366 } 0367 0368 if (event->key() == modifier) { 0369 setDragMode(QGraphicsView::DragMode::NoDrag); 0370 event->accept(); 0371 } 0372 0373 QGraphicsView::keyReleaseEvent(event); 0374 } 0375 0376 namespace { 0377 template<typename T> 0378 T castUpToParent(QGraphicsItem *item) 0379 { 0380 if (!item) { 0381 return nullptr; 0382 } 0383 if (auto entity = dynamic_cast<T>(item)) { 0384 return entity; 0385 } 0386 if (item->parentItem() == nullptr) { 0387 return nullptr; 0388 } 0389 return castUpToParent<T>(item->parentItem()); 0390 } 0391 } // namespace 0392 0393 void GraphicsView::mousePressEvent(QMouseEvent *event) 0394 { 0395 if (Preferences::enableDebugOutput()) { 0396 qDebug() << "GraphicsView mousePressEvent"; 0397 } 0398 0399 if (event->button() == Qt::ForwardButton) { 0400 Q_EMIT requestNext(); 0401 return; 0402 } 0403 if (event->button() == Qt::BackButton) { 0404 Q_EMIT requestPrevious(); 0405 return; 0406 } 0407 0408 if (event->modifiers() & Preferences::panModifier() 0409 || Preferences::panModifier() == Qt::KeyboardModifier::NoModifier) { 0410 setDragMode(QGraphicsView::DragMode::ScrollHandDrag); 0411 } else { 0412 if (!itemAt(event->pos())) { 0413 if (event->button() == Qt::LeftButton) { 0414 d->multiSelect.start = event->pos(); 0415 d->multiSelect.end = event->pos(); 0416 d->multiSelect.isActive = true; 0417 } else { 0418 d->multiSelect.isActive = false; 0419 } 0420 } 0421 0422 if (event->button() == Qt::LeftButton) { 0423 if (QGraphicsItem *item = itemAt(event->pos())) { 0424 if (d->scene->selectedEntities().size() > 1) { 0425 if (const auto *entity = castUpToParent<LakosEntity *>(item)) { 0426 if (entity->isSelected()) { 0427 d->isMultiDragging = true; 0428 } 0429 } 0430 } 0431 } 0432 } 0433 0434 if (d->isMultiDragging) { 0435 for (auto& entity : d->scene->selectedEntities()) { 0436 entity->startDrag(mapToScene(event->pos())); 0437 } 0438 } 0439 } 0440 0441 // Qt loses the selection if we right click. So we need to 0442 // store the selection, run the virtual call, then restore 0443 // the selection. 0444 if (event->button() == Qt::RightButton) { 0445 const auto selectedItems = scene()->selectedItems(); 0446 QGraphicsView::mousePressEvent(event); 0447 for (auto *item : selectedItems) { 0448 item->setSelected(true); 0449 } 0450 } else { 0451 QGraphicsView::mousePressEvent(event); 0452 } 0453 } 0454 0455 void GraphicsView::mouseReleaseEvent(QMouseEvent *event) 0456 { 0457 if (Preferences::enableDebugOutput()) { 0458 qDebug() << "GraphicsView mouseReleaseEvent"; 0459 } 0460 0461 // update selectedEntities using entity->toggleSelection() 0462 for (const auto& entity : d->scene->allEntities()) { 0463 const bool isSelected = entity->isSelected(); 0464 const auto selectedEntities = d->scene->selectedEntities(); 0465 const bool isContainedInSelectedEntities = 0466 std::find(selectedEntities.begin(), selectedEntities.end(), entity) != selectedEntities.end(); 0467 0468 if ((isSelected && !isContainedInSelectedEntities) || (!isSelected && isContainedInSelectedEntities)) { 0469 entity->toggleSelection(); 0470 } 0471 } 0472 0473 d->multiSelect.isActive = false; 0474 0475 if (d->isMultiDragging) { 0476 for (const auto& entity : d->scene->selectedEntities()) { 0477 entity->endDrag(mapToScene(event->pos())); 0478 } 0479 d->isMultiDragging = false; 0480 } 0481 0482 viewport()->update(); 0483 0484 setDragMode(QGraphicsView::DragMode::NoDrag); 0485 0486 QGraphicsView::mouseReleaseEvent(event); 0487 } 0488 0489 void GraphicsView::showEvent(QShowEvent *event) 0490 { 0491 if (!d->initialized) { 0492 d->initialized = true; 0493 fitAllInView(); 0494 } 0495 QGraphicsView::showEvent(event); 0496 } 0497 0498 void GraphicsView::setCurrentTool(ITool *tool) 0499 { 0500 d->currentTool = tool; 0501 } 0502 0503 ITool *GraphicsView::currentTool() const 0504 { 0505 return d->currentTool; 0506 } 0507 0508 void GraphicsView::drawForeground(QPainter *painter, const QRectF& rect) 0509 { 0510 if (d->currentTool) { 0511 d->currentTool->drawForeground(painter, rect); 0512 } 0513 0514 if (d->multiSelect.isActive) { 0515 painter->save(); 0516 painter->setWorldMatrixEnabled(false); 0517 painter->setPen(d->multiSelect.pen); 0518 painter->setBrush(d->multiSelect.brush); 0519 painter->drawRect(QRect(d->multiSelect.start, d->multiSelect.end)); 0520 painter->setWorldMatrixEnabled(true); 0521 painter->restore(); 0522 } 0523 0524 if (d->search.text.length() == 0) { 0525 return; 0526 } 0527 0528 // Semi translucent yellow 0529 const QColor foundColor(0x55, 0xff, 0xff, 0x99); 0530 0531 // semi translucent blue 0532 const QColor currentColor(0x55, 0x38, 0xB0, 0xDE); 0533 0534 // semi translucent black 0535 const QColor foreground(155, 155, 155, 220); 0536 0537 QPainterPath path; 0538 path.addRect(rect); 0539 0540 painter->save(); 0541 // draw the whole area with a blackish translucent brush. 0542 0543 int idx = 1; 0544 std::set<LakosEntity *> parentsNotInSearch; 0545 0546 for (LakosEntity *entity : qAsConst(d->search.searchResult)) { 0547 auto scenePos = entity->sceneBoundingRect().center(); 0548 if (rect.contains(scenePos)) { 0549 QRectF rectHole = entity->boundingRect(); 0550 0551 // The LakosEntities have the center on the middle, we need to fix 0552 // the rect. 0553 rectHole.moveTo(QPointF(scenePos.x() - rectHole.width() / 2, scenePos.y() - rectHole.height() / 2)); 0554 painter->setBrush(QBrush(idx == d->search.current ? currentColor : foundColor)); 0555 0556 // We can't paint the parent item unless it's the currently selected 0557 // one, because it covers all the child items that could have been 0558 // selected. 0559 if (!entity->parentItem() || idx == d->search.current) { 0560 painter->drawRect(rectHole); 0561 } 0562 0563 // Add the parents that are not found on a set so it's faster to 0564 // look them up for the subsequent children. We assume that there 0565 // are way more children than parents on the searches, since `bal` 0566 // will match the package, and all the children. looking the parent 0567 // for every child on a vector can be slow. 0568 auto *parentEntity = dynamic_cast<LakosEntity *>(entity->parentItem()); 0569 auto it = parentsNotInSearch.find(parentEntity); 0570 if (it == std::end(parentsNotInSearch)) { 0571 if (!d->search.searchResult.contains(parentEntity)) { 0572 it = parentsNotInSearch.insert(parentEntity).first; 0573 } 0574 } 0575 0576 if (!entity->parentItem() || it != std::end(parentsNotInSearch)) { 0577 path.addRect(rectHole); 0578 } 0579 } 0580 0581 idx += 1; 0582 } 0583 painter->restore(); 0584 0585 painter->setBrush(QBrush(foreground)); 0586 painter->drawPath(path); 0587 } 0588 0589 void GraphicsView::setSearchMode(lvtshr::SearchMode mode) 0590 { 0591 d->search.mode = mode; 0592 doSearch(); 0593 } 0594 0595 void GraphicsView::setSearchString(const QString& search) 0596 { 0597 d->search.text = search; 0598 invalidateScene(); 0599 doSearch(); 0600 } 0601 0602 void GraphicsView::highlightedNextSearchElement() 0603 { 0604 d->search.current = d->search.current % d->search.searchResult.size() + 1; 0605 0606 Q_EMIT currentSearchItemHighlighted(d->search.current); 0607 0608 // We use 1 based index because this is for user facing entries, we need 0609 // to subtract -1 here. 0610 0611 LakosEntity *entity = d->search.searchResult[d->search.current - 1]; 0612 ensureVisible(entity); 0613 0614 viewport()->update(); 0615 } 0616 0617 void GraphicsView::highlightedPreviousSearchElement() 0618 { 0619 d->search.current = d->search.current == 1 ? d->search.searchResult.size() : d->search.current - 1; 0620 0621 Q_EMIT currentSearchItemHighlighted(d->search.current); 0622 0623 // We use 1 based index because this is for user facing entries, we need 0624 // to subtract -1 here. 0625 LakosEntity *entity = d->search.searchResult[d->search.current - 1]; 0626 ensureVisible(entity); 0627 0628 viewport()->update(); 0629 } 0630 0631 void GraphicsView::doSearch() 0632 { 0633 d->search.searchResult.clear(); 0634 std::vector<LakosEntity *> entities = d->scene->allEntities(); 0635 const QString searchText = 0636 d->search.mode == lvtshr::SearchMode::CaseInsensitive ? d->search.text.toLower() : d->search.text; 0637 0638 if (searchText.size() == 0) { 0639 Q_EMIT searchTotal(0); 0640 Q_EMIT currentSearchItemHighlighted(0); 0641 viewport()->update(); 0642 return; 0643 } 0644 0645 for (LakosEntity *entity : entities) { 0646 if (!entity->isVisible()) { 0647 continue; 0648 } 0649 0650 // QString allow us to use some nice things that std::string lacks. 0651 const QString nameEntity = d->search.mode == lvtshr::SearchMode::CaseInsensitive 0652 ? QString::fromStdString(entity->name()).toLower() 0653 : QString::fromStdString(entity->name()); 0654 0655 if (nameEntity.contains(searchText)) { 0656 d->search.searchResult.append(entity); 0657 }; 0658 } 0659 0660 if (d->search.searchResult.empty()) { 0661 Q_EMIT searchTotal(0); 0662 Q_EMIT currentSearchItemHighlighted(0); 0663 viewport()->update(); 0664 return; 0665 } 0666 0667 d->search.current = 1; 0668 Q_EMIT searchTotal(d->search.searchResult.size()); 0669 Q_EMIT currentSearchItemHighlighted(d->search.current); 0670 viewport()->update(); 0671 } 0672 0673 void GraphicsView::contextMenuEvent(QContextMenuEvent *event) 0674 { 0675 QMenu menu; 0676 menu.setToolTipsVisible(true); 0677 0678 QMenu *debugMenu = nullptr; 0679 0680 if (Preferences::enableSceneContextMenu()) { 0681 debugMenu = new QMenu(tr("Debug tools")); 0682 debugMenu->setToolTipsVisible(true); 0683 } 0684 0685 QGraphicsItem *item = itemAt(event->pos()); 0686 if (!item) { 0687 d->scene->populateMenu(menu, debugMenu); 0688 } else { 0689 if (auto *relation = dynamic_cast<LakosRelation *>(item)) { 0690 relation->populateMenu(menu, debugMenu); 0691 } else if (auto *entity = castUpToParent<LakosEntity *>(item)) { 0692 entity->populateMenu(menu, debugMenu, mapToScene(event->pos())); 0693 } 0694 } 0695 0696 if (debugMenu && !debugMenu->isEmpty()) { 0697 menu.addMenu(debugMenu); 0698 } 0699 0700 if (menu.isEmpty()) { 0701 return; 0702 } 0703 menu.exec(event->globalPos()); 0704 } 0705 0706 void GraphicsView::dragEnterEvent(QDragEnterEvent *event) 0707 { 0708 if (event->mimeData()->hasFormat("codevis/qualifiednames")) { 0709 event->acceptProposedAction(); 0710 } 0711 } 0712 0713 void GraphicsView::dragMoveEvent(QDragMoveEvent *event) 0714 { 0715 event->acceptProposedAction(); 0716 } 0717 0718 void GraphicsView::dropEvent(QDropEvent *event) 0719 { 0720 const QString qualNames = event->mimeData()->data("codevis/qualifiednames"); 0721 #ifdef KDE_FRAMEWORKS_IS_OLD 0722 QStringList qualNameList = qualNames.split(";"); 0723 qualNameList.removeAll(QString(";")); 0724 #else 0725 const QStringList qualNameList = qualNames.split(";", Qt::SplitBehaviorFlags::SkipEmptyParts); 0726 #endif 0727 0728 for (const auto& qualName : qualNameList) { 0729 d->scene->loadEntityByQualifiedName(qualName, mapToScene(event->pos())); 0730 } 0731 0732 if (qualNameList.size() > 1) { 0733 d->scene->reLayout(); 0734 } 0735 } 0736 0737 GraphicsScene *GraphicsView::graphicsScene() const 0738 { 0739 return d->scene; 0740 } 0741 0742 void GraphicsView::setPluginManager(Codethink::lvtplg::PluginManager& pm) 0743 { 0744 d->scene->setPluginManager(pm); 0745 } 0746 0747 } // namespace Codethink::lvtqtc