File indexing completed on 2024-05-19 15:27:45
0001 /* This file is part of KGraphViewer. 0002 Copyright (C) 2005-2007 Gael de Chalendar <kleag@free.fr> 0003 0004 KGraphViewer is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU General Public 0006 License as published by the Free Software Foundation, version 2. 0007 0008 This program is distributed in the hope that it will be useful, 0009 but WITHOUT ANY WARRANTY; without even the implied warranty of 0010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0011 General Public License for more details. 0012 0013 You should have received a copy of the GNU General Public License 0014 along with this program; if not, write to the Free Software 0015 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0016 02110-1301, USA 0017 */ 0018 0019 /* This file was callgraphview.cpp, part of KCachegrind. 0020 Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 0021 0022 KCachegrind is free software; you can redistribute it and/or 0023 modify it under the terms of the GNU General Public 0024 License as published by the Free Software Foundation, version 2. 0025 */ 0026 0027 #include "canvasedge.h" 0028 #include "FontsCache.h" 0029 #include "dot2qtconsts.h" 0030 #include "dotdefaults.h" 0031 #include "dotgraphview.h" 0032 #include "graphedge.h" 0033 #include "graphnode.h" 0034 #include "kgraphviewerlib_debug.h" 0035 0036 #include <QAction> 0037 0038 #include <QGraphicsSceneMouseEvent> 0039 #include <QMenu> 0040 #include <QPainter> 0041 0042 #include <klocalizedstring.h> 0043 0044 #include <iostream> 0045 0046 // 0047 // CanvasEdge 0048 // 0049 0050 namespace KGraphViewer 0051 { 0052 CanvasEdge::CanvasEdge(DotGraphView *view, GraphEdge *e, qreal scaleX, qreal scaleY, qreal xMargin, qreal yMargin, qreal gh, qreal wdhcf, qreal hdvcf, QGraphicsItem *parent) 0053 : QAbstractGraphicsShapeItem(parent) 0054 , m_scaleX(scaleX) 0055 , m_scaleY(scaleY) 0056 , m_xMargin(xMargin) 0057 , m_yMargin(yMargin) 0058 , m_gh(/*gh*/ 0) 0059 , m_wdhcf(wdhcf) 0060 , m_hdvcf(hdvcf) 0061 , m_edge(e) 0062 , m_font(nullptr) 0063 , m_view(view) 0064 , m_popup(new QMenu()) 0065 { 0066 Q_UNUSED(gh); 0067 qCDebug(KGRAPHVIEWERLIB_LOG) << "edge " << edge()->fromNode()->id() << "->" << edge()->toNode()->id() << m_gh; 0068 setBoundingRegionGranularity(0.9); 0069 m_font = FontsCache::changeable().fromName(e->fontName()); 0070 0071 computeBoundingRect(); 0072 // qCDebug(KGRAPHVIEWERLIB_LOG) << "boundingRect computed: " << m_boundingRect; 0073 0074 QString tipStr = i18n("%1 -> %2\nlabel='%3'", edge()->fromNode()->id(), edge()->toNode()->id(), e->label()); 0075 setToolTip(tipStr); 0076 0077 // the message should be given (or possible to be given) by the part user 0078 QAction *removeEdgeAction = new QAction(i18n("Remove selected edge(s)"), this); 0079 m_popup->addAction(removeEdgeAction); 0080 connect(removeEdgeAction, &QAction::triggered, this, &CanvasEdge::slotRemoveEdge); 0081 0082 connect(e, &GraphEdge::changed, this, &CanvasEdge::modelChanged); 0083 connect(this, &CanvasEdge::selected, view, &DotGraphView::slotEdgeSelected); 0084 0085 connect(this, &CanvasEdge::edgeContextMenuEvent, view, &DotGraphView::slotContextMenuEvent); 0086 0087 setAcceptHoverEvents(true); 0088 0089 qCDebug(KGRAPHVIEWERLIB_LOG) << "connect slotElementHoverEnter"; 0090 connect(this, &CanvasEdge::hoverEnter, view, static_cast<void (DotGraphView::*)(CanvasEdge *)>(&DotGraphView::slotElementHoverEnter)); 0091 connect(this, &CanvasEdge::hoverLeave, view, static_cast<void (DotGraphView::*)(CanvasEdge *)>(&DotGraphView::slotElementHoverLeave)); 0092 } 0093 0094 CanvasEdge::~CanvasEdge() 0095 { 0096 delete m_popup; 0097 } 0098 0099 QRectF CanvasEdge::boundingRect() const 0100 { 0101 return m_boundingRect; 0102 } 0103 0104 QPainterPath CanvasEdge::shape() const 0105 { 0106 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id(); 0107 0108 if (!m_shape.isEmpty()) { 0109 return m_shape; 0110 } 0111 0112 for (const DotRenderOp &dro : edge()->renderOperations()) { 0113 if (dro.renderop == "B") { 0114 for (int splineNum = 0; splineNum < edge()->colors().count() || (splineNum == 0 && edge()->colors().count() == 0); splineNum++) { 0115 m_shape.addPath(pathForSpline(splineNum, dro)); 0116 } 0117 } 0118 } 0119 return m_shape; 0120 } 0121 0122 QPainterPath CanvasEdge::pathForSpline(int splineNum, const DotRenderOp &dro) const 0123 { 0124 QPolygonF points(dro.integers[0]); 0125 for (int i = 0; i < dro.integers[0]; i++) { 0126 // computing of diffX and diffY to draw parallel edges 0127 // when asked through the corresponding Graphviz feature 0128 qreal nom = (dro.integers[2 * dro.integers[0]] - dro.integers[2]); 0129 qreal denom = (dro.integers[2 * dro.integers[0] - 1] - dro.integers[1]); 0130 qreal diffX, diffY; 0131 if (nom == 0) { 0132 diffX = 0; 0133 diffY = 2 * (edge()->colors().count() / 2 - splineNum); 0134 } else if (denom == 0) { 0135 diffX = 2 * (edge()->colors().count() / 2 - splineNum); 0136 diffY = 0; 0137 } else { 0138 double pente = nom / denom; 0139 if (pente < 0) { 0140 diffX = 2 * (edge()->colors().count() / 2 - splineNum); 0141 diffY = edge()->colors().count() / 2 + splineNum; 0142 } else { 0143 diffX = 2 * (edge()->colors().count() / 2 - splineNum); 0144 diffY = 2 * (edge()->colors().count() / 2 - splineNum); 0145 } 0146 } 0147 QPointF p((dro.integers[2 * i + 1] /*%m_wdhcf*/ * m_scaleX) + m_xMargin + diffX, (m_gh - dro.integers[2 * i + 2] /*%m_hdvcf*/) * m_scaleY + m_yMargin + diffY); 0148 points[i] = p; 0149 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id() << p; 0150 } 0151 0152 QPainterPath path; 0153 path.moveTo(points[0]); 0154 for (int j = 0; j < (points.size() - 1) / 3; j++) { 0155 path.cubicTo(points[3 * j + 1], points[3 * j + 1 + 1], points[3 * j + 2 + 1]); 0156 } 0157 return path; 0158 } 0159 0160 void CanvasEdge::paint(QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget) 0161 { 0162 // qCDebug(KGRAPHVIEWERLIB_LOG); 0163 Q_UNUSED(option) 0164 Q_UNUSED(widget) 0165 if (m_boundingRect == QRectF()) { 0166 return; 0167 } 0168 /// computes the scaling of line width 0169 qreal widthScaleFactor = (m_scaleX + m_scaleY) / 2; 0170 if (widthScaleFactor < 1) { 0171 widthScaleFactor = 1; 0172 } 0173 0174 if (edge()->style() == "invis") { 0175 return; 0176 } 0177 if (edge()->renderOperations().isEmpty()) { 0178 if ((edge()->fromNode()->canvasElement()) && (edge()->toNode()->canvasElement())) { 0179 p->drawLine(edge()->fromNode()->canvasElement()->boundingRect().center() + edge()->fromNode()->canvasElement()->pos(), edge()->toNode()->canvasElement()->boundingRect().center() + edge()->toNode()->canvasElement()->pos()); 0180 } 0181 return; 0182 } 0183 0184 QColor lineColor = Dot2QtConsts::componentData().qtColor(edge()->color(0)); 0185 QColor backColor; 0186 0187 QList<QPointF> allPoints; 0188 0189 const QFont oldFont = p->font(); 0190 const QPen oldPen = p->pen(); 0191 const QBrush oldBrush = p->brush(); 0192 0193 for (const DotRenderOp &dro : edge()->renderOperations()) { 0194 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id() << "renderop" << dro.renderop << "; selected:" << edge()->isSelected(); 0195 if (dro.renderop == "c") { 0196 QColor c(dro.str.mid(0, 7)); 0197 bool ok; 0198 c.setAlpha(255 - dro.str.mid(8).toInt(&ok, 16)); 0199 lineColor = c; 0200 // qCDebug(KGRAPHVIEWERLIB_LOG) << "c" << dro.str.mid(0,7) << lineColor; 0201 } else if (dro.renderop == "C") { 0202 QColor c(dro.str.mid(0, 7)); 0203 bool ok; 0204 c.setAlpha(255 - dro.str.mid(8).toInt(&ok, 16)); 0205 /* if (m_hovered && m_view->highlighting()) 0206 { 0207 c = c.lighter(); 0208 }*/ 0209 backColor = c; 0210 // qCDebug(KGRAPHVIEWERLIB_LOG) << "C" << dro.str.mid(0,7) << backColor; 0211 } else if (dro.renderop == "T") { 0212 const QString &str = dro.str; 0213 0214 qreal stringWidthGoal = dro.integers[3] * m_scaleX; 0215 int fontSize = edge()->fontSize(); 0216 m_font->setPointSize(fontSize); 0217 QFontMetrics fm(*m_font); 0218 while (fm.horizontalAdvance(str) > stringWidthGoal && fontSize > 1) { 0219 fontSize--; 0220 m_font->setPointSize(fontSize); 0221 fm = QFontMetrics(*m_font); 0222 } 0223 0224 p->setFont(*m_font); 0225 0226 p->setPen(Dot2QtConsts::componentData().qtColor(edge()->fontColor())); 0227 0228 qreal x = (m_scaleX * ((dro.integers[0]) + (((-dro.integers[2]) * (fm.horizontalAdvance(dro.str))) / 2) - ((fm.horizontalAdvance(dro.str)) / 2))) + m_xMargin; 0229 qreal y = ((m_gh - (dro.integers[1])) * m_scaleY) + m_yMargin; 0230 QPointF point(x, y); 0231 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id() << "drawText" << edge()->fontColor() << point; 0232 0233 p->drawText(point, str); 0234 0235 p->setFont(oldFont); 0236 p->setPen(oldPen); 0237 } else if ((dro.renderop == "p") || (dro.renderop == "P")) { 0238 QPolygonF polygon(dro.integers[0]); 0239 for (int i = 0; i < dro.integers[0]; i++) { 0240 QPointF point((int(dro.integers[2 * i + 1]) /*%m_wdhcf*/) * m_scaleX + m_xMargin, (int(m_gh - dro.integers[2 * i + 2]) /*%m_hdvcf*/) * m_scaleY + m_yMargin); 0241 polygon[i] = point; 0242 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id() << point; 0243 allPoints.append(point); 0244 } 0245 if (dro.renderop == "P") { 0246 p->setBrush(lineColor); 0247 p->drawPolygon(polygon); 0248 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id() << "drawPolygon" << edge()->color(0) << polygon; 0249 p->setBrush(oldBrush); 0250 } else { 0251 p->setBrush(Dot2QtConsts::componentData().qtColor("white")); 0252 } 0253 QPen pen(lineColor); 0254 if (edge()->style() == "bold") { 0255 pen.setStyle(Qt::SolidLine); 0256 pen.setWidth((int)(2 * widthScaleFactor)); 0257 } else { 0258 pen.setWidth((int)(1 * widthScaleFactor)); 0259 pen.setStyle(Dot2QtConsts::componentData().qtPenStyle(edge()->style())); 0260 } 0261 p->setPen(pen); 0262 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id() << "drawPolyline" << edge()->color(0) << polygon; 0263 polygon << polygon[0]; 0264 p->drawPolyline(polygon); 0265 p->setPen(oldPen); 0266 p->setBrush(oldBrush); 0267 } else if ((dro.renderop == "e") || (dro.renderop == "E")) { 0268 qreal w = m_scaleX * dro.integers[2] * 2; 0269 qreal h = m_scaleY * dro.integers[3] * 2; 0270 qreal x = (m_xMargin + (dro.integers[0] /*%m_wdhcf*/) * m_scaleX) - w / 2; 0271 qreal y = ((m_gh - dro.integers[1] /*%m_hdvcf*/) * m_scaleY + m_yMargin) - h / 2; 0272 if (dro.renderop == "E") { 0273 p->setBrush(lineColor); 0274 } else { 0275 p->setBrush(Dot2QtConsts::componentData().qtColor("white")); 0276 } 0277 QPen pen(lineColor); 0278 if (edge()->style() == "bold") { 0279 pen.setStyle(Qt::SolidLine); 0280 pen.setWidth(int(2 * widthScaleFactor)); 0281 } else { 0282 pen.setWidth(int(1 * widthScaleFactor)); 0283 pen.setStyle(Dot2QtConsts::componentData().qtPenStyle(edge()->style())); 0284 } 0285 p->setPen(pen); 0286 QRectF rect(x, y, w, h); 0287 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id() << "drawEllipse" << edge()->color(0) << rect; 0288 p->drawEllipse(rect); 0289 p->setPen(oldPen); 0290 p->setBrush(oldBrush); 0291 } else if (dro.renderop == "B") { 0292 uint lineWidth = 1; 0293 QPen pen; 0294 if (edge()->style() == "bold") { 0295 pen.setStyle(Qt::SolidLine); 0296 pen.setWidth(int(2 * widthScaleFactor)); 0297 } else if (edge()->style() != "filled") { 0298 pen.setStyle(Dot2QtConsts::componentData().qtPenStyle(edge()->style())); 0299 } 0300 if (edge()->style().left(12) == "setlinewidth") { 0301 bool ok; 0302 lineWidth = edge()->style().mid(12, edge()->style().length() - 1 - 12).toInt(&ok); 0303 pen.setWidth(int(lineWidth * widthScaleFactor)); 0304 } 0305 if (edge()->attributes().contains("penwidth")) { 0306 bool ok; 0307 lineWidth = edge()->attributes()["penwidth"].toInt(&ok); 0308 pen.setWidth(int(lineWidth * widthScaleFactor)); 0309 } 0310 if (edge()->attributes().contains("color")) { 0311 qCDebug(KGRAPHVIEWERLIB_LOG) << "set edge color to " << QColor(edge()->attributes()["color"]).name(); 0312 lineColor = QColor(edge()->attributes()["color"]); 0313 } 0314 for (int splineNum = 0; splineNum < edge()->colors().count() || (splineNum == 0 && edge()->colors().count() == 0); splineNum++) { 0315 if (splineNum != 0) 0316 lineColor = Dot2QtConsts::componentData().qtColor(edge()->color(splineNum)); 0317 pen.setColor(lineColor); 0318 // p->setBrush(Dot2QtConsts::componentData().qtColor(edge()->color(0))); 0319 p->setBrush(Qt::NoBrush); 0320 p->setPen(pen); 0321 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id() << "drawPath" << edge()->color(splineNum) << points.first() << points.last(); 0322 p->drawPath(pathForSpline(splineNum, dro)); 0323 p->setPen(oldPen); 0324 p->setBrush(oldBrush); 0325 } 0326 } 0327 } 0328 if (edge()->isSelected()) { 0329 // qCDebug(KGRAPHVIEWERLIB_LOG) << "draw square"; 0330 // p->drawRect(m_boundingRect); 0331 qreal maxDist = 0; 0332 QPair<QPointF, QPointF> pointsPair; 0333 for (const QPointF &point1 : allPoints) { 0334 for (const QPointF &point2 : allPoints) { 0335 if (distance(point1, point2) > maxDist) { 0336 maxDist = distance(point1, point2); 0337 pointsPair = qMakePair(point1, point2); 0338 } 0339 } 0340 } 0341 if (maxDist > 0) { 0342 // p->setBrush(Dot2QtConsts::componentData().qtColor(edge()->color(0))); 0343 p->setBrush(Qt::black); 0344 p->setPen(Qt::black); 0345 p->drawRect(QRectF(pointsPair.first - QPointF(3, 3), QSizeF(6, 6))); 0346 p->drawRect(QRectF(pointsPair.second - QPointF(3, 3), QSizeF(6, 6))); 0347 p->setBrush(oldBrush); 0348 p->setPen(oldPen); 0349 } 0350 } 0351 } 0352 0353 void CanvasEdge::modelChanged() 0354 { 0355 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id(); 0356 prepareGeometryChange(); 0357 computeBoundingRect(); 0358 } 0359 0360 void CanvasEdge::computeBoundingRect() 0361 { 0362 // qCDebug(KGRAPHVIEWERLIB_LOG); 0363 // invalidate bounding region cache 0364 m_shape = QPainterPath(); 0365 if (edge()->renderOperations().isEmpty()) { 0366 if ((edge()->fromNode()->canvasElement() == nullptr) || (edge()->toNode()->canvasElement() == nullptr) || edge()->style() == "invis") { 0367 m_boundingRect = QRectF(); 0368 } else { 0369 QRectF br(edge()->fromNode()->canvasElement()->boundingRect().center() + edge()->fromNode()->canvasElement()->pos(), edge()->toNode()->canvasElement()->boundingRect().center() + edge()->toNode()->canvasElement()->pos()); 0370 // qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id() <<br; 0371 m_boundingRect = br; 0372 } 0373 } else { 0374 QPolygonF points; 0375 for (const DotRenderOp &dro : edge()->renderOperations()) { 0376 // qCDebug(KGRAPHVIEWERLIB_LOG) << dro.renderop << ", "; 0377 if ((dro.renderop != "B") && (dro.renderop != "p") && (dro.renderop != "P")) 0378 continue; 0379 uint previousSize = points.size(); 0380 points.resize(previousSize + dro.integers[0]); 0381 for (int i = 0; i < dro.integers[0]; i++) { 0382 QPointF p(((dro.integers[2 * i + 1] /*%m_wdhcf*/) * m_scaleX) + m_xMargin, ((m_gh - dro.integers[2 * i + 2] /*%m_hdvcf*/) * m_scaleY) + m_yMargin); 0383 points[previousSize + i] = p; 0384 } 0385 } 0386 // qCDebug(KGRAPHVIEWERLIB_LOG) << points.size() << "points"; 0387 if (points.size() == 0) 0388 return; 0389 0390 int len = points.count(); 0391 QPolygonF a = points, b = points; 0392 a.translate(-1, -1); 0393 b.translate(1, 1); 0394 a.resize(2 * len); 0395 for (int i = 0; i < len; i++) { 0396 a[len + i] = b[i]; 0397 } 0398 // qCDebug(KGRAPHVIEWERLIB_LOG) << a.size() << "points"; 0399 0400 m_boundingRect = a.boundingRect(); 0401 } 0402 qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->fromNode()->id() << "->" << edge()->toNode()->id() << "New bounding rect is:" << m_boundingRect; 0403 } 0404 0405 void CanvasEdge::mousePressEvent(QGraphicsSceneMouseEvent *event) 0406 { 0407 qCDebug(KGRAPHVIEWERLIB_LOG) << event; 0408 if (m_view->isReadOnly()) { 0409 return; 0410 } 0411 if (event->button() == Qt::LeftButton) { 0412 edge()->setSelected(!edge()->isSelected()); 0413 if (edge()->isSelected()) { 0414 emit(selected(this, event->modifiers())); 0415 } 0416 update(); 0417 } else if (event->button() == Qt::RightButton) { 0418 if (!edge()->isSelected()) { 0419 edge()->setSelected(true); 0420 emit(selected(this, event->modifiers())); 0421 update(); 0422 } 0423 qCDebug(KGRAPHVIEWERLIB_LOG) << "emiting edgeContextMenuEvent(" << m_edge->id() << "," << event->screenPos() << ")"; 0424 emit(edgeContextMenuEvent(m_edge->id(), event->screenPos())); 0425 // opens the selected edge contextual menu and if necessary select the edge 0426 /* qCDebug(KGRAPHVIEWERLIB_LOG) << "opens the contextual menu"; 0427 m_popup->exec(event->screenPos());*/ 0428 } 0429 } 0430 0431 qreal CanvasEdge::distance(const QPointF &point1, const QPointF &point2) 0432 { 0433 return sqrt(pow(point1.x() - point2.x(), 2) + pow(point1.y() - point2.y(), 2)); 0434 } 0435 0436 void CanvasEdge::slotRemoveEdge() 0437 { 0438 m_view->removeSelectedElements(); 0439 } 0440 0441 void CanvasEdge::hoverEnterEvent(QGraphicsSceneHoverEvent *event) 0442 { 0443 Q_UNUSED(event) 0444 qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->id(); 0445 emit hoverEnter(this); 0446 } 0447 0448 void CanvasEdge::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) 0449 { 0450 Q_UNUSED(event) 0451 qCDebug(KGRAPHVIEWERLIB_LOG) << edge()->id(); 0452 emit hoverLeave(this); 0453 } 0454 0455 } 0456 0457 #include "moc_canvasedge.cpp"