File indexing completed on 2024-05-19 15:27:48
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 /* 0028 * Callgraph View 0029 */ 0030 0031 #include "dotgraphview.h" 0032 0033 #include "FontsCache.h" 0034 #include "canvasedge.h" 0035 #include "canvasnode.h" 0036 #include "canvassubgraph.h" 0037 #include "dot2qtconsts.h" 0038 #include "dotgraph.h" 0039 #include "graphedge.h" 0040 #include "graphelement.h" 0041 #include "graphexporter.h" 0042 #include "graphnode.h" 0043 #include "kgraphviewer_partsettings.h" 0044 #include "kgraphviewerlib_debug.h" 0045 #include "layoutagraphthread.h" 0046 #include "loadagraphthread.h" 0047 #include "pannerview.h" 0048 #include "simpleprintingcommand.h" 0049 0050 #include <iostream> 0051 #include <math.h> 0052 #include <stdlib.h> 0053 0054 #include <QApplication> 0055 #include <QBitmap> 0056 #include <QContextMenuEvent> 0057 #include <QDebug> 0058 #include <QFileDialog> 0059 #include <QFocusEvent> 0060 #include <QGraphicsSimpleTextItem> 0061 #include <QImage> 0062 #include <QImageWriter> 0063 #include <QInputDialog> 0064 #include <QKeyEvent> 0065 #include <QMenu> 0066 #include <QMouseEvent> 0067 #include <QPainter> 0068 #include <QPixmap> 0069 #include <QResizeEvent> 0070 #include <QScrollBar> 0071 #include <QStandardPaths> 0072 #include <QStyle> 0073 #include <QSvgGenerator> 0074 #include <QTransform> 0075 #include <QUuid> 0076 #include <QWheelEvent> 0077 0078 #include <kwidgetsaddons_version.h> 0079 #include <kactionmenu.h> 0080 #include <klocalizedstring.h> 0081 #include <kmessagebox.h> 0082 #include <kselectaction.h> 0083 #include <ktoggleaction.h> 0084 0085 // DotGraphView defaults 0086 0087 #define DEFAULT_ZOOMPOS KGraphViewerInterface::Auto 0088 #define KGV_MAX_PANNER_NODES 100 0089 0090 namespace KGraphViewer 0091 { 0092 class DotGraphViewPrivate 0093 { 0094 public: 0095 DotGraphViewPrivate(KActionCollection *actions, DotGraphView *parent) 0096 : m_labelViews() 0097 , m_popup(nullptr) 0098 , m_zoom(1) 0099 , m_isMoving(false) 0100 , m_exporter() 0101 , m_zoomPosition(DEFAULT_ZOOMPOS) 0102 , m_lastAutoPosition(KGraphViewerInterface::TopLeft) 0103 , m_graph(nullptr) 0104 , m_printCommand(nullptr) 0105 , m_actions(actions) 0106 , m_detailLevel(DEFAULT_DETAILLEVEL) 0107 , m_defaultNewElement(nullptr) 0108 , m_defaultNewElementPixmap(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kgraphviewerpart/pics/kgraphviewer-newnode.png")) 0109 , m_editingMode(DotGraphView::None) 0110 , m_newEdgeSource(nullptr) 0111 , m_newEdgeDraft(nullptr) 0112 , m_readWrite(false) 0113 , m_leavedTimer(std::numeric_limits<int>::max()) 0114 , m_highlighting(false) 0115 , m_loadThread() 0116 , m_layoutThread() 0117 , m_backgroundColor(QColor("white")) 0118 , q_ptr(parent) 0119 { 0120 } 0121 ~DotGraphViewPrivate() 0122 { 0123 delete m_birdEyeView; 0124 m_birdEyeView = nullptr; 0125 delete m_popup; 0126 if (m_canvas) { 0127 Q_Q(DotGraphView); 0128 q->setScene(nullptr); 0129 delete m_canvas; 0130 } 0131 delete m_graph; 0132 } 0133 0134 void updateSizes(QSizeF s = QSizeF(0, 0)); 0135 void updateBirdEyeView(); 0136 void setupPopup(); 0137 void exportToImage(); 0138 KActionCollection *actionCollection() 0139 { 0140 return m_actions; 0141 } 0142 double detailAdjustedScale(); 0143 int displaySubgraph(GraphSubgraph *gsubgraph, int zValue, CanvasElement *parent = nullptr); 0144 0145 QSet<QGraphicsSimpleTextItem *> m_labelViews; 0146 QGraphicsScene *m_canvas; 0147 QMenu *m_popup; 0148 KSelectAction *m_bevPopup; 0149 KSelectAction *m_layoutAlgoSelectAction; 0150 int m_xMargin, m_yMargin; 0151 PannerView *m_birdEyeView; 0152 double m_cvZoom; 0153 double m_zoom; 0154 bool m_isMoving; 0155 QPoint m_lastPos; 0156 0157 GraphExporter m_exporter; 0158 0159 // widget options 0160 KGraphViewerInterface::PannerPosition m_zoomPosition, m_lastAutoPosition; 0161 0162 DotGraph *m_graph; 0163 0164 KGVSimplePrintingCommand *m_printCommand; 0165 0166 KToggleAction *m_bevEnabledAction; 0167 KActionCollection *m_actions; 0168 0169 int m_detailLevel; 0170 0171 GraphElement *m_defaultNewElement; 0172 0173 /** image used for a new node just added in an edited graph because this new node has 0174 * still no attribute and thus no render operation */ 0175 QPixmap m_defaultNewElementPixmap; 0176 DotGraphView::EditingMode m_editingMode; 0177 0178 CanvasElement *m_newEdgeSource; 0179 QGraphicsLineItem *m_newEdgeDraft; 0180 0181 bool m_readWrite; 0182 0183 QMap<QString, QString> m_newElementAttributes; 0184 0185 /// identifier of the timer started when the mouse leaves the view during 0186 /// edge drawing 0187 int m_leavedTimer; 0188 0189 DotGraphView::ScrollDirection m_scrollDirection; 0190 0191 QPoint m_pressPos; 0192 QPoint m_pressScrollBarsPos; 0193 0194 /// true if elements should be highlighted on hover; false otherwise 0195 bool m_highlighting; 0196 0197 /// A thread to load graphviz agraph files 0198 LoadAGraphThread m_loadThread; 0199 0200 /// A thread to layout graphviz agraph files 0201 LayoutAGraphThread m_layoutThread; 0202 0203 /// The graph background color 0204 QColor m_backgroundColor; 0205 0206 DotGraphView *const q_ptr; 0207 Q_DECLARE_PUBLIC(DotGraphView); 0208 }; 0209 0210 void DotGraphViewPrivate::updateSizes(QSizeF s) 0211 { 0212 Q_Q(DotGraphView); 0213 if (m_canvas == nullptr) 0214 return; 0215 if (s == QSizeF(0, 0)) 0216 s = q->size(); 0217 0218 // the part of the canvas that should be visible 0219 qreal cWidth = m_canvas->width() - 2 * m_xMargin + 100; 0220 qreal cHeight = m_canvas->height() - 2 * m_yMargin + 100; 0221 0222 // hide birds eye view if no overview needed 0223 if ( //!_data || !_activeItem || 0224 !KGraphViewerPartSettings::birdsEyeViewEnabled() || (((cWidth * m_zoom) < s.width()) && (cHeight * m_zoom) < s.height())) { 0225 m_birdEyeView->hide(); 0226 return; 0227 } 0228 m_birdEyeView->hide(); 0229 0230 // first, assume use of 1/3 of width/height (possible larger) 0231 double zoom = .33 * s.width() / cWidth; 0232 if (zoom * cHeight < .33 * s.height()) 0233 zoom = .33 * s.height() / cHeight; 0234 0235 // fit to widget size 0236 if (cWidth * zoom > s.width()) 0237 zoom = s.width() / (double)cWidth; 0238 if (cHeight * zoom > s.height()) 0239 zoom = s.height() / (double)cHeight; 0240 0241 // scale to never use full height/width 0242 zoom = zoom * 3 / 4; 0243 0244 // at most a zoom of 1/3 0245 if (zoom > .33) 0246 zoom = .33; 0247 0248 if (zoom != m_cvZoom) { 0249 m_cvZoom = zoom; 0250 0251 QTransform wm; 0252 wm.scale(zoom, zoom); 0253 m_birdEyeView->setTransform(wm); 0254 0255 // make it a little bigger to compensate for widget frame 0256 m_birdEyeView->resize((cWidth * zoom) + 4, (cHeight * zoom) + 4); 0257 } 0258 updateBirdEyeView(); 0259 m_birdEyeView->setZoomRect(q->mapToScene(q->viewport()->rect()).boundingRect()); 0260 m_birdEyeView->show(); 0261 QSizeF newCanvasSize = m_canvas->sceneRect().size(); 0262 if (newCanvasSize.width() < q->viewport()->width()) { 0263 newCanvasSize.setWidth(q->viewport()->width()); 0264 } else if (q->viewport()->width() < m_canvas->sceneRect().size().width()) { 0265 newCanvasSize.setWidth(m_canvas->sceneRect().size().width()); 0266 } 0267 if (newCanvasSize.height() < q->viewport()->height()) { 0268 newCanvasSize.setHeight(q->viewport()->height()); 0269 } else if (q->viewport()->height() < m_canvas->sceneRect().size().height()) { 0270 newCanvasSize.setHeight(m_canvas->sceneRect().size().height()); 0271 } 0272 // std::cerr << "done." << std::endl; 0273 } 0274 0275 void DotGraphViewPrivate::updateBirdEyeView() 0276 { 0277 Q_Q(DotGraphView); 0278 qreal cvW = m_birdEyeView->width(); 0279 qreal cvH = m_birdEyeView->height(); 0280 qreal x = q->width() - cvW - q->verticalScrollBar()->width() - 2; 0281 qreal y = q->height() - cvH - q->horizontalScrollBar()->height() - 2; 0282 QPoint oldZoomPos = m_birdEyeView->pos(); 0283 QPoint newZoomPos = QPoint(0, 0); 0284 KGraphViewerInterface::PannerPosition zp = m_zoomPosition; 0285 if (zp == KGraphViewerInterface::Auto) { 0286 QPointF tl1Pos = q->mapToScene(QPoint(0, 0)); 0287 QPointF tl2Pos = q->mapToScene(QPoint(cvW, cvH)); 0288 QPointF tr1Pos = q->mapToScene(QPoint(x, 0)); 0289 QPointF tr2Pos = q->mapToScene(QPoint(x + cvW, cvH)); 0290 QPointF bl1Pos = q->mapToScene(QPoint(0, y)); 0291 QPointF bl2Pos = q->mapToScene(QPoint(cvW, y + cvH)); 0292 QPointF br1Pos = q->mapToScene(QPoint(x, y)); 0293 QPointF br2Pos = q->mapToScene(QPoint(x + cvW, y + cvH)); 0294 int tlCols = m_canvas->items(QRectF(tl1Pos.x(), tl1Pos.y(), tl2Pos.x(), tl2Pos.y())).count(); 0295 qCDebug(KGRAPHVIEWERLIB_LOG) << tr1Pos.x() << tr1Pos.y() << tr2Pos.x() << tr2Pos.y(); 0296 int trCols = m_canvas->items(QRectF(tr1Pos.x(), tr1Pos.y(), tr2Pos.x(), tr2Pos.y())).count(); 0297 int blCols = m_canvas->items(QRectF(bl1Pos.x(), bl1Pos.y(), bl2Pos.x(), bl2Pos.y())).count(); 0298 int brCols = m_canvas->items(QRectF(br1Pos.x(), br1Pos.y(), br2Pos.x(), br2Pos.y())).count(); 0299 int minCols = tlCols; 0300 zp = m_lastAutoPosition; 0301 switch (zp) { 0302 case KGraphViewerInterface::TopRight: 0303 minCols = trCols; 0304 break; 0305 case KGraphViewerInterface::BottomLeft: 0306 minCols = blCols; 0307 break; 0308 case KGraphViewerInterface::BottomRight: 0309 minCols = brCols; 0310 break; 0311 default: 0312 case KGraphViewerInterface::TopLeft: 0313 minCols = tlCols; 0314 break; 0315 } 0316 if (minCols > tlCols) { 0317 minCols = tlCols; 0318 zp = KGraphViewerInterface::TopLeft; 0319 } 0320 if (minCols > trCols) { 0321 minCols = trCols; 0322 zp = KGraphViewerInterface::TopRight; 0323 } 0324 if (minCols > blCols) { 0325 minCols = blCols; 0326 zp = KGraphViewerInterface::BottomLeft; 0327 } 0328 if (minCols > brCols) { 0329 minCols = brCols; 0330 zp = KGraphViewerInterface::BottomRight; 0331 } 0332 0333 m_lastAutoPosition = zp; 0334 } 0335 0336 switch (zp) { 0337 case KGraphViewerInterface::TopRight: 0338 newZoomPos = QPoint(x, 0); 0339 break; 0340 case KGraphViewerInterface::BottomLeft: 0341 newZoomPos = QPoint(0, y); 0342 break; 0343 case KGraphViewerInterface::BottomRight: 0344 newZoomPos = QPoint(x, y); 0345 break; 0346 default: 0347 break; 0348 } 0349 if (newZoomPos != oldZoomPos) 0350 m_birdEyeView->move(newZoomPos); 0351 } 0352 0353 double DotGraphViewPrivate::detailAdjustedScale() 0354 { 0355 double scale = m_graph->scale(); 0356 switch (m_detailLevel) { 0357 case 0: 0358 scale *= 0.7; 0359 break; 0360 case 2: 0361 scale *= 1.3; 0362 break; 0363 } 0364 return scale; 0365 } 0366 0367 int DotGraphViewPrivate::displaySubgraph(GraphSubgraph *gsubgraph, int zValue, CanvasElement *parent) 0368 { 0369 Q_Q(DotGraphView); 0370 double scale = detailAdjustedScale(); 0371 0372 qreal gh = m_graph->height(); 0373 0374 if (gsubgraph->canvasSubgraph() == nullptr) { 0375 qCDebug(KGRAPHVIEWERLIB_LOG) << "Creating canvas subgraph for" << gsubgraph->id(); 0376 CanvasSubgraph *csubgraph = new CanvasSubgraph(q, gsubgraph, m_canvas, parent); 0377 csubgraph->initialize(scale, scale, m_xMargin, m_yMargin, gh); 0378 gsubgraph->setCanvasSubgraph(csubgraph); 0379 // csubgraph->setZValue(gsubgraph->z()); 0380 csubgraph->setZValue(zValue += 2); 0381 csubgraph->show(); 0382 m_canvas->addItem(csubgraph); 0383 qCDebug(KGRAPHVIEWERLIB_LOG) << " one CanvasSubgraph... Done"; 0384 } 0385 for (GraphElement *element : gsubgraph->content()) { 0386 GraphNode *gnode = dynamic_cast<GraphNode *>(element); 0387 if (gnode->canvasNode() == nullptr) { 0388 qCDebug(KGRAPHVIEWERLIB_LOG) << "Creating canvas node for:" << gnode->id(); 0389 CanvasNode *cnode = new CanvasNode(q, gnode, m_canvas); 0390 if (cnode == nullptr) 0391 continue; 0392 cnode->initialize(scale, scale, m_xMargin, m_yMargin, gh); 0393 gnode->setCanvasNode(cnode); 0394 m_canvas->addItem(cnode); 0395 // cnode->setZValue(gnode->z()); 0396 cnode->setZValue(zValue + 1); 0397 cnode->show(); 0398 } 0399 gnode->canvasNode()->computeBoundingRect(); 0400 } 0401 gsubgraph->canvasSubgraph()->computeBoundingRect(); 0402 0403 int newZvalue = zValue; 0404 for (GraphSubgraph *ssg : gsubgraph->subgraphs()) { 0405 int hereZvalue = displaySubgraph(ssg, zValue, gsubgraph->canvasSubgraph()); 0406 if (hereZvalue > newZvalue) 0407 newZvalue = hereZvalue; 0408 } 0409 return newZvalue; 0410 } 0411 0412 void DotGraphViewPrivate::setupPopup() 0413 { 0414 Q_Q(DotGraphView); 0415 if (m_popup) { 0416 return; 0417 } 0418 qCDebug(KGRAPHVIEWERLIB_LOG) << "DotGraphView::setupPopup"; 0419 m_popup = new QMenu(); 0420 0421 m_layoutAlgoSelectAction = new KSelectAction(i18n("Select Layout Algo"), q); 0422 actionCollection()->addAction("view_layout_algo", m_layoutAlgoSelectAction); 0423 0424 QAction *lda = new QAction(i18n("Dot"), q); 0425 lda->setWhatsThis(i18n("Layout the graph using the dot program.")); 0426 actionCollection()->addAction("layout_dot", lda); 0427 lda->setCheckable(true); 0428 0429 QAction *lna = new QAction(i18n("Neato"), q); 0430 lna->setWhatsThis(i18n("Layout the graph using the neato program.")); 0431 actionCollection()->addAction("layout_neato", lna); 0432 lna->setCheckable(true); 0433 0434 QAction *lta = new QAction(i18n("Twopi"), q); 0435 lta->setWhatsThis(i18n("Layout the graph using the twopi program.")); 0436 actionCollection()->addAction("layout_twopi", lta); 0437 lta->setCheckable(true); 0438 0439 QAction *lfa = new QAction(i18n("Fdp"), q); 0440 lfa->setWhatsThis(i18n("Layout the graph using the fdp program.")); 0441 actionCollection()->addAction("layout_fdp", lfa); 0442 lfa->setCheckable(true); 0443 0444 QAction *lca = new QAction(i18n("Circo"), q); 0445 lca->setWhatsThis(i18n("Layout the graph using the circo program.")); 0446 actionCollection()->addAction("layout_c", lca); 0447 lca->setCheckable(true); 0448 0449 m_layoutAlgoSelectAction->addAction(lda); 0450 m_layoutAlgoSelectAction->addAction(lna); 0451 m_layoutAlgoSelectAction->addAction(lta); 0452 m_layoutAlgoSelectAction->addAction(lfa); 0453 m_layoutAlgoSelectAction->addAction(lca); 0454 0455 m_layoutAlgoSelectAction->setCurrentAction(lda); 0456 m_layoutAlgoSelectAction->setEditable(true); 0457 m_layoutAlgoSelectAction->setToolTip(i18n("Choose a Graphviz layout algorithm or edit your own one.")); 0458 m_layoutAlgoSelectAction->setWhatsThis( 0459 i18n("Choose a Graphviz layout algorithm or type in your own command that will " 0460 "generate a graph in the xdot format on its standard output. For example, to " 0461 "manually specify the <tt>G</tt> option to the dot command, type in: " 0462 "<tt>dot -Gname=MyGraphName -Txdot </tt>")); 0463 QObject::connect(m_layoutAlgoSelectAction, &KSelectAction::textTriggered, q, &DotGraphView::slotSelectLayoutAlgo); 0464 0465 QMenu *layoutPopup = m_popup->addMenu(i18n("Layout")); 0466 layoutPopup->addAction(m_layoutAlgoSelectAction); 0467 QAction *slc = layoutPopup->addAction(i18n("Specify layout command"), q, SLOT(slotLayoutSpecify())); 0468 slc->setWhatsThis(i18n("Specify yourself the layout command to use. Given a dot file, it should produce an xdot file on its standard output.")); 0469 QAction *rlc = layoutPopup->addAction(i18n("Reset layout command to default"), q, SLOT(slotLayoutReset())); 0470 rlc->setWhatsThis(i18n("Resets the layout command to use to the default depending on the graph type (directed or not).")); 0471 0472 m_popup->addAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"), q, SLOT(zoomIn())); 0473 m_popup->addAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"), q, SLOT(zoomOut())); 0474 0475 m_popup->addSeparator(); 0476 0477 KActionMenu *file_exportMenu = new KActionMenu(i18n("Export Graph"), q); 0478 actionCollection()->addAction("file_export", file_exportMenu); 0479 file_exportMenu->setToolTip(i18n("Allows the graph to be exported in another format.")); 0480 file_exportMenu->setWhatsThis( 0481 i18n("Use the Export Graph menu to export the graph in another format. " 0482 "There is currently only one export format supported: as a PNG image.")); 0483 0484 m_popup->addAction(file_exportMenu); 0485 QAction *exportToImageAction = new QAction(i18n("As Image..."), q); 0486 exportToImageAction->setWhatsThis(i18n("Export the graph to an image file.")); 0487 actionCollection()->addAction("export_image", exportToImageAction); 0488 QObject::connect(exportToImageAction, &QAction::triggered, q, &DotGraphView::slotExportImage); 0489 0490 file_exportMenu->addAction(exportToImageAction); 0491 0492 m_popup->addSeparator(); 0493 0494 m_bevEnabledAction = new KToggleAction(QIcon(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kgraphviewerpart/pics/kgraphviewer-bev.png")), i18n("Enable Bird's-eye View"), q); 0495 actionCollection()->addAction("view_bev_enabled", m_bevEnabledAction); 0496 actionCollection()->setDefaultShortcut(m_bevEnabledAction, Qt::CTRL + Qt::Key_B); 0497 m_bevEnabledAction->setWhatsThis(i18n("Enables or disables the Bird's-eye View")); 0498 QObject::connect(m_bevEnabledAction, &QAction::triggered, q, &DotGraphView::slotBevToggled); 0499 m_bevEnabledAction->setCheckable(true); 0500 m_popup->addAction(m_bevEnabledAction); 0501 0502 m_bevPopup = new KSelectAction(i18n("Birds-eye View"), q); 0503 m_bevPopup->setWhatsThis(i18n("Allows the Bird's-eye View to be setup.")); 0504 m_popup->addAction(m_bevPopup); 0505 actionCollection()->addAction("view_bev", m_bevPopup); 0506 0507 QAction *btla = new QAction(i18n("Top Left"), q); 0508 btla->setWhatsThis(i18n("Puts the Bird's-eye View in the top-left corner.")); 0509 btla->setCheckable(true); 0510 actionCollection()->addAction("bev_top_left", btla); 0511 QObject::connect(btla, &QAction::triggered, q, &DotGraphView::slotBevTopLeft); 0512 QAction *btra = new QAction(i18n("Top Right"), q); 0513 btra->setWhatsThis(i18n("Puts the Bird's-eye View in the top-right corner.")); 0514 btra->setCheckable(true); 0515 actionCollection()->addAction("bev_top_right", btra); 0516 QObject::connect(btra, &QAction::triggered, q, &DotGraphView::slotBevTopRight); 0517 QAction *bbla = new QAction(i18n("Bottom Left"), q); 0518 bbla->setWhatsThis(i18n("Puts the Bird's-eye View in the bottom-left corner.")); 0519 bbla->setCheckable(true); 0520 actionCollection()->addAction("bev_bottom_left", bbla); 0521 QObject::connect(bbla, &QAction::triggered, q, &DotGraphView::slotBevBottomLeft); 0522 QAction *bbra = new QAction(i18n("Bottom Right"), q); 0523 bbra->setWhatsThis(i18n("Puts the Bird's-eye View in the bottom-right corner.")); 0524 bbra->setCheckable(true); 0525 actionCollection()->addAction("bev_bottom_right", bbra); 0526 QObject::connect(bbra, &QAction::triggered, q, &DotGraphView::slotBevBottomRight); 0527 QAction *bba = new QAction(i18n("Automatic"), q); 0528 bba->setWhatsThis(i18n("Let KGraphViewer automatically choose the position of the Bird's-eye View.")); 0529 bba->setCheckable(true); 0530 actionCollection()->addAction("bev_automatic", bba); 0531 QObject::connect(bba, &QAction::triggered, q, &DotGraphView::slotBevAutomatic); 0532 m_bevPopup->addAction(btla); 0533 m_bevPopup->addAction(btra); 0534 m_bevPopup->addAction(bbla); 0535 m_bevPopup->addAction(bbra); 0536 m_bevPopup->addAction(bba); 0537 switch (m_zoomPosition) { 0538 case KGraphViewerInterface::TopLeft: 0539 btla->setChecked(true); 0540 break; 0541 case KGraphViewerInterface::TopRight: 0542 btra->setChecked(true); 0543 break; 0544 case KGraphViewerInterface::BottomLeft: 0545 bbla->setChecked(true); 0546 break; 0547 case KGraphViewerInterface::BottomRight: 0548 bbra->setChecked(true); 0549 break; 0550 case KGraphViewerInterface::Auto: 0551 bba->setChecked(true); 0552 break; 0553 } 0554 0555 qCDebug(KGRAPHVIEWERLIB_LOG) << " m_bevEnabledAction setting checked to : " << KGraphViewerPartSettings::birdsEyeViewEnabled(); 0556 m_bevEnabledAction->setChecked(KGraphViewerPartSettings::birdsEyeViewEnabled()); 0557 m_bevPopup->setEnabled(KGraphViewerPartSettings::birdsEyeViewEnabled()); 0558 } 0559 0560 void DotGraphViewPrivate::exportToImage() 0561 { 0562 // write current content of canvas as image to file 0563 if (!m_canvas) 0564 return; 0565 0566 QStringList writableMimetypes; 0567 for (const QByteArray &mimeType : QImageWriter::supportedMimeTypes()) { 0568 writableMimetypes.append(QString::fromLatin1(mimeType)); 0569 } 0570 const QString svgMimetype = QStringLiteral("image/svg+xml"); 0571 if (!writableMimetypes.contains(svgMimetype)) { 0572 writableMimetypes.append(svgMimetype); 0573 } 0574 0575 QFileDialog fileDialog(nullptr, i18n("Select file")); 0576 fileDialog.setMimeTypeFilters(writableMimetypes); 0577 fileDialog.setAcceptMode(QFileDialog::AcceptSave); 0578 if (fileDialog.exec() != QFileDialog::Accepted) { 0579 return; 0580 } 0581 const QString fn = fileDialog.selectedFiles().at(0); 0582 0583 if (!fn.isEmpty()) { 0584 if (fn.toLower().endsWith(QLatin1String(".svg"))) { 0585 QSvgGenerator generator; 0586 generator.setFileName(fn); 0587 generator.setSize(m_canvas->sceneRect().size().toSize()); 0588 // generator.setViewBox(); 0589 generator.setTitle(i18n("Graph SVG Generated by KGraphViewer")); 0590 generator.setDescription(i18n("Graph SVG Generated by KGraphViewer.")); 0591 QPainter painter; 0592 painter.begin(&generator); 0593 m_canvas->render(&painter); 0594 painter.end(); 0595 } else { 0596 QPixmap pix(m_canvas->sceneRect().size().toSize()); 0597 QPainter p(&pix); 0598 m_canvas->render(&p); 0599 pix.save(fn, nullptr, 100); 0600 } 0601 } 0602 } 0603 0604 // 0605 // DotGraphView 0606 // 0607 DotGraphView::DotGraphView(KActionCollection *actions, QWidget *parent) 0608 : QGraphicsView(parent) 0609 , d_ptr(new DotGraphViewPrivate(actions, this)) 0610 { 0611 // qCDebug(KGRAPHVIEWERLIB_LOG) << "New node pic=" << KGlobal::dirs()->findResource("data","kgraphviewerpart/pics/kgraphviewer-newnode.png"); 0612 Q_D(DotGraphView); 0613 d->m_canvas = nullptr; 0614 d->m_xMargin = d->m_yMargin = 0; 0615 d->m_birdEyeView = new PannerView(this); 0616 d->m_cvZoom = 1; 0617 0618 // if there are ever graphic glitches to be found, remove this again 0619 setOptimizationFlags(QGraphicsView::DontAdjustForAntialiasing | QGraphicsView::DontClipPainter | QGraphicsView::DontSavePainterState); 0620 0621 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 0622 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 0623 0624 d->m_birdEyeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0625 d->m_birdEyeView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0626 d->m_birdEyeView->raise(); 0627 d->m_birdEyeView->hide(); 0628 0629 setFocusPolicy(Qt::StrongFocus); 0630 setBackgroundRole(QPalette::Window); 0631 // viewport()->setMouseTracking(true); 0632 0633 connect(d->m_birdEyeView, &PannerView::zoomRectMovedTo, this, &DotGraphView::zoomRectMovedTo); 0634 connect(d->m_birdEyeView, &PannerView::zoomRectMoveFinished, this, &DotGraphView::zoomRectMoveFinished); 0635 0636 setWhatsThis( 0637 i18n("<h1>Graphviz DOT format graph visualization</h1>" 0638 "<p>If the graph is larger than the widget area, an overview " 0639 "panner is shown in one edge. Choose through the context menu " 0640 "if the optimal position of this overview should be automatically " 0641 "computed or put it where you want.</p>" 0642 "<h2>How to work with it?</h2>" 0643 "<ul>" 0644 "<li>To move the graph, you can:" 0645 " <ul>" 0646 " <li>click & drag it</li>" 0647 " <li>use the elevators</li>" 0648 " <li>press the arrows keys</li>" 0649 " <li>click somewhere in the panner view</li>" 0650 " <li>use the mouse wheel (up and down with no modifier, left and right with the <Alt> key pressed)</li>" 0651 " <li>or click & drag the panner view</li>" 0652 " </ul>" 0653 "</li>" 0654 "<li>To zoom, you can either use the zoom in and zoom out toolbar buttons, or click on the <Shift> key while rolling your mouse wheel.</li>" 0655 "<li>Try the contextual menu (usually by right-clicking) to discover other " 0656 "possibilities.</li>" 0657 "<li>Try the <tt>Print preview</tt> or the <tt>Page setup</tt> buttons to explore the printing options.</li>" 0658 "</ul>")); 0659 0660 readViewConfig(); 0661 0662 QTransform m; 0663 m.scale(d->m_zoom, d->m_zoom); 0664 setTransform(m); 0665 d->setupPopup(); 0666 setInteractive(true); 0667 setDragMode(NoDrag); 0668 setRenderHint(QPainter::Antialiasing); 0669 0670 connect(&d->m_loadThread, &LoadAGraphThread::finished, this, &DotGraphView::slotAGraphReadFinished); 0671 connect(&d->m_layoutThread, &LoadAGraphThread::finished, this, &DotGraphView::slotAGraphLayoutFinished); 0672 } 0673 0674 DotGraphView::~DotGraphView() 0675 { 0676 saveViewConfig(); 0677 Q_D(DotGraphView); 0678 delete d; 0679 } 0680 0681 KGraphViewerInterface::PannerPosition DotGraphView::zoomPos() const 0682 { 0683 Q_D(const DotGraphView); 0684 return d->m_zoomPosition; 0685 } 0686 0687 double DotGraphView::zoom() const 0688 { 0689 Q_D(const DotGraphView); 0690 return d->m_zoom; 0691 } 0692 KSelectAction *DotGraphView::bevPopup() 0693 { 0694 Q_D(DotGraphView); 0695 return d->m_bevPopup; 0696 } 0697 0698 DotGraph *DotGraphView::graph() 0699 { 0700 Q_D(DotGraphView); 0701 return d->m_graph; 0702 } 0703 const DotGraph *DotGraphView::graph() const 0704 { 0705 Q_D(const DotGraphView); 0706 return d->m_graph; 0707 } 0708 0709 const GraphElement *DotGraphView::defaultNewElement() const 0710 { 0711 Q_D(const DotGraphView); 0712 return d->m_defaultNewElement; 0713 } 0714 QPixmap DotGraphView::defaultNewElementPixmap() const 0715 { 0716 Q_D(const DotGraphView); 0717 return d->m_defaultNewElementPixmap; 0718 } 0719 0720 void DotGraphView::setDefaultNewElement(GraphElement *elem) 0721 { 0722 Q_D(DotGraphView); 0723 d->m_defaultNewElement = elem; 0724 } 0725 void DotGraphView::setDefaultNewElementPixmap(const QPixmap &pm) 0726 { 0727 Q_D(DotGraphView); 0728 d->m_defaultNewElementPixmap = pm; 0729 } 0730 0731 bool DotGraphView::isReadWrite() const 0732 { 0733 Q_D(const DotGraphView); 0734 return d->m_readWrite; 0735 } 0736 bool DotGraphView::isReadOnly() const 0737 { 0738 Q_D(const DotGraphView); 0739 return !d->m_readWrite; 0740 } 0741 0742 bool DotGraphView::highlighting() 0743 { 0744 Q_D(DotGraphView); 0745 return d->m_highlighting; 0746 } 0747 void DotGraphView::setHighlighting(bool highlightingValue) 0748 { 0749 Q_D(DotGraphView); 0750 d->m_highlighting = highlightingValue; 0751 } 0752 0753 DotGraphView::EditingMode DotGraphView::editingMode() const 0754 { 0755 Q_D(const DotGraphView); 0756 return d->m_editingMode; 0757 } 0758 0759 void DotGraphView::setBackgroundColor(const QColor &color) 0760 { 0761 Q_D(DotGraphView); 0762 d->m_backgroundColor = color; 0763 d->m_canvas->setBackgroundBrush(QBrush(d->m_backgroundColor)); 0764 } 0765 0766 bool DotGraphView::initEmpty() 0767 { 0768 Q_D(DotGraphView); 0769 d->m_birdEyeView->hide(); 0770 d->m_birdEyeView->setScene(nullptr); 0771 0772 if (d->m_canvas) { 0773 delete d->m_canvas; 0774 d->m_canvas = nullptr; 0775 } 0776 0777 delete d->m_graph; 0778 d->m_graph = new DotGraph(); 0779 connect(d->m_graph, &DotGraph::readyToDisplay, this, &DotGraphView::displayGraph); 0780 0781 if (d->m_readWrite) { 0782 d->m_graph->setReadWrite(); 0783 } 0784 0785 // qCDebug(KGRAPHVIEWERLIB_LOG) << "Parsing " << m_graph->dotFileName() << " with " << m_graph->layoutCommand(); 0786 d->m_xMargin = 50; 0787 d->m_yMargin = 50; 0788 0789 QGraphicsScene *newCanvas = new QGraphicsScene(); 0790 QGraphicsSimpleTextItem *item = newCanvas->addSimpleText(i18n("no graph loaded")); 0791 // qCDebug(KGRAPHVIEWERLIB_LOG) << "Created canvas " << newCanvas; 0792 0793 d->m_birdEyeView->setScene(newCanvas); 0794 // std::cerr << "After m_birdEyeView set canvas" << std::endl; 0795 0796 setScene(newCanvas); 0797 d->m_canvas = newCanvas; 0798 centerOn(item); 0799 0800 d->m_cvZoom = 0; 0801 0802 return true; 0803 } 0804 0805 bool DotGraphView::slotLoadLibrary(graph_t *graph) 0806 { 0807 qCDebug(KGRAPHVIEWERLIB_LOG) << "graph_t"; 0808 Q_D(DotGraphView); 0809 d->m_birdEyeView->setScene(nullptr); 0810 0811 if (d->m_canvas) { 0812 d->m_canvas->deleteLater(); 0813 d->m_canvas = nullptr; 0814 } 0815 0816 QString layoutCommand = (d->m_graph ? d->m_graph->layoutCommand() : QString()); 0817 delete d->m_graph; 0818 0819 if (layoutCommand.isEmpty()) 0820 layoutCommand = "dot"; 0821 0822 qCDebug(KGRAPHVIEWERLIB_LOG) << "layoutCommand:" << layoutCommand; 0823 d->m_graph = new DotGraph(layoutCommand, ""); 0824 d->m_graph->setUseLibrary(true); 0825 0826 connect(d->m_graph, &DotGraph::readyToDisplay, this, &DotGraphView::displayGraph); 0827 connect(this, &DotGraphView::removeEdge, d->m_graph, &DotGraph::removeEdge); 0828 connect(this, &DotGraphView::removeNodeNamed, d->m_graph, &DotGraph::removeNodeNamed); 0829 connect(this, &DotGraphView::removeElement, d->m_graph, &DotGraph::removeElement); 0830 0831 if (d->m_readWrite) { 0832 d->m_graph->setReadWrite(); 0833 } 0834 0835 if (layoutCommand.isEmpty()) { 0836 layoutCommand = d->m_graph->chooseLayoutProgramForFile(d->m_graph->dotFileName()); 0837 } 0838 d->m_graph->layoutCommand(layoutCommand); 0839 0840 GVC_t *gvc = gvContext(); 0841 threadsafe_wrap_gvLayout(gvc, graph, layoutCommand.toUtf8().data()); 0842 threadsafe_wrap_gvRender(gvc, graph, "xdot", nullptr); 0843 0844 d->m_xMargin = 50; 0845 d->m_yMargin = 50; 0846 0847 QGraphicsScene *newCanvas = new QGraphicsScene(); 0848 qCDebug(KGRAPHVIEWERLIB_LOG) << "Created canvas " << newCanvas; 0849 0850 d->m_birdEyeView->setScene(newCanvas); 0851 // std::cerr << "After m_birdEyeView set canvas" << std::endl; 0852 0853 setScene(newCanvas); 0854 connect(newCanvas, &QGraphicsScene::selectionChanged, this, &DotGraphView::slotSelectionChanged); 0855 d->m_canvas = newCanvas; 0856 0857 d->m_cvZoom = 0; 0858 0859 d->m_graph->updateWithGraph(graph); 0860 d->m_layoutAlgoSelectAction->setCurrentAction(d->m_graph->layoutCommand(), Qt::CaseInsensitive); 0861 0862 gvFreeLayout(gvc, graph); 0863 gvFreeContext(gvc); 0864 return true; 0865 } 0866 0867 bool DotGraphView::loadDot(const QString &dotFileName) 0868 { 0869 qCDebug(KGRAPHVIEWERLIB_LOG) << "'" << dotFileName << "'"; 0870 Q_D(DotGraphView); 0871 d->m_birdEyeView->setScene(nullptr); 0872 0873 if (d->m_canvas) { 0874 d->m_canvas->deleteLater(); 0875 d->m_canvas = nullptr; 0876 } 0877 0878 QString layoutCommand = (d->m_graph ? d->m_graph->layoutCommand() : QString()); 0879 delete d->m_graph; 0880 0881 d->m_graph = new DotGraph(layoutCommand, dotFileName); 0882 connect(d->m_graph, &DotGraph::readyToDisplay, this, &DotGraphView::displayGraph); 0883 0884 if (d->m_readWrite) { 0885 d->m_graph->setReadWrite(); 0886 } 0887 if (layoutCommand.isEmpty()) { 0888 layoutCommand = d->m_graph->chooseLayoutProgramForFile(d->m_graph->dotFileName()); 0889 } 0890 d->m_graph->layoutCommand(layoutCommand); 0891 0892 // qCDebug(KGRAPHVIEWERLIB_LOG) << "Parsing " << m_graph->dotFileName() << " with " << m_graph->layoutCommand(); 0893 d->m_xMargin = 50; 0894 d->m_yMargin = 50; 0895 0896 QGraphicsScene *newCanvas = new QGraphicsScene(); 0897 qCDebug(KGRAPHVIEWERLIB_LOG) << "Created canvas " << newCanvas; 0898 0899 d->m_birdEyeView->setScene(newCanvas); 0900 // std::cerr << "After m_birdEyeView set canvas" << std::endl; 0901 0902 setScene(newCanvas); 0903 connect(newCanvas, &QGraphicsScene::selectionChanged, this, &DotGraphView::slotSelectionChanged); 0904 d->m_canvas = newCanvas; 0905 0906 QGraphicsSimpleTextItem *loadingLabel = newCanvas->addSimpleText(i18n("graph %1 is getting loaded...", dotFileName)); 0907 loadingLabel->setZValue(100); 0908 centerOn(loadingLabel); 0909 0910 d->m_cvZoom = 0; 0911 0912 if (!d->m_graph->parseDot(d->m_graph->dotFileName())) { 0913 qCWarning(KGRAPHVIEWERLIB_LOG) << "NOT successfully parsed!"; 0914 loadingLabel->setText(i18n("error parsing file %1", dotFileName)); 0915 return false; 0916 } 0917 d->m_layoutAlgoSelectAction->setCurrentAction(d->m_graph->layoutCommand(), Qt::CaseInsensitive); 0918 return true; 0919 } 0920 0921 bool DotGraphView::loadLibrarySync(const QString &dotFileName) 0922 { 0923 qCDebug(KGRAPHVIEWERLIB_LOG) << "loading sync: '" << dotFileName << "'"; 0924 Q_D(DotGraphView); 0925 if (d->m_canvas) 0926 d->m_canvas->clear(); 0927 QGraphicsSimpleTextItem *loadingLabel = d->m_canvas->addSimpleText(i18n("graph %1 is getting loaded...", dotFileName)); 0928 loadingLabel->setZValue(100); 0929 centerOn(loadingLabel); 0930 0931 // HACK: store filename in m_loadThread data structure for pick up by slotAGraphLayoutFinished() 0932 // Both methods loadLibrarySync(QString) and loadLibrary(QString) after loading the file 0933 // activate the m_layoutThread. And it is only the handler of that thread's finished signal. 0934 // slotAGraphLayoutFinished(), which then completes the loading of the file and stores the 0935 // filename with the graph data, taking it from m_loadThread. 0936 // As intermediate solution to a rewrite of the loading code, for now the filename is simply 0937 // also stored in the m_loadThread object from this code path, so the rest of the existing code 0938 // does not need to be further adapted. 0939 d->m_loadThread.setDotFileName(dotFileName); 0940 0941 qCDebug(KGRAPHVIEWERLIB_LOG) << dotFileName; 0942 FILE *fp = fopen(dotFileName.toUtf8().data(), "r"); 0943 if (!fp) { 0944 qCWarning(KGRAPHVIEWERLIB_LOG) << "Failed to open file " << dotFileName; 0945 return false; 0946 } 0947 graph_t *graph = agread(fp, nullptr); 0948 if (!graph) { 0949 qCWarning(KGRAPHVIEWERLIB_LOG) << "Failed to read file, retrying to work around graphviz bug(?)"; 0950 rewind(fp); 0951 graph = agread(fp, nullptr); 0952 } 0953 fclose(fp); 0954 if (!graph) { 0955 qCWarning(KGRAPHVIEWERLIB_LOG) << "Failed to read file " << dotFileName; 0956 return false; 0957 } 0958 0959 QString layoutCommand = (d->m_graph ? d->m_graph->layoutCommand() : QString()); 0960 if (layoutCommand.isEmpty()) { 0961 layoutCommand = d->m_graph ? d->m_graph->chooseLayoutProgramForFile(dotFileName) : "dot"; 0962 } 0963 d->m_layoutThread.layoutGraph(graph, layoutCommand); 0964 0965 return true; 0966 } 0967 0968 bool DotGraphView::loadLibrary(const QString &dotFileName) 0969 { 0970 qCDebug(KGRAPHVIEWERLIB_LOG) << "'" << dotFileName << "'"; 0971 Q_D(DotGraphView); 0972 if (d->m_canvas) 0973 d->m_canvas->clear(); 0974 QGraphicsSimpleTextItem *loadingLabel = d->m_canvas->addSimpleText(i18n("graph %1 is getting loaded...", dotFileName)); 0975 loadingLabel->setZValue(100); 0976 centerOn(loadingLabel); 0977 0978 d->m_loadThread.loadFile(dotFileName); 0979 0980 return true; 0981 } 0982 0983 bool DotGraphView::loadLibrary(graph_t *graph, const QString &layoutCommand) 0984 { 0985 qCDebug(KGRAPHVIEWERLIB_LOG) << "graph_t"; 0986 Q_D(DotGraphView); 0987 d->m_birdEyeView->setScene(nullptr); 0988 0989 if (d->m_canvas) { 0990 d->m_canvas->deleteLater(); 0991 d->m_canvas = nullptr; 0992 } 0993 0994 delete d->m_graph; 0995 d->m_graph = nullptr; 0996 0997 if (!graph) 0998 return false; 0999 1000 qCDebug(KGRAPHVIEWERLIB_LOG) << "layoutCommand:" << layoutCommand; 1001 d->m_graph = new DotGraph(layoutCommand, ""); 1002 d->m_graph->setUseLibrary(true); 1003 1004 connect(d->m_graph, &DotGraph::readyToDisplay, this, &DotGraphView::displayGraph); 1005 1006 if (d->m_readWrite) { 1007 d->m_graph->setReadWrite(); 1008 } 1009 1010 d->m_xMargin = 50; 1011 d->m_yMargin = 50; 1012 1013 QGraphicsScene *newCanvas = new QGraphicsScene(); 1014 qCDebug(KGRAPHVIEWERLIB_LOG) << "Created canvas " << newCanvas; 1015 1016 d->m_birdEyeView->setScene(newCanvas); 1017 setScene(newCanvas); 1018 connect(newCanvas, &QGraphicsScene::selectionChanged, this, &DotGraphView::slotSelectionChanged); 1019 d->m_canvas = newCanvas; 1020 1021 d->m_cvZoom = 0; 1022 1023 d->m_graph->updateWithGraph(graph); 1024 d->m_layoutAlgoSelectAction->setCurrentAction(d->m_graph->layoutCommand(), Qt::CaseInsensitive); 1025 1026 return true; 1027 } 1028 1029 void DotGraphView::slotSelectionChanged() 1030 { 1031 qCDebug(KGRAPHVIEWERLIB_LOG) << scene()->selectedItems().size(); 1032 } 1033 1034 bool DotGraphView::displayGraph() 1035 { 1036 Q_D(DotGraphView); 1037 qCDebug(KGRAPHVIEWERLIB_LOG) << d->m_graph->backColor(); 1038 // hide(); 1039 viewport()->setUpdatesEnabled(false); 1040 1041 if (d->m_graph->backColor().size() != 0) { 1042 setBackgroundColor(QColor(d->m_graph->backColor())); 1043 } 1044 1045 if (d->m_graph->nodes().size() > KGV_MAX_PANNER_NODES) { 1046 d->m_birdEyeView->setDrawingEnabled(false); 1047 } 1048 // QCanvasEllipse* eItem; 1049 double scale = d->detailAdjustedScale(); 1050 1051 qreal gh = d->m_graph->height(); 1052 1053 d->m_xMargin = 50; 1054 d->m_yMargin = 50; 1055 1056 // m_canvas->setSceneRect(0,0,w+2*m_xMargin, h+2*m_yMargin); 1057 // m_canvas->setBackgroundBrush(QBrush(QColor(m_graph->backColor()))); 1058 d->m_canvas->setBackgroundBrush(QBrush(d->m_backgroundColor)); 1059 1060 // qCDebug(KGRAPHVIEWERLIB_LOG) << "sceneRect is now " << m_canvas->sceneRect(); 1061 1062 qCDebug(KGRAPHVIEWERLIB_LOG) << "Creating" << d->m_graph->subgraphs().size() << "CanvasSubgraphs from" << d->m_graph; 1063 int zvalue = -1; 1064 for (GraphSubgraph *gsubgraph : d->m_graph->subgraphs()) { 1065 int newZvalue = d->displaySubgraph(gsubgraph, zvalue); 1066 if (newZvalue > zvalue) 1067 zvalue = newZvalue; 1068 } 1069 1070 qCDebug(KGRAPHVIEWERLIB_LOG) << "Creating" << d->m_graph->nodes().size() << "nodes from" << d->m_graph; 1071 GraphNodeMap::const_iterator it = d->m_graph->nodes().constBegin(); 1072 for (; it != d->m_graph->nodes().constEnd(); it++) { 1073 const QString &id = it.key(); 1074 GraphNode *gnode = it.value(); 1075 qCDebug(KGRAPHVIEWERLIB_LOG) << "Handling" << id << (void *)gnode; 1076 qCDebug(KGRAPHVIEWERLIB_LOG) << " gnode id=" << gnode->id(); 1077 qCDebug(KGRAPHVIEWERLIB_LOG) << " canvasNode=" << (void *)gnode->canvasNode(); 1078 if (gnode->canvasNode() == nullptr) { 1079 qCDebug(KGRAPHVIEWERLIB_LOG) << "Creating canvas node for" << gnode->id(); 1080 CanvasNode *cnode = new CanvasNode(this, gnode, d->m_canvas); 1081 if (cnode == nullptr) 1082 continue; 1083 cnode->initialize(scale, scale, d->m_xMargin, d->m_yMargin, gh); 1084 gnode->setCanvasNode(cnode); 1085 d->m_canvas->addItem(cnode); 1086 // cnode->setZValue(gnode->z()); 1087 cnode->setZValue(zvalue + 1); 1088 cnode->show(); 1089 } 1090 gnode->canvasNode()->computeBoundingRect(); 1091 } 1092 1093 qCDebug(KGRAPHVIEWERLIB_LOG) << "Creating" << d->m_graph->edges().size() << "edges from" << d->m_graph; 1094 for (GraphEdge *gedge : d->m_graph->edges()) { 1095 qCDebug(KGRAPHVIEWERLIB_LOG) << "One GraphEdge:" << gedge->id(); 1096 if (gedge->canvasEdge() == nullptr && gedge->fromNode() && gedge->toNode()) { 1097 qCDebug(KGRAPHVIEWERLIB_LOG) << "New CanvasEdge for" << gedge->id(); 1098 qCDebug(KGRAPHVIEWERLIB_LOG) << "edge fromNode=" << (void *)gedge->fromNode(); 1099 qCDebug(KGRAPHVIEWERLIB_LOG) << " " << gedge->fromNode()->id(); 1100 qCDebug(KGRAPHVIEWERLIB_LOG) << "edge toNode=" << (void *)gedge->toNode(); 1101 qCDebug(KGRAPHVIEWERLIB_LOG) << " " << gedge->toNode()->id(); 1102 CanvasEdge *cedge = new CanvasEdge(this, gedge, scale, scale, d->m_xMargin, d->m_yMargin, gh, d->m_graph->wdhcf(), d->m_graph->hdvcf()); 1103 1104 gedge->setCanvasEdge(cedge); 1105 // std::cerr << "setting z = " << gedge->z() << std::endl; 1106 // cedge->setZValue(gedge->z()); 1107 cedge->setZValue(zvalue + 2); 1108 cedge->show(); 1109 d->m_canvas->addItem(cedge); 1110 } 1111 if (gedge->canvasEdge()) 1112 gedge->canvasEdge()->computeBoundingRect(); 1113 } 1114 qCDebug(KGRAPHVIEWERLIB_LOG) << "Adding graph render operations: " << d->m_graph->renderOperations().size(); 1115 for (const DotRenderOp &dro : d->m_graph->renderOperations()) { 1116 if (dro.renderop == "T") { 1117 // std::cerr << "Adding graph label '"<<dro.str<<"'" << std::endl; 1118 const QString &str = dro.str; 1119 int stringWidthGoal = int(dro.integers[3] * scale); 1120 int fontSize = d->m_graph->fontSize(); 1121 QFont *font = FontsCache::changeable().fromName(d->m_graph->fontName()); 1122 font->setPointSize(fontSize); 1123 QFontMetrics fm(*font); 1124 while (fm.horizontalAdvance(str) > stringWidthGoal && fontSize > 1) { 1125 fontSize--; 1126 font->setPointSize(fontSize); 1127 fm = QFontMetrics(*font); 1128 } 1129 QGraphicsSimpleTextItem *labelView = new QGraphicsSimpleTextItem(str, d->m_canvas->activePanel()); 1130 labelView->setFont(*font); 1131 labelView->setPos((scale * ((dro.integers[0]) + (((dro.integers[2]) * (dro.integers[3])) / 2) - ((dro.integers[3]) / 2)) + d->m_xMargin), ((gh - (dro.integers[1])) * scale) + d->m_yMargin); 1132 /// @todo port that ; how to set text color ? 1133 labelView->setPen(QPen(Dot2QtConsts::componentData().qtColor(d->m_graph->fontColor()))); 1134 labelView->setFont(*font); 1135 d->m_labelViews.insert(labelView); 1136 } 1137 } 1138 1139 qCDebug(KGRAPHVIEWERLIB_LOG) << "Finalizing"; 1140 d->m_cvZoom = 0; 1141 d->updateSizes(); 1142 1143 centerOn(d->m_canvas->sceneRect().center()); 1144 1145 viewport()->setUpdatesEnabled(true); 1146 QSet<QGraphicsSimpleTextItem *>::iterator labelViewsIt, labelViewsIt_end; 1147 labelViewsIt = d->m_labelViews.begin(); 1148 labelViewsIt_end = d->m_labelViews.end(); 1149 for (; labelViewsIt != labelViewsIt_end; labelViewsIt++) { 1150 (*labelViewsIt)->show(); 1151 } 1152 d->m_canvas->update(); 1153 1154 emit graphLoaded(); 1155 1156 return true; 1157 } 1158 1159 void DotGraphView::focusInEvent(QFocusEvent *) 1160 { 1161 Q_D(DotGraphView); 1162 if (!d->m_canvas) 1163 return; 1164 1165 // m_canvas->update(); 1166 } 1167 1168 void DotGraphView::focusOutEvent(QFocusEvent *e) 1169 { 1170 // trigger updates as in focusInEvent 1171 focusInEvent(e); 1172 } 1173 1174 void DotGraphView::scrollViewPercent(bool horizontal, int percent) 1175 { 1176 QScrollBar *scrollbar = horizontal ? horizontalScrollBar() : verticalScrollBar(); 1177 int amount = horizontal ? viewport()->width() : viewport()->height(); 1178 amount = amount * percent / 100; 1179 scrollbar->setValue(scrollbar->value() + amount); 1180 } 1181 1182 void DotGraphView::keyPressEvent(QKeyEvent *e) 1183 { 1184 Q_D(DotGraphView); 1185 if (!d->m_canvas) { 1186 e->ignore(); 1187 return; 1188 } 1189 1190 // move canvas... 1191 if (e->key() == Qt::Key_Home) 1192 verticalScrollBar()->setValue(verticalScrollBar()->minimum()); 1193 else if (e->key() == Qt::Key_End) 1194 verticalScrollBar()->setValue(verticalScrollBar()->maximum()); 1195 else if (e->key() == Qt::Key_PageUp) 1196 scrollViewPercent(false, -50); 1197 else if (e->key() == Qt::Key_PageDown) 1198 scrollViewPercent(false, 50); 1199 else if (e->key() == Qt::Key_Left) 1200 scrollViewPercent(true, -10); 1201 else if (e->key() == Qt::Key_Right) 1202 scrollViewPercent(true, 10); 1203 else if (e->key() == Qt::Key_Down) 1204 scrollViewPercent(false, 10); 1205 else if (e->key() == Qt::Key_Up) 1206 scrollViewPercent(false, -10); 1207 else { 1208 e->ignore(); 1209 return; 1210 } 1211 } 1212 1213 void DotGraphView::wheelEvent(QWheelEvent *e) 1214 { 1215 Q_D(DotGraphView); 1216 if (!d->m_canvas) { 1217 e->ignore(); 1218 return; 1219 } 1220 e->accept(); 1221 if (QApplication::keyboardModifiers() == Qt::ShiftModifier || QApplication::keyboardModifiers() == Qt::ControlModifier) { 1222 qCDebug(KGRAPHVIEWERLIB_LOG) << " + Shift/Ctrl: zooming"; 1223 // move canvas... 1224 if (e->delta() < 0) { 1225 zoomOut(); 1226 } else { 1227 zoomIn(); 1228 } 1229 } else { 1230 qCDebug(KGRAPHVIEWERLIB_LOG) << " : scrolling "; 1231 scrollViewPercent(e->orientation() == Qt::Horizontal, e->delta() < 0 ? 10 : -10); 1232 } 1233 } 1234 1235 void DotGraphView::zoomIn() 1236 { 1237 applyZoom(1.10); 1238 } 1239 1240 void DotGraphView::zoomOut() 1241 { 1242 applyZoom(.90); 1243 } 1244 1245 void DotGraphView::setZoomFactor(double newZoom) 1246 { 1247 Q_D(DotGraphView); 1248 if (newZoom < 0.1 || newZoom > 10) 1249 return; 1250 d->m_zoom = newZoom; 1251 if (d->m_zoom > 1.0 && d->m_zoom < 1.1) { 1252 d->m_zoom = 1; 1253 } 1254 1255 setUpdatesEnabled(false); 1256 QTransform m; 1257 m.scale(d->m_zoom, d->m_zoom); 1258 setTransform(m); 1259 emit zoomed(d->m_zoom); 1260 setUpdatesEnabled(true); 1261 d->updateSizes(); 1262 } 1263 1264 void DotGraphView::applyZoom(double factor) 1265 { 1266 Q_D(DotGraphView); 1267 setZoomFactor(d->m_zoom * factor); 1268 } 1269 1270 void DotGraphView::scrollContentsBy(int dx, int dy) 1271 { 1272 Q_D(DotGraphView); 1273 QGraphicsView::scrollContentsBy(dx, dy); 1274 if (d->m_birdEyeView && scene()) { // we might be shutting down 1275 d->m_birdEyeView->moveZoomRectTo(mapToScene(viewport()->rect()).boundingRect().center(), false); 1276 } 1277 } 1278 1279 void DotGraphView::resizeEvent(QResizeEvent *e) 1280 { 1281 Q_D(DotGraphView); 1282 qCDebug(KGRAPHVIEWERLIB_LOG) << "resizeEvent"; 1283 QGraphicsView::resizeEvent(e); 1284 if (d->m_canvas) 1285 d->updateSizes(e->size()); 1286 // std::cerr << "resizeEvent end" << std::endl; 1287 } 1288 1289 void DotGraphView::zoomRectMovedTo(QPointF newZoomPos) 1290 { 1291 // qCDebug(KGRAPHVIEWERLIB_LOG) << "DotGraphView::zoomRectMovedTo " << newZoomPos; 1292 centerOn(newZoomPos); 1293 } 1294 1295 void DotGraphView::zoomRectMoveFinished() 1296 { 1297 Q_D(DotGraphView); 1298 // qCDebug(KGRAPHVIEWERLIB_LOG) << "zoomRectMoveFinished"; 1299 d->updateBirdEyeView(); 1300 // std::cerr << "zoomRectMoveFinished end" << std::endl; 1301 } 1302 1303 void DotGraphView::mousePressEvent(QMouseEvent *e) 1304 { 1305 Q_D(DotGraphView); 1306 if (e->button() != Qt::LeftButton) { 1307 return; 1308 } 1309 qCDebug(KGRAPHVIEWERLIB_LOG) << e << d->m_editingMode; 1310 QGraphicsView::mousePressEvent(e); 1311 1312 if (d->m_editingMode == AddNewElement) { 1313 double scale = d->detailAdjustedScale(); 1314 1315 qreal gh = d->m_graph->height(); 1316 1317 QPointF pos = mapToScene(e->pos().x() - d->m_defaultNewElementPixmap.width() / 2, e->pos().y() - d->m_defaultNewElementPixmap.height() / 2); 1318 GraphNode *newNode = new GraphNode(); 1319 newNode->attributes() = d->m_newElementAttributes; 1320 if (newNode->attributes().find("id") == newNode->attributes().end()) { 1321 newNode->setId(QString("NewNode%1").arg(d->m_graph->nodes().size())); 1322 } 1323 if (newNode->attributes().find("label") == newNode->attributes().end()) { 1324 newNode->setLabel(newNode->id()); 1325 } 1326 d->m_graph->nodes().insert(newNode->id(), newNode); 1327 CanvasNode *newCNode = new CanvasNode(this, newNode, d->m_canvas); 1328 newCNode->initialize(scale, scale, d->m_xMargin, d->m_yMargin, gh); 1329 newNode->setCanvasNode(newCNode); 1330 scene()->addItem(newCNode); 1331 qCDebug(KGRAPHVIEWERLIB_LOG) << "setting pos to " << pos; 1332 newCNode->setPos(pos); 1333 newCNode->setZValue(100); 1334 newCNode->show(); 1335 1336 d->m_editingMode = None; 1337 unsetCursor(); 1338 emit newNodeAdded(newNode->id()); 1339 } else if (d->m_editingMode == SelectingElements) { 1340 } else { 1341 if (d->m_editingMode != None && itemAt(e->pos()) == nullptr) // click outside any item: unselect all 1342 { 1343 if (d->m_editingMode == DrawNewEdge) // was drawing an edge; cancel it 1344 { 1345 if (d->m_newEdgeDraft) { 1346 d->m_newEdgeDraft->hide(); 1347 scene()->removeItem(d->m_newEdgeDraft); 1348 delete d->m_newEdgeDraft; 1349 d->m_newEdgeDraft = nullptr; 1350 } 1351 d->m_newEdgeSource = nullptr; 1352 d->m_editingMode = None; 1353 } else if (d->m_editingMode == AddNewEdge) { 1354 d->m_editingMode = None; 1355 } 1356 for (GraphEdge *e : d->m_graph->edges()) { 1357 if (e->isSelected()) { 1358 e->setSelected(false); 1359 e->canvasEdge()->update(); 1360 } 1361 } 1362 for (GraphNode *n : d->m_graph->nodes()) { 1363 if (n->isSelected()) { 1364 n->setSelected(false); 1365 n->canvasElement()->update(); 1366 } 1367 } 1368 for (GraphSubgraph *s : d->m_graph->subgraphs()) { 1369 if (s->isSelected()) { 1370 s->setSelected(false); 1371 s->canvasElement()->update(); 1372 } 1373 } 1374 emit selectionIs(QList<QString>(), QPoint()); 1375 } 1376 d->m_pressPos = e->globalPos(); 1377 d->m_pressScrollBarsPos = QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); 1378 } 1379 d->m_isMoving = true; 1380 } 1381 1382 void DotGraphView::mouseMoveEvent(QMouseEvent *e) 1383 { 1384 Q_D(DotGraphView); 1385 QGraphicsView::mouseMoveEvent(e); 1386 // qCDebug(KGRAPHVIEWERLIB_LOG) << scene()->selectedItems().size(); 1387 1388 if (d->m_editingMode == DrawNewEdge) { 1389 if (d->m_newEdgeDraft) { 1390 QPointF src = d->m_newEdgeDraft->line().p1(); 1391 QPointF tgt = mapToScene(e->pos()); 1392 1393 // qCDebug(KGRAPHVIEWERLIB_LOG) << "Setting new edge draft line to" << QLineF(src,tgt); 1394 d->m_newEdgeDraft->setLine(QLineF(src, tgt)); 1395 } 1396 } else if (d->m_editingMode == SelectingElements) { 1397 // qCDebug(KGRAPHVIEWERLIB_LOG) << "selecting"; 1398 } else if (e->buttons().testFlag(Qt::LeftButton)) { 1399 // qCDebug(KGRAPHVIEWERLIB_LOG) << (e->globalPos() - d->m_pressPos); 1400 QPoint diff = e->globalPos() - d->m_pressPos; 1401 horizontalScrollBar()->setValue(d->m_pressScrollBarsPos.x() - diff.x()); 1402 verticalScrollBar()->setValue(d->m_pressScrollBarsPos.y() - diff.y()); 1403 } 1404 } 1405 1406 void DotGraphView::mouseReleaseEvent(QMouseEvent *e) 1407 { 1408 Q_D(DotGraphView); 1409 qCDebug(KGRAPHVIEWERLIB_LOG) << e << d->m_editingMode; 1410 // qCDebug(KGRAPHVIEWERLIB_LOG) << "setDragMode(NoDrag)"; 1411 // setDragMode(NoDrag); 1412 if (d->m_editingMode == AddNewElement) { 1413 d->m_editingMode = None; 1414 unsetCursor(); 1415 } else if (d->m_editingMode == SelectingElements) { 1416 QGraphicsView::mouseReleaseEvent(e); 1417 qCDebug(KGRAPHVIEWERLIB_LOG) << "Stopping selection" << scene() << d->m_canvas; 1418 QList<QGraphicsItem *> items = scene()->selectedItems(); 1419 QList<QString> selection; 1420 for (QGraphicsItem *item : items) { 1421 CanvasElement *element = dynamic_cast<CanvasElement *>(item); 1422 element->element()->setSelected(true); 1423 if (element) { 1424 selection.push_back(element->element()->id()); 1425 } 1426 } 1427 d->m_editingMode = None; 1428 unsetCursor(); 1429 setDragMode(NoDrag); 1430 if (!selection.isEmpty()) { 1431 update(); 1432 emit selectionIs(selection, mapToGlobal(e->pos())); 1433 } 1434 } else { 1435 QGraphicsView::mouseReleaseEvent(e); 1436 } 1437 d->m_isMoving = false; 1438 } 1439 1440 void DotGraphView::mouseDoubleClickEvent(QMouseEvent *e) 1441 { 1442 QGraphicsView::mouseDoubleClickEvent(e); 1443 } 1444 1445 void DotGraphView::contextMenuEvent(QContextMenuEvent *e) 1446 { 1447 Q_D(DotGraphView); 1448 // QList<QGraphicsItem *> l = scene()->collidingItems(scene()->itemAt(e->pos())); 1449 1450 d->m_popup->exec(e->globalPos()); 1451 } 1452 1453 void DotGraphView::slotContextMenuEvent(const QString &id, const QPoint &p) 1454 { 1455 // QList<QGraphicsItem *> l = scene()->collidingItems(scene()->itemAt(e->pos())); 1456 1457 emit(contextMenuEvent(id, p)); 1458 } 1459 1460 void DotGraphView::slotElementHoverEnter(CanvasElement *element) 1461 { 1462 qCDebug(KGRAPHVIEWERLIB_LOG) << element->element()->id(); 1463 // QList<QGraphicsItem *> l = scene()->collidingItems(scene()->itemAt(e->pos())); 1464 1465 emit(hoverEnter(element->element()->id())); 1466 } 1467 1468 void DotGraphView::slotElementHoverLeave(CanvasElement *element) 1469 { 1470 qCDebug(KGRAPHVIEWERLIB_LOG) << element->element()->id(); 1471 // QList<QGraphicsItem *> l = scene()->collidingItems(scene()->itemAt(e->pos())); 1472 1473 emit(hoverLeave(element->element()->id())); 1474 } 1475 1476 void DotGraphView::slotElementHoverEnter(CanvasEdge *element) 1477 { 1478 qCDebug(KGRAPHVIEWERLIB_LOG) << element->edge()->id(); 1479 // QList<QGraphicsItem *> l = scene()->collidingItems(scene()->itemAt(e->pos())); 1480 1481 emit(hoverEnter(element->edge()->id())); 1482 } 1483 1484 void DotGraphView::slotElementHoverLeave(CanvasEdge *element) 1485 { 1486 qCDebug(KGRAPHVIEWERLIB_LOG) << element->edge()->id(); 1487 // QList<QGraphicsItem *> l = scene()->collidingItems(scene()->itemAt(e->pos())); 1488 1489 emit(hoverLeave(element->edge()->id())); 1490 } 1491 1492 void DotGraphView::setLayoutCommand(const QString &command) 1493 { 1494 Q_D(DotGraphView); 1495 d->m_graph->layoutCommand(command); 1496 reload(); 1497 } 1498 1499 KGraphViewerInterface::PannerPosition DotGraphView::zoomPos(const QString &s) 1500 { 1501 KGraphViewerInterface::PannerPosition res = DEFAULT_ZOOMPOS; 1502 if (s == QString("KGraphViewerInterface::TopLeft")) 1503 res = KGraphViewerInterface::TopLeft; 1504 if (s == QString("KGraphViewerInterface::TopRight")) 1505 res = KGraphViewerInterface::TopRight; 1506 if (s == QString("KGraphViewerInterface::BottomLeft")) 1507 res = KGraphViewerInterface::BottomLeft; 1508 if (s == QString("KGraphViewerInterface::BottomRight")) 1509 res = KGraphViewerInterface::BottomRight; 1510 if (s == QString("Automatic")) 1511 res = KGraphViewerInterface::Auto; 1512 1513 return res; 1514 } 1515 1516 void DotGraphView::setPannerEnabled(bool enabled) 1517 { 1518 Q_UNUSED(enabled); 1519 Q_D(DotGraphView); 1520 d->m_bevPopup->setEnabled(d->m_bevEnabledAction->isChecked()); 1521 KGraphViewerPartSettings::setBirdsEyeViewEnabled(d->m_bevEnabledAction->isChecked()); 1522 KGraphViewerPartSettings::self()->save(); 1523 d->updateSizes(); 1524 } 1525 1526 void DotGraphView::viewBevActivated(int newZoomPos) 1527 { 1528 Q_D(DotGraphView); 1529 d->m_zoomPosition = (KGraphViewerInterface::PannerPosition)newZoomPos; 1530 d->updateSizes(); 1531 emit(sigViewBevActivated(newZoomPos)); 1532 } 1533 1534 QString DotGraphView::zoomPosString(KGraphViewerInterface::PannerPosition p) 1535 { 1536 if (p == KGraphViewerInterface::TopRight) 1537 return QString("KGraphViewerInterface::TopRight"); 1538 if (p == KGraphViewerInterface::BottomLeft) 1539 return QString("KGraphViewerInterface::BottomLeft"); 1540 if (p == KGraphViewerInterface::BottomRight) 1541 return QString("KGraphViewerInterface::BottomRight"); 1542 if (p == KGraphViewerInterface::Auto) 1543 return QString("Automatic"); 1544 1545 return QString("KGraphViewerInterface::TopLeft"); 1546 } 1547 1548 void DotGraphView::readViewConfig() 1549 { 1550 Q_D(DotGraphView); 1551 KConfigGroup g(KSharedConfig::openConfig(), "GraphViewLayout"); 1552 1553 QVariant dl = DEFAULT_DETAILLEVEL; 1554 d->m_detailLevel = g.readEntry("DetailLevel", dl).toInt(); 1555 d->m_zoomPosition = zoomPos(g.readEntry("KGraphViewerInterface::PannerPosition", zoomPosString(DEFAULT_ZOOMPOS))); 1556 emit(sigViewBevActivated(d->m_zoomPosition)); 1557 } 1558 1559 void DotGraphView::saveViewConfig() 1560 { 1561 Q_D(DotGraphView); 1562 // qCDebug(KGRAPHVIEWERLIB_LOG) << "Saving view config"; 1563 KConfigGroup g(KSharedConfig::openConfig(), "GraphViewLayout"); 1564 1565 writeConfigEntry(&g, "DetailLevel", d->m_detailLevel, DEFAULT_DETAILLEVEL); 1566 writeConfigEntry(&g, "KGraphViewerInterface::PannerPosition", zoomPosString(d->m_zoomPosition), zoomPosString(DEFAULT_ZOOMPOS).toUtf8().data()); 1567 g.sync(); 1568 } 1569 1570 void DotGraphView::pageSetup() 1571 { 1572 Q_D(DotGraphView); 1573 if (d->m_printCommand == nullptr) { 1574 d->m_printCommand = new KGVSimplePrintingCommand(this, 0); 1575 } 1576 d->m_printCommand->showPageSetup(d->m_graph->dotFileName()); 1577 return; 1578 } 1579 1580 void DotGraphView::print() 1581 { 1582 Q_D(DotGraphView); 1583 if (d->m_printCommand == nullptr) { 1584 d->m_printCommand = new KGVSimplePrintingCommand(this, 0); 1585 } 1586 d->m_printCommand->print(d->m_graph->dotFileName()); 1587 return; 1588 } 1589 1590 void DotGraphView::printPreview() 1591 { 1592 Q_D(DotGraphView); 1593 if (d->m_printCommand == nullptr) { 1594 d->m_printCommand = new KGVSimplePrintingCommand(this, 0); 1595 } 1596 d->m_printCommand->showPrintPreview(d->m_graph->dotFileName(), false); 1597 return; 1598 } 1599 1600 bool DotGraphView::reload() 1601 { 1602 Q_D(DotGraphView); 1603 QString fileName = d->m_graph->dotFileName(); 1604 if (d->m_graph->useLibrary()) 1605 return loadLibrary(fileName); 1606 else 1607 return loadDot(fileName); 1608 } 1609 1610 void DotGraphView::dirty(const QString &dotFileName) 1611 { 1612 Q_D(DotGraphView); 1613 // std::cerr << "SLOT dirty for " << dotFileName << std::endl; 1614 if (dotFileName == d->m_graph->dotFileName()) { 1615 if (QMessageBox::question(this, i18n("Reload Confirmation"), i18n("The file %1 has been modified on disk.\nDo you want to reload it?", dotFileName)) == QMessageBox::Yes) { 1616 if (d->m_graph->useLibrary()) 1617 loadLibrary(dotFileName); 1618 else 1619 loadDot(dotFileName); 1620 } 1621 } 1622 } 1623 1624 KConfigGroup *DotGraphView::configGroup(KConfig *c, const QString &group, const QString &post) 1625 { 1626 QStringList gList = c->groupList(); 1627 QString res = group; 1628 if (gList.contains(group + post)) 1629 res += post; 1630 return new KConfigGroup(c, res); 1631 } 1632 1633 void DotGraphView::writeConfigEntry(KConfigGroup *c, const char *pKey, const QString &value, const char *def) 1634 { 1635 if (!c) 1636 return; 1637 if ((value.isEmpty() && ((def == nullptr) || (*def == 0))) || (value == QString(def))) 1638 c->deleteEntry(pKey); 1639 else 1640 c->writeEntry(pKey, value); 1641 } 1642 1643 void DotGraphView::writeConfigEntry(KConfigGroup *c, const char *pKey, int value, int def) 1644 { 1645 if (!c) 1646 return; 1647 if (value == def) 1648 c->deleteEntry(pKey); 1649 else 1650 c->writeEntry(pKey, value); 1651 } 1652 1653 void DotGraphView::writeConfigEntry(KConfigGroup *c, const char *pKey, double value, double def) 1654 { 1655 if (!c) 1656 return; 1657 if (value == def) 1658 c->deleteEntry(pKey); 1659 else 1660 c->writeEntry(pKey, value); 1661 } 1662 1663 void DotGraphView::writeConfigEntry(KConfigGroup *c, const char *pKey, bool value, bool def) 1664 { 1665 if (!c) 1666 return; 1667 if (value == def) 1668 c->deleteEntry(pKey); 1669 else 1670 c->writeEntry(pKey, value); 1671 } 1672 1673 const QString &DotGraphView::dotFileName() 1674 { 1675 Q_D(DotGraphView); 1676 return d->m_graph->dotFileName(); 1677 } 1678 1679 void DotGraphView::hideToolsWindows() 1680 { 1681 Q_D(DotGraphView); 1682 if (d->m_printCommand) { 1683 d->m_printCommand->hidePageSetup(); 1684 d->m_printCommand->hidePrintPreview(); 1685 } 1686 } 1687 1688 void DotGraphView::slotExportImage() 1689 { 1690 Q_D(DotGraphView); 1691 d->exportToImage(); 1692 } 1693 1694 void DotGraphView::slotLayoutSpecify() 1695 { 1696 { 1697 Q_D(DotGraphView); 1698 bool ok = false; 1699 QString currentLayoutCommand = d->m_graph->layoutCommand(); 1700 QString layoutCommand = QInputDialog::getText(this, 1701 i18n("Layout Command"), 1702 i18n("Specify here the command that will be used to layout the graph.\n" 1703 "The command MUST write its results on stdout in xdot format."), 1704 QLineEdit::Normal, 1705 currentLayoutCommand, 1706 &ok); 1707 // std::cerr << "Got layout command: " << layoutCommand << std::endl; 1708 if (ok && layoutCommand != currentLayoutCommand) { 1709 // std::cerr << "Setting new layout command: " << layoutCommand << std::endl; 1710 if (!d->m_layoutAlgoSelectAction->setCurrentAction(layoutCommand, Qt::CaseInsensitive)) { 1711 QAction *new_action = d->m_layoutAlgoSelectAction->addAction(layoutCommand); 1712 d->m_layoutAlgoSelectAction->setCurrentAction(new_action); 1713 slotSelectLayoutAlgo(layoutCommand); 1714 } 1715 } 1716 } 1717 } 1718 1719 void DotGraphView::slotLayoutReset() 1720 { 1721 Q_D(DotGraphView); 1722 d->m_layoutAlgoSelectAction->setCurrentAction("Dot"); 1723 slotSelectLayoutAlgo("Dot"); 1724 } 1725 1726 void DotGraphView::slotSelectLayoutAlgo(const QString &ttext) 1727 { 1728 QString text = ttext; //.mid(1); 1729 qCDebug(KGRAPHVIEWERLIB_LOG) << "DotGraphView::slotSelectLayoutAlgo '" << text << "'"; 1730 if (text == "Dot") { 1731 setLayoutCommand("dot"); 1732 } else if (text == "Neato") { 1733 setLayoutCommand("neato"); 1734 } else if (text == "Twopi") { 1735 setLayoutCommand("twopi"); 1736 } else if (text == "Fdp") { 1737 setLayoutCommand("fdp"); 1738 } else if (text == "Circo") { 1739 setLayoutCommand("circo"); 1740 } else { 1741 setLayoutCommand(text); 1742 } 1743 } 1744 1745 void DotGraphView::slotSelectLayoutDot() 1746 { 1747 qCDebug(KGRAPHVIEWERLIB_LOG) << "DotGraphView::slotSelectLayoutDot"; 1748 setLayoutCommand("dot -Txdot"); 1749 } 1750 1751 void DotGraphView::slotSelectLayoutNeato() 1752 { 1753 qCDebug(KGRAPHVIEWERLIB_LOG) << "DotGraphView::slotSelectLayoutNeato"; 1754 setLayoutCommand("neato -Txdot"); 1755 } 1756 1757 void DotGraphView::slotSelectLayoutTwopi() 1758 { 1759 qCDebug(KGRAPHVIEWERLIB_LOG) << "DotGraphView::slotSelectLayoutTwopi"; 1760 setLayoutCommand("twopi -Txdot"); 1761 } 1762 1763 void DotGraphView::slotSelectLayoutFdp() 1764 { 1765 qCDebug(KGRAPHVIEWERLIB_LOG) << "DotGraphView::slotSelectLayoutFdp"; 1766 setLayoutCommand("fdp -Txdot"); 1767 } 1768 1769 void DotGraphView::slotSelectLayoutCirco() 1770 { 1771 qCDebug(KGRAPHVIEWERLIB_LOG) << "DotGraphView::slotSelectLayoutCirco"; 1772 setLayoutCommand("circo -Txdot"); 1773 } 1774 1775 void DotGraphView::slotBevToggled() 1776 { 1777 Q_D(DotGraphView); 1778 qCDebug(KGRAPHVIEWERLIB_LOG) << "DotGraphView::slotBevToggled"; 1779 qCDebug(KGRAPHVIEWERLIB_LOG) << " d->m_bevEnabledAction is checked ? " << d->m_bevEnabledAction->isChecked(); 1780 setPannerEnabled(d->m_bevEnabledAction->isChecked()); 1781 } 1782 1783 void DotGraphView::slotBevTopLeft() 1784 { 1785 viewBevActivated(KGraphViewerInterface::TopLeft); 1786 } 1787 1788 void DotGraphView::slotBevTopRight() 1789 { 1790 viewBevActivated(KGraphViewerInterface::TopRight); 1791 } 1792 1793 void DotGraphView::slotBevBottomLeft() 1794 { 1795 viewBevActivated(KGraphViewerInterface::BottomLeft); 1796 } 1797 1798 void DotGraphView::slotBevBottomRight() 1799 { 1800 viewBevActivated(KGraphViewerInterface::BottomRight); 1801 } 1802 1803 void DotGraphView::slotBevAutomatic() 1804 { 1805 viewBevActivated(KGraphViewerInterface::Auto); 1806 } 1807 1808 void DotGraphView::slotUpdate() 1809 { 1810 Q_D(DotGraphView); 1811 d->m_graph->update(); 1812 d->m_layoutAlgoSelectAction->setCurrentAction(d->m_graph->layoutCommand(), Qt::CaseInsensitive); 1813 } 1814 1815 void DotGraphView::prepareAddNewElement(QMap<QString, QString> attribs) 1816 { 1817 Q_D(DotGraphView); 1818 d->m_editingMode = AddNewElement; 1819 d->m_newElementAttributes = attribs; 1820 unsetCursor(); 1821 setCursor(QCursor(d->m_defaultNewElementPixmap)); 1822 } 1823 1824 void DotGraphView::prepareAddNewEdge(QMap<QString, QString> attribs) 1825 { 1826 Q_D(DotGraphView); 1827 qCDebug(KGRAPHVIEWERLIB_LOG) << attribs; 1828 bool anySelected = false; 1829 for (GraphEdge *edge : d->m_graph->edges()) { 1830 if (edge->isSelected()) { 1831 anySelected = true; 1832 QMap<QString, QString>::const_iterator it = attribs.constBegin(); 1833 for (; it != attribs.constEnd(); it++) { 1834 edge->attributes()[it.key()] = it.value(); 1835 } 1836 } 1837 } 1838 if (anySelected) { 1839 return; 1840 } 1841 d->m_editingMode = AddNewEdge; 1842 d->m_newElementAttributes = attribs; 1843 unsetCursor(); 1844 QBitmap bm(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kgraphviewerpart/pics/kgraphviewer-newedge.png")); 1845 setCursor(QCursor(bm, bm, 32, 16)); 1846 } 1847 1848 void DotGraphView::prepareSelectElements() 1849 { 1850 Q_D(DotGraphView); 1851 d->m_editingMode = SelectingElements; 1852 setCursor(Qt::CrossCursor); 1853 setDragMode(RubberBandDrag); 1854 } 1855 1856 void DotGraphView::createNewEdgeDraftFrom(CanvasElement *node) 1857 { 1858 Q_D(DotGraphView); 1859 qCDebug(KGRAPHVIEWERLIB_LOG) << node->element()->id(); 1860 d->m_editingMode = DrawNewEdge; 1861 unsetCursor(); 1862 d->m_newEdgeSource = node; 1863 1864 if (d->m_newEdgeDraft) { 1865 qCDebug(KGRAPHVIEWERLIB_LOG) << "removing new edge draft"; 1866 d->m_newEdgeDraft->hide(); 1867 scene()->removeItem(d->m_newEdgeDraft); 1868 delete d->m_newEdgeDraft; 1869 d->m_newEdgeDraft = nullptr; 1870 } 1871 d->m_newEdgeDraft = new QGraphicsLineItem(QLineF(node->boundingRect().center() + node->pos(), node->boundingRect().center() + node->pos() + QPointF(10, 10))); 1872 scene()->addItem(d->m_newEdgeDraft); 1873 d->m_newEdgeDraft->setZValue(1000); 1874 d->m_newEdgeDraft->show(); 1875 qCDebug(KGRAPHVIEWERLIB_LOG) << d->m_newEdgeDraft->line(); 1876 } 1877 1878 void DotGraphView::finishNewEdgeTo(CanvasElement *node) 1879 { 1880 Q_D(DotGraphView); 1881 qCDebug(KGRAPHVIEWERLIB_LOG) << node->element()->id(); 1882 d->m_editingMode = None; 1883 unsetCursor(); 1884 1885 if (d->m_newEdgeDraft) { 1886 qCDebug(KGRAPHVIEWERLIB_LOG) << "removing new edge draft"; 1887 d->m_newEdgeDraft->hide(); 1888 scene()->removeItem(d->m_newEdgeDraft); 1889 delete d->m_newEdgeDraft; 1890 d->m_newEdgeDraft = nullptr; 1891 } 1892 1893 emit newEdgeFinished(d->m_newEdgeSource->element()->id(), node->element()->id(), d->m_newElementAttributes); 1894 1895 d->m_newEdgeSource = nullptr; 1896 } 1897 1898 // void DotGraphView::slotFinishNewEdge( 1899 // const QString& srcId, 1900 // const QString& tgtId, 1901 // const QMap<QString, QString> newElementAttributes) 1902 // { 1903 // qCDebug(KGRAPHVIEWERLIB_LOG) ; 1904 // 1905 // GraphEdge* gedge = new GraphEdge(); 1906 // gedge->setFromNode(d->m_graph->nodes()[srcId]); 1907 // gedge->setToNode(d->m_graph->nodes()[tgtId]); 1908 // gedge->attributes() = newElementAttributes; 1909 // foreach (const QString &attrib, newElementAttributes.keys()) 1910 // { 1911 // if (attrib == "z") 1912 // { 1913 // bool ok; 1914 // gedge->setZ(newElementAttributes[attrib].toDouble(&ok)); 1915 // } 1916 // } 1917 // gedge->setId(srcId+tgtId+QString::number(d->m_graph->edges().size())); 1918 // d->m_graph->edges().insert(gedge->id(), gedge); 1919 // 1920 // double scale = detailAdjustedScale(); 1921 // 1922 // qreal gh = d->m_graph->height(); 1923 // CanvasEdge* cedge = new CanvasEdge(this, gedge, scale, scale, d->m_xMargin, 1924 // d->m_yMargin, gh, d->m_graph->wdhcf(), d->m_graph->hdvcf()); 1925 // 1926 // gedge->setCanvasEdge(cedge); 1927 // // std::cerr << "setting z = " << gedge->z() << std::endl; 1928 // cedge->setZValue(gedge->z()); 1929 // cedge->show(); 1930 // scene()->addItem(cedge); 1931 // 1932 // emit newEdgeAdded(gedge->fromNode()->id(),gedge->toNode()->id()); 1933 // } 1934 1935 void DotGraphView::setReadOnly() 1936 { 1937 Q_D(DotGraphView); 1938 d->m_readWrite = false; 1939 if (d->m_graph) { 1940 d->m_graph->setReadOnly(); 1941 } 1942 } 1943 1944 void DotGraphView::setReadWrite() 1945 { 1946 Q_D(DotGraphView); 1947 d->m_readWrite = true; 1948 if (d->m_graph) { 1949 d->m_graph->setReadWrite(); 1950 } 1951 } 1952 1953 void DotGraphView::slotEdgeSelected(CanvasEdge *edge, Qt::KeyboardModifiers modifiers) 1954 { 1955 Q_D(DotGraphView); 1956 qCDebug(KGRAPHVIEWERLIB_LOG) << edge->edge()->id(); 1957 QList<QString> selection; 1958 selection.push_back(edge->edge()->id()); 1959 if (!modifiers.testFlag(Qt::ControlModifier)) { 1960 for (GraphEdge *e : d->m_graph->edges()) { 1961 if (e->canvasEdge() != edge) { 1962 e->setSelected(false); 1963 e->canvasEdge()->update(); 1964 } 1965 } 1966 for (GraphNode *n : d->m_graph->nodes()) { 1967 n->setSelected(false); 1968 n->canvasNode()->update(); 1969 } 1970 for (GraphSubgraph *s : d->m_graph->subgraphs()) { 1971 s->setElementSelected(nullptr, false, true); 1972 } 1973 } else { 1974 for (GraphEdge *e : d->m_graph->edges()) { 1975 if (e->canvasEdge() != edge) { 1976 if (e->isSelected()) { 1977 selection.push_back(e->id()); 1978 } 1979 } 1980 } 1981 for (GraphNode *n : d->m_graph->nodes()) { 1982 if (n->isSelected()) { 1983 selection.push_back(n->id()); 1984 } 1985 } 1986 for (GraphSubgraph *s : d->m_graph->subgraphs()) { 1987 if (s->isSelected()) { 1988 selection.push_back(s->id()); 1989 } 1990 } 1991 } 1992 emit selectionIs(selection, QPoint()); 1993 } 1994 1995 void DotGraphView::slotElementSelected(CanvasElement *element, Qt::KeyboardModifiers modifiers) 1996 { 1997 Q_D(DotGraphView); 1998 QList<QString> selection; 1999 selection.push_back(element->element()->id()); 2000 if (!modifiers.testFlag(Qt::ControlModifier)) { 2001 for (GraphEdge *e : d->m_graph->edges()) { 2002 if (e->isSelected()) { 2003 e->setSelected(false); 2004 e->canvasEdge()->update(); 2005 } 2006 } 2007 for (GraphNode *e : d->m_graph->nodes()) { 2008 if (e->canvasElement() != element) { 2009 if (e->isSelected()) { 2010 e->setSelected(false); 2011 e->canvasElement()->update(); 2012 } 2013 } 2014 } 2015 for (GraphSubgraph *s : d->m_graph->subgraphs()) { 2016 s->setElementSelected(element->element(), true, true); 2017 } 2018 } else { 2019 for (GraphEdge *e : d->m_graph->edges()) { 2020 if (e->isSelected()) { 2021 selection.push_back(e->id()); 2022 } 2023 } 2024 for (GraphNode *n : d->m_graph->nodes()) { 2025 if (n->isSelected()) { 2026 selection.push_back(n->id()); 2027 } 2028 } 2029 for (GraphSubgraph *s : d->m_graph->subgraphs()) { 2030 s->retrieveSelectedElementsIds(selection); 2031 } 2032 } 2033 emit selectionIs(selection, QPoint()); 2034 } 2035 2036 void DotGraphView::removeSelectedEdges() 2037 { 2038 Q_D(DotGraphView); 2039 for (GraphEdge *e : d->m_graph->edges()) { 2040 if (e->isSelected()) { 2041 qCDebug(KGRAPHVIEWERLIB_LOG) << "emiting removeEdge " << e->id(); 2042 d->m_graph->removeEdge(e->id()); 2043 emit removeEdge(e->id()); 2044 } 2045 } 2046 } 2047 2048 void DotGraphView::removeSelectedNodes() 2049 { 2050 Q_D(DotGraphView); 2051 qCDebug(KGRAPHVIEWERLIB_LOG); 2052 for (GraphNode *e : d->m_graph->nodes()) { 2053 if (e->isSelected()) { 2054 qCDebug(KGRAPHVIEWERLIB_LOG) << "emiting removeElement " << e->id(); 2055 d->m_graph->removeElement(e->id()); 2056 emit removeElement(e->id()); 2057 } 2058 } 2059 } 2060 2061 void DotGraphView::removeSelectedSubgraphs() 2062 { 2063 Q_D(DotGraphView); 2064 for (GraphSubgraph *e : d->m_graph->subgraphs()) { 2065 if (e->isSelected()) { 2066 qCDebug(KGRAPHVIEWERLIB_LOG) << "emiting removeElement " << e->id(); 2067 d->m_graph->removeElement(e->id()); 2068 emit removeElement(e->id()); 2069 } 2070 } 2071 } 2072 2073 void DotGraphView::removeSelectedElements() 2074 { 2075 removeSelectedNodes(); 2076 removeSelectedEdges(); 2077 removeSelectedSubgraphs(); 2078 } 2079 2080 void DotGraphView::timerEvent(QTimerEvent *event) 2081 { 2082 Q_D(DotGraphView); 2083 qCDebug(KGRAPHVIEWERLIB_LOG) << event->timerId(); 2084 qreal vpercent = verticalScrollBar()->value() * 1.0 / 100; 2085 qreal hpercent = horizontalScrollBar()->value() * 1.0 / 100; 2086 if (d->m_scrollDirection == Left) { 2087 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - (5 * hpercent)); 2088 } else if (d->m_scrollDirection == Right) { 2089 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + (5 * hpercent)); 2090 } else if (d->m_scrollDirection == Top) { 2091 verticalScrollBar()->setValue(verticalScrollBar()->value() - (5 * vpercent)); 2092 } else if (d->m_scrollDirection == Bottom) { 2093 verticalScrollBar()->setValue(verticalScrollBar()->value() + (5 * vpercent)); 2094 } 2095 } 2096 2097 void DotGraphView::leaveEvent(QEvent * /*event*/) 2098 { 2099 Q_D(DotGraphView); 2100 qCDebug(KGRAPHVIEWERLIB_LOG) << mapFromGlobal(QCursor::pos()); 2101 if (d->m_editingMode == DrawNewEdge) { 2102 d->m_leavedTimer = startTimer(10); 2103 if (mapFromGlobal(QCursor::pos()).x() <= 0) { 2104 d->m_scrollDirection = Left; 2105 } else if (mapFromGlobal(QCursor::pos()).y() <= 0) { 2106 d->m_scrollDirection = Top; 2107 } else if (mapFromGlobal(QCursor::pos()).x() >= width()) { 2108 d->m_scrollDirection = Right; 2109 } else if (mapFromGlobal(QCursor::pos()).y() >= height()) { 2110 d->m_scrollDirection = Bottom; 2111 } 2112 } 2113 } 2114 2115 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) 2116 void DotGraphView::enterEvent(QEnterEvent * /*event*/) 2117 #else 2118 void DotGraphView::enterEvent(QEvent * /*event*/) 2119 #endif 2120 { 2121 Q_D(DotGraphView); 2122 qCDebug(KGRAPHVIEWERLIB_LOG); 2123 if (d->m_leavedTimer != std::numeric_limits<int>::max()) { 2124 killTimer(d->m_leavedTimer); 2125 d->m_leavedTimer = std::numeric_limits<int>::max(); 2126 } 2127 } 2128 2129 void DotGraphView::slotAGraphReadFinished() 2130 { 2131 Q_D(DotGraphView); 2132 QString layoutCommand = (d->m_graph ? d->m_graph->layoutCommand() : QString()); 2133 if (layoutCommand.isEmpty()) { 2134 if (!d->m_loadThread.dotFileName().isEmpty()) 2135 layoutCommand = d->m_graph->chooseLayoutProgramForFile(d->m_loadThread.dotFileName()); 2136 else 2137 layoutCommand = "dot"; 2138 } 2139 d->m_layoutThread.layoutGraph(d->m_loadThread.g(), layoutCommand); 2140 d->m_loadThread.processed_finished(); 2141 } 2142 2143 void DotGraphView::slotAGraphLayoutFinished() 2144 { 2145 Q_D(DotGraphView); 2146 graph_t *g = d->m_layoutThread.g(); 2147 bool result = loadLibrary(g, d->m_layoutThread.layoutCommand()); 2148 if (result) 2149 // file name can be taken from m_loadThread both in sync and async loading cases 2150 // see comment in loadLibrarySync() 2151 d->m_graph->dotFileName(d->m_loadThread.dotFileName()); 2152 else { 2153 Q_ASSERT(!d->m_canvas); 2154 QGraphicsScene *newCanvas = new QGraphicsScene(); 2155 QGraphicsSimpleTextItem *loadingLabel = newCanvas->addSimpleText(i18n("Failed to open %1", d->m_loadThread.dotFileName())); 2156 loadingLabel->setZValue(100); 2157 centerOn(loadingLabel); 2158 setScene(newCanvas); 2159 d->m_canvas = newCanvas; 2160 } 2161 2162 if (g) { 2163 gvFreeLayout(d->m_layoutThread.gvc(), g); 2164 agclose(g); 2165 } 2166 d->m_layoutThread.processed_finished(); 2167 } 2168 2169 void DotGraphView::slotSelectNode(const QString &nodeName) 2170 { 2171 qCDebug(KGRAPHVIEWERLIB_LOG) << nodeName; 2172 GraphNode *node = dynamic_cast<GraphNode *>(graph()->elementNamed(nodeName)); 2173 if (node == nullptr) 2174 return; 2175 node->setSelected(true); 2176 if (node->canvasNode()) { 2177 node->canvasNode()->modelChanged(); 2178 slotElementSelected(node->canvasNode(), Qt::NoModifier); 2179 } 2180 } 2181 2182 void DotGraphView::centerOnNode(const QString &nodeId) 2183 { 2184 GraphNode *node = dynamic_cast<GraphNode *>(graph()->elementNamed(nodeId)); 2185 if (node == nullptr) 2186 return; 2187 if (node->canvasNode()) { 2188 centerOn(node->canvasNode()); 2189 } 2190 } 2191 2192 } 2193 2194 #include "moc_dotgraphview.cpp"