709 lines
13 KiB
C++
709 lines
13 KiB
C++
#include "QtGraphNode.h"
|
|
|
|
#include <QBrush>
|
|
#include <QCursor>
|
|
#include <QFont>
|
|
#include <QFontMetrics>
|
|
#include <QGraphicsSceneEvent>
|
|
#include <QPen>
|
|
|
|
#include "GraphFocusHandler.h"
|
|
#include "MessageCodeShowDefinition.h"
|
|
#include "MessageGraphNodeHide.h"
|
|
#include "MessageGraphNodeMove.h"
|
|
#include "QtDeviceScaledPixmap.h"
|
|
#include "QtGraphEdge.h"
|
|
#include "QtGraphNodeComponent.h"
|
|
#include "QtGraphNodeExpandToggle.h"
|
|
#include "QtRoundedRectItem.h"
|
|
#include "ResourcePaths.h"
|
|
#include "utilityQt.h"
|
|
#include "utilityString.h"
|
|
|
|
void QtGraphNode::blendIn()
|
|
{
|
|
setOpacity(1.0f);
|
|
}
|
|
|
|
void QtGraphNode::blendOut()
|
|
{
|
|
setOpacity(0.0f);
|
|
}
|
|
|
|
void QtGraphNode::showNode()
|
|
{
|
|
this->show();
|
|
}
|
|
|
|
void QtGraphNode::hideNode()
|
|
{
|
|
this->hide();
|
|
}
|
|
|
|
QtGraphNode* QtGraphNode::findNodeRecursive(const std::list<QtGraphNode*>& nodes, Id tokenId)
|
|
{
|
|
for (QtGraphNode* node: nodes)
|
|
{
|
|
if (node->getTokenId() == tokenId)
|
|
{
|
|
return node;
|
|
}
|
|
|
|
QtGraphNode* result = findNodeRecursive(node->getSubNodes(), tokenId);
|
|
if (result != nullptr)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
QtGraphNode::QtGraphNode(GraphFocusHandler* focusHandler): m_focusHandler(focusHandler)
|
|
{
|
|
this->setPen(QPen(Qt::transparent));
|
|
this->setCursor(Qt::PointingHandCursor);
|
|
|
|
m_text = new QGraphicsSimpleTextItem(this);
|
|
m_rect = new QtRoundedRectItem(this);
|
|
m_undefinedRect = new QtRoundedRectItem(this);
|
|
m_undefinedRect->hide();
|
|
}
|
|
|
|
QtGraphNode::~QtGraphNode() {}
|
|
|
|
QtGraphNode* QtGraphNode::getParent() const
|
|
{
|
|
return m_parentNode;
|
|
}
|
|
|
|
QtGraphNode* QtGraphNode::getLastParent(bool noGroups) const
|
|
{
|
|
QtGraphNode* node = const_cast<QtGraphNode*>(this);
|
|
while (true)
|
|
{
|
|
QtGraphNode* parent = dynamic_cast<QtGraphNode*>(node->parentItem());
|
|
if (!parent || (noGroups && parent->isGroupNode()))
|
|
{
|
|
break;
|
|
}
|
|
node = parent;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
QtGraphNode* QtGraphNode::getLastNonGroupParent() const
|
|
{
|
|
return getLastParent(true);
|
|
}
|
|
|
|
void QtGraphNode::setParent(QtGraphNode* parentNode)
|
|
{
|
|
m_parentNode = parentNode;
|
|
|
|
if (m_parentNode != nullptr)
|
|
{
|
|
QGraphicsRectItem::setParentItem(m_parentNode);
|
|
}
|
|
}
|
|
|
|
const std::list<QtGraphNode*>& QtGraphNode::getSubNodes() const
|
|
{
|
|
return m_subNodes;
|
|
}
|
|
|
|
Vec2i QtGraphNode::getPosition() const
|
|
{
|
|
return Vec2i(static_cast<int>(this->scenePos().x()), static_cast<int>(this->scenePos().y()));
|
|
}
|
|
|
|
bool QtGraphNode::setPosition(const Vec2i& position)
|
|
{
|
|
Vec2i currentPosition = getPosition();
|
|
Vec2i offset = position - currentPosition;
|
|
|
|
if (offset.x != 0 || offset.y != 0)
|
|
{
|
|
this->moveBy(offset.x, offset.y);
|
|
setColumnSize(Vec2i());
|
|
notifyEdgesAfterMove();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const Vec2i& QtGraphNode::getSize() const
|
|
{
|
|
return m_size;
|
|
}
|
|
|
|
void QtGraphNode::setSize(const Vec2i& size)
|
|
{
|
|
m_size = size;
|
|
|
|
this->setRect(0, 0, size.x, size.y);
|
|
m_rect->setRect(0, 0, size.x, size.y);
|
|
m_undefinedRect->setRect(1, 1, size.x - 2, size.y - 2);
|
|
}
|
|
|
|
const Vec2i& QtGraphNode::getColumnSize() const
|
|
{
|
|
return m_columnSize;
|
|
}
|
|
|
|
void QtGraphNode::setColumnSize(const Vec2i& size)
|
|
{
|
|
m_columnSize = size;
|
|
}
|
|
|
|
QSize QtGraphNode::size() const
|
|
{
|
|
return QSize(m_size.x, m_size.y);
|
|
}
|
|
|
|
void QtGraphNode::setSize(QSize size)
|
|
{
|
|
setSize(Vec2i(size.width(), size.height()));
|
|
}
|
|
|
|
Vec4i QtGraphNode::getBoundingRect() const
|
|
{
|
|
Vec2i pos = getPosition();
|
|
Vec2i size = getSize();
|
|
return Vec4i(pos.x, pos.y, pos.x + size.x, pos.y + size.y);
|
|
}
|
|
|
|
void QtGraphNode::addOutEdge(QtGraphEdge* edge)
|
|
{
|
|
m_outEdges.push_back(edge);
|
|
}
|
|
|
|
void QtGraphNode::addInEdge(QtGraphEdge* edge)
|
|
{
|
|
m_inEdges.push_back(edge);
|
|
}
|
|
|
|
size_t QtGraphNode::getOutEdgeCount() const
|
|
{
|
|
return m_outEdges.size();
|
|
}
|
|
|
|
size_t QtGraphNode::getInEdgeCount() const
|
|
{
|
|
return m_inEdges.size();
|
|
}
|
|
|
|
bool QtGraphNode::getIsActive() const
|
|
{
|
|
return m_isActive;
|
|
}
|
|
|
|
void QtGraphNode::setIsActive(bool isActive)
|
|
{
|
|
m_isActive = isActive;
|
|
}
|
|
|
|
void QtGraphNode::setMultipleActive(bool multipleActive)
|
|
{
|
|
m_multipleActive = multipleActive;
|
|
}
|
|
|
|
bool QtGraphNode::hasActiveChild() const
|
|
{
|
|
if (m_isActive)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (auto subNode: m_subNodes)
|
|
{
|
|
if (subNode->hasActiveChild())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool QtGraphNode::isFocusable() const
|
|
{
|
|
return m_isInteractive && (isDataNode() || isGroupNode() || isBundleNode());
|
|
}
|
|
|
|
std::wstring QtGraphNode::getName() const
|
|
{
|
|
return m_text->text().toStdWString();
|
|
}
|
|
|
|
void QtGraphNode::setName(const std::wstring& name)
|
|
{
|
|
m_text->setText(QString::fromStdWString(name));
|
|
}
|
|
|
|
void QtGraphNode::addComponent(const std::shared_ptr<QtGraphNodeComponent>& component)
|
|
{
|
|
m_components.push_back(component);
|
|
}
|
|
|
|
void QtGraphNode::hoverEnter()
|
|
{
|
|
hoverEnterEvent(nullptr);
|
|
|
|
QtGraphNode* parent = getParent();
|
|
if (parent)
|
|
{
|
|
parent->hoverEnter();
|
|
}
|
|
}
|
|
|
|
bool QtGraphNode::getIsFocused() const
|
|
{
|
|
return m_isFocused;
|
|
}
|
|
|
|
void QtGraphNode::setIsFocused(bool focused)
|
|
{
|
|
if (m_isFocused != focused)
|
|
{
|
|
m_isFocused = focused;
|
|
|
|
if (focused)
|
|
{
|
|
coFocusIn();
|
|
}
|
|
else
|
|
{
|
|
coFocusOut();
|
|
}
|
|
|
|
updateStyle();
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::focusIn()
|
|
{
|
|
if (m_isInteractive && m_focusHandler)
|
|
{
|
|
m_focusHandler->focusNode(this);
|
|
}
|
|
else
|
|
{
|
|
coFocusIn();
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::focusOut()
|
|
{
|
|
if (m_isInteractive && m_focusHandler)
|
|
{
|
|
m_focusHandler->defocusNode(this);
|
|
}
|
|
else
|
|
{
|
|
coFocusOut();
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::coFocusIn()
|
|
{
|
|
if (m_isCoFocused)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_isCoFocused = true;
|
|
|
|
forEachEdge([](QtGraphEdge* edge) {
|
|
if (edge->isTrailEdge())
|
|
{
|
|
edge->coFocusIn();
|
|
}
|
|
});
|
|
|
|
updateStyle();
|
|
}
|
|
|
|
void QtGraphNode::coFocusOut()
|
|
{
|
|
if (!m_isCoFocused)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_isCoFocused = false;
|
|
|
|
forEachEdge([](QtGraphEdge* edge) {
|
|
if (edge->isTrailEdge())
|
|
{
|
|
edge->coFocusOut();
|
|
}
|
|
});
|
|
|
|
updateStyle();
|
|
}
|
|
|
|
void QtGraphNode::showNodeRecursive()
|
|
{
|
|
blendIn();
|
|
showNode();
|
|
|
|
for (auto subNode: m_subNodes)
|
|
{
|
|
subNode->showNodeRecursive();
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::matchNameRecursive(const std::wstring& query, std::vector<QtGraphNode*>* matchedNodes)
|
|
{
|
|
matchName(query, matchedNodes);
|
|
|
|
for (auto subNode: m_subNodes)
|
|
{
|
|
subNode->matchNameRecursive(query, matchedNodes);
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::removeNameMatch()
|
|
{
|
|
if (m_matchLength)
|
|
{
|
|
m_matchRect->hide();
|
|
m_matchText->hide();
|
|
|
|
m_matchPos = 0;
|
|
m_matchLength = 0;
|
|
|
|
updateStyle();
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::setActiveMatch(bool active)
|
|
{
|
|
m_isActiveMatch = active;
|
|
}
|
|
|
|
bool QtGraphNode::isDataNode() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool QtGraphNode::isAccessNode() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool QtGraphNode::isExpandToggleNode() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool QtGraphNode::isBundleNode() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool QtGraphNode::isQualifierNode() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool QtGraphNode::isTextNode() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool QtGraphNode::isGroupNode() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Id QtGraphNode::getTokenId() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void QtGraphNode::addSubNode(QtGraphNode* node)
|
|
{
|
|
m_subNodes.push_back(node);
|
|
|
|
// push parent nodes to back so all edges going to the active subnode are visible
|
|
if (node->getIsActive())
|
|
{
|
|
QtGraphNode* parent = this;
|
|
while (parent && !parent->isGroupNode())
|
|
{
|
|
parent->setZValue(-10.0f);
|
|
parent->m_text->setZValue(-9.0f);
|
|
parent->m_rect->setZValue(-10.0f);
|
|
parent->m_undefinedRect->setZValue(-10.0f);
|
|
|
|
parent = parent->getParent();
|
|
}
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::moved(const Vec2i& oldPosition)
|
|
{
|
|
setPosition(GraphViewStyle::alignOnRaster(getPosition()));
|
|
|
|
if (isDataNode() || isGroupNode() || isBundleNode())
|
|
{
|
|
MessageGraphNodeMove(getTokenId(), getPosition() - oldPosition).dispatch();
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::onClick() {}
|
|
|
|
void QtGraphNode::onMiddleClick() {}
|
|
|
|
void QtGraphNode::onHide()
|
|
{
|
|
Id tokenId = getTokenId();
|
|
|
|
if (tokenId)
|
|
{
|
|
MessageGraphNodeHide(tokenId).dispatch();
|
|
}
|
|
else if (getParent())
|
|
{
|
|
getParent()->onHide();
|
|
}
|
|
}
|
|
|
|
Id QtGraphNode::onCollapseExpand()
|
|
{
|
|
for (auto subNode: getSubNodes())
|
|
{
|
|
if (subNode->isExpandToggleNode())
|
|
{
|
|
subNode->onClick();
|
|
return getTokenId();
|
|
}
|
|
}
|
|
|
|
if (getParent())
|
|
{
|
|
return getParent()->onCollapseExpand();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void QtGraphNode::onShowDefinition(bool inIDE)
|
|
{
|
|
Id tokenId = getTokenId();
|
|
|
|
if (tokenId)
|
|
{
|
|
MessageCodeShowDefinition(tokenId, inIDE).dispatch();
|
|
}
|
|
else if (getParent())
|
|
{
|
|
getParent()->onShowDefinition(inIDE);
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::mousePressEvent(QGraphicsSceneMouseEvent* event)
|
|
{
|
|
event->ignore();
|
|
|
|
for (std::shared_ptr<QtGraphNodeComponent> component: m_components)
|
|
{
|
|
component->nodeMousePressEvent(event);
|
|
}
|
|
|
|
if (!event->isAccepted())
|
|
{
|
|
QtGraphNode* parent = getParent();
|
|
if (parent)
|
|
{
|
|
parent->mousePressEvent(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
|
|
{
|
|
event->ignore();
|
|
|
|
for (std::shared_ptr<QtGraphNodeComponent> component: m_components)
|
|
{
|
|
component->nodeMouseMoveEvent(event);
|
|
}
|
|
|
|
if (!event->isAccepted())
|
|
{
|
|
QtGraphNode* parent = getParent();
|
|
if (parent)
|
|
{
|
|
parent->mouseMoveEvent(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
|
|
{
|
|
event->ignore();
|
|
|
|
for (std::shared_ptr<QtGraphNodeComponent> component: m_components)
|
|
{
|
|
component->nodeMouseReleaseEvent(event);
|
|
}
|
|
|
|
if (!event->isAccepted())
|
|
{
|
|
QtGraphNode* parent = getParent();
|
|
if (parent)
|
|
{
|
|
parent->mouseReleaseEvent(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::forEachEdge(std::function<void(QtGraphEdge*)> func)
|
|
{
|
|
for (QtGraphEdge* edge: m_outEdges)
|
|
{
|
|
func(edge);
|
|
}
|
|
|
|
for (QtGraphEdge* edge: m_inEdges)
|
|
{
|
|
func(edge);
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::notifyEdgesAfterMove()
|
|
{
|
|
forEachEdge([](QtGraphEdge* edge) {
|
|
edge->clearPath();
|
|
edge->updateLine();
|
|
});
|
|
|
|
for (QtGraphNode* node: m_subNodes)
|
|
{
|
|
node->notifyEdgesAfterMove();
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::matchName(const std::wstring& query, std::vector<QtGraphNode*>* matchedNodes)
|
|
{
|
|
m_isActiveMatch = false;
|
|
const std::wstring name = getName();
|
|
size_t pos = utility::toLowerCase(name).find(query);
|
|
|
|
if (pos != std::string::npos)
|
|
{
|
|
if (!m_matchText)
|
|
{
|
|
m_matchRect = new QtRoundedRectItem(this);
|
|
m_matchText = new QGraphicsSimpleTextItem(this);
|
|
}
|
|
|
|
m_matchRect->show();
|
|
m_matchText->show();
|
|
|
|
std::wstring matchName(name.length(), L' ');
|
|
matchName.replace(pos, query.length(), name.substr(pos, query.length()));
|
|
m_matchText->setText(QString::fromStdWString(matchName));
|
|
|
|
matchedNodes->push_back(this);
|
|
|
|
m_matchPos = pos;
|
|
m_matchLength = query.length();
|
|
|
|
updateStyle();
|
|
}
|
|
else if (m_matchLength)
|
|
{
|
|
removeNameMatch();
|
|
}
|
|
}
|
|
|
|
void QtGraphNode::setStyle(const GraphViewStyle::NodeStyle& style)
|
|
{
|
|
QPen pen(Qt::transparent);
|
|
if (style.borderWidth > 0)
|
|
{
|
|
pen.setColor(style.color.border.c_str());
|
|
pen.setWidthF(style.borderWidth);
|
|
if (style.borderDashed)
|
|
{
|
|
pen.setStyle(Qt::DashLine);
|
|
}
|
|
}
|
|
|
|
m_rect->setPen(pen);
|
|
m_rect->setBrush(QBrush(style.color.fill.c_str()));
|
|
|
|
qreal radius = style.cornerRadius;
|
|
m_rect->setRadius(radius);
|
|
|
|
if (style.hasHatching)
|
|
{
|
|
QtDeviceScaledPixmap pattern(QString::fromStdWString(
|
|
ResourcePaths::getGuiPath().concatenate(L"graph_view/images/pattern.png").wstr()));
|
|
pattern.scaleToHeight(12);
|
|
QPixmap pixmap = utility::colorizePixmap(pattern.pixmap(), style.color.hatching.c_str());
|
|
|
|
pen.setWidth(0);
|
|
pen.setColor(Qt::transparent);
|
|
|
|
m_undefinedRect->setPen(pen);
|
|
m_undefinedRect->setBrush(pixmap);
|
|
m_undefinedRect->setRadius(radius);
|
|
m_undefinedRect->show();
|
|
}
|
|
|
|
if (!m_icon && !style.iconPath.empty())
|
|
{
|
|
QtDeviceScaledPixmap pixmap(QString::fromStdWString(style.iconPath.wstr()));
|
|
pixmap.scaleToHeight(static_cast<int>(style.iconSize));
|
|
|
|
m_icon = new QGraphicsPixmapItem(
|
|
utility::colorizePixmap(pixmap.pixmap(), style.color.icon.c_str()), this);
|
|
m_icon->setTransformationMode(Qt::SmoothTransformation);
|
|
m_icon->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
|
|
m_icon->setPos(style.iconOffset.x, style.iconOffset.y);
|
|
}
|
|
|
|
QFont font(style.fontName.c_str());
|
|
font.setPixelSize(static_cast<int>(style.fontSize));
|
|
if (style.fontBold)
|
|
{
|
|
font.setWeight(QFont::Bold);
|
|
}
|
|
|
|
m_text->setFont(font);
|
|
m_text->setBrush(QBrush(style.color.text.c_str()));
|
|
m_text->setPos(
|
|
static_cast<qreal>(style.iconOffset.x + style.iconSize + style.textOffset.x),
|
|
static_cast<qreal>(style.textOffset.y));
|
|
|
|
if (m_matchLength)
|
|
{
|
|
GraphViewStyle::NodeColor color = GraphViewStyle::getScreenMatchColor(m_isActiveMatch);
|
|
|
|
m_matchText->setFont(font);
|
|
m_matchText->setBrush(QBrush(color.text.c_str()));
|
|
m_matchText->setPos(
|
|
static_cast<qreal>(style.iconOffset.x + style.iconSize + style.textOffset.x),
|
|
static_cast<qreal>(style.textOffset.y));
|
|
|
|
const float charWidth =
|
|
QFontMetrics(font).boundingRect(QStringLiteral("QtGraphNode::QtGraphNode::QtGraphNode")).width() / 37.0f;
|
|
const float charHeight = static_cast<float>(QFontMetrics(font).height());
|
|
m_matchRect->setRect(
|
|
static_cast<qreal>(
|
|
style.iconOffset.x + style.iconSize + style.textOffset.x + m_matchPos * charWidth),
|
|
static_cast<qreal>(style.textOffset.y),
|
|
static_cast<qreal>(m_matchLength * charWidth),
|
|
static_cast<qreal>(charHeight));
|
|
m_matchRect->setPen(QPen(color.border.c_str()));
|
|
m_matchRect->setBrush(QBrush(color.fill.c_str()));
|
|
m_matchRect->setRadius(3);
|
|
}
|
|
}
|