Graphviz 16.1.0~dev.20260902.0144
Loading...
Searching...
No Matches
mainwindow.cpp
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include "csettings.h"
14#include "mainwindow.h"
15#include "mdichild.h"
16#include <QStringList>
17#include <QtWidgets>
18#include <optional>
19#include <qframe.h>
20#include <string_view>
21
22#include "moc_mainwindow.cpp"
23
24QTextEdit *globTextEdit;
25
26int errorPipe(char *errMsg) {
27 globTextEdit->setText(globTextEdit->toPlainText() +
28 QString::fromUtf8(errMsg));
29 return 0;
30}
31
32static void freeList(char **lp, int count) {
33 for (int i = 0; i < count; i++)
34 free(lp[i]);
35 free(lp);
36}
37
38static int LoadPlugins(QComboBox &cb, GVC_t *gvc, const char *kind,
39 const QStringList &more, std::string_view prefer) {
40 int count;
41 char **lp = gvPluginList(gvc, kind, &count);
42 std::optional<int> idx;
43
44 cb.clear();
45 for (int id = 0; id < count; id++) {
46 cb.addItem(QString::fromUtf8(lp[id]));
47 if (!idx.has_value() && prefer == lp[id])
48 idx = id;
49 }
50 freeList(lp, count);
51
52 /* Add additional items if supplied */
53 cb.addItems(more);
54
55 if (idx.has_value())
56 cb.setCurrentIndex(*idx);
57
58 return idx.value_or(0);
59}
60
61void CMainWindow::createConsole() {
62 QDockWidget *dock = new QDockWidget(tr("Output Console"), nullptr);
63 QTextEdit *textEdit = new QTextEdit(dock);
64
65 dock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea);
66 addDockWidget(Qt::BottomDockWidgetArea, dock);
67 QVBoxLayout *vL = new QVBoxLayout();
68
69 textEdit->setObjectName(QStringLiteral("textEdit"));
70 globTextEdit = textEdit;
72
73 vL->addWidget(textEdit);
74 vL->setContentsMargins(1, 1, 1, 1);
75
76 QFrame *fr = new QFrame(dock);
77 vL->addWidget(fr);
78
79 QPushButton *logNewBtn =
80 new QPushButton(QIcon(QStringLiteral(":/images/new.png")), {}, fr);
81 QPushButton *logSaveBtn =
82 new QPushButton(QIcon(QStringLiteral(":/images/save.png")), {}, fr);
83 QHBoxLayout *consoleLayout = new QHBoxLayout();
84 consoleLayout->addWidget(logNewBtn);
85 connect(logNewBtn, &QPushButton::clicked, this, &CMainWindow::slotNewLog);
86 connect(logSaveBtn, &QPushButton::clicked, this, &CMainWindow::slotSaveLog);
87 consoleLayout->addWidget(logSaveBtn);
88 consoleLayout->addStretch();
89
90 consoleLayout->setContentsMargins(1, 1, 1, 1);
91
92 fr->setLayout(consoleLayout);
93
94 QFrame *mainFrame = new QFrame(dock);
95 mainFrame->setLayout(vL);
96
97 dock->setWidget(mainFrame);
98}
99
100static const QStringList xtra = {QStringLiteral("NONE")};
101
102CMainWindow::CMainWindow(const QStringList &files) {
103
104 QWidget *centralwidget = new QWidget(this);
105 centralwidget->setObjectName(QStringLiteral("centralwidget"));
106 QVBoxLayout *verticalLayout_2 = new QVBoxLayout(centralwidget);
107 verticalLayout_2->setObjectName(QStringLiteral("verticalLayout_2"));
108 QVBoxLayout *verticalLayout = new QVBoxLayout();
109 verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
110 mdiArea = new QMdiArea(centralwidget);
111 mdiArea->setObjectName(QStringLiteral("mdiArea"));
112
113 verticalLayout->addWidget(mdiArea);
114 verticalLayout_2->setContentsMargins(1, 1, 1, 1);
115 verticalLayout_2->addLayout(verticalLayout);
116 setCentralWidget(centralwidget);
117 centralwidget->layout()->setContentsMargins(1, 1, 1, 1);
118 prevChild = nullptr;
119
120 createConsole();
121
122 connect(mdiArea, &QMdiArea::subWindowActivated, this,
123 &CMainWindow::slotRefreshMenus);
124
125 frmSettings = new CFrmSettings();
126
127 actions();
128 menus();
129 toolBars();
130 statusBar();
131 updateMenus();
132
133 readSettings();
134
135 setWindowTitle(tr("GVEdit"));
136 this->resize(1024, 900);
137 this->move(0, 0);
138 setUnifiedTitleAndToolBarOnMac(true);
139 QComboBox *cb =
140 frmSettings->findChild<QComboBox *>(QStringLiteral("cbLayout"));
141 dfltLayoutIdx = LoadPlugins(*cb, frmSettings->gvc, "layout", {}, "dot");
142 cb = frmSettings->findChild<QComboBox *>(QStringLiteral("cbExtension"));
143 dfltRenderIdx = LoadPlugins(*cb, frmSettings->gvc, "device", xtra, "png");
144 statusBar()->showMessage(tr("Ready"));
145 setWindowIcon(QIcon(QStringLiteral(":/images/icon.png")));
146 // load files specified in command line , one time task
147 for (const QString &file : files) {
148 addFile(file);
149 }
150}
151
152void CMainWindow::closeEvent(QCloseEvent *event) {
153 mdiArea->closeAllSubWindows();
154 if (mdiArea->currentSubWindow()) {
155 event->ignore();
156 } else {
157 writeSettings();
158 event->accept();
159 }
160}
161
162void CMainWindow::slotNew() {
163 MdiChild *child = createMdiChild();
164 child->newFile();
165 child->show();
166}
167
168void CMainWindow::addFile(const QString &fileName) {
169 if (!fileName.isEmpty()) {
170 QMdiSubWindow *existing = findMdiChild(fileName);
171 if (existing) {
172 mdiArea->setActiveSubWindow(existing);
173 return;
174 }
175
176 MdiChild *child = createMdiChild();
177 if (child->loadFile(fileName)) {
178 statusBar()->showMessage(tr("File loaded"), 2000);
179 child->show();
180 slotRun(child);
181 } else {
182 child->close();
183 }
184 }
185}
186
187void CMainWindow::slotOpen() {
188 QStringList filters{
189 QStringLiteral("*.cpp"),
190 QStringLiteral("*.cxx"),
191 QStringLiteral("*.cc"),
192 };
193
194 QFileDialog fd;
195 fd.setNameFilter(QStringLiteral("XML (*.xml)"));
196 QString fileName = fd.getOpenFileName(this);
197
199}
200
201void CMainWindow::slotSave() {
202 if (activeMdiChild() && activeMdiChild()->save())
203 statusBar()->showMessage(tr("File saved"), 2000);
204}
205
206void CMainWindow::slotSaveAs() {
207 if (activeMdiChild() && activeMdiChild()->saveAs())
208 statusBar()->showMessage(tr("File saved"), 2000);
209}
210
211void CMainWindow::slotCut() {
212 if (activeMdiChild())
213 activeMdiChild()->cut();
214}
215
216void CMainWindow::slotCopy() {
217 if (activeMdiChild())
218 activeMdiChild()->copy();
219}
220
221void CMainWindow::slotPaste() {
222 if (activeMdiChild())
223 activeMdiChild()->paste();
224}
225
226void CMainWindow::slotAbout() {
227 QString abs(tr("<b>GVEdit</b> Graph File Editor For Graphviz"
228 " version: 1.02\n"
229 "Graphviz version: "));
230
231 abs += tr(gvcVersion(frmSettings->gvc));
232 QMessageBox::about(this, tr("About GVEdit"), abs);
233}
234
235void CMainWindow::setChild() {
236 if (prevChild != activeMdiChild()) {
237 const QString msg =
238 QStringLiteral("working on %1\n").arg(activeMdiChild()->currentFile());
239 errorPipe(msg.toLatin1().data());
240 prevChild = activeMdiChild();
241 }
242}
243
244void CMainWindow::slotSettings() {
245 setChild();
246 frmSettings->showSettings(activeMdiChild());
247}
248
249void CMainWindow::slotRun(MdiChild *m) {
250 setChild();
251
252 if (m)
253 frmSettings->runSettings(m);
254 else
255 frmSettings->runSettings(activeMdiChild());
256}
257
258void CMainWindow::slotNewLog() { globTextEdit->clear(); }
259
260void CMainWindow::slotSaveLog() {
261
262 if (globTextEdit->toPlainText().trimmed().isEmpty()) {
263 QMessageBox::warning(this, tr("GvEdit"), tr("Nothing to save!"),
264 QMessageBox::Ok, QMessageBox::Ok);
265 return;
266 }
267
268 QString fileName = QFileDialog::getSaveFileName(
269 this, tr("Open File"), QStringLiteral("/"), tr("Text File(*.*)"));
270 if (!fileName.isEmpty()) {
271
272 QFile file(fileName);
273 if (!file.open(QFile::WriteOnly | QFile::Text)) {
274 QMessageBox::warning(this, tr("MDI"),
275 tr("Cannot write file %1:\n%2.")
276 .arg(fileName)
277 .arg(file.errorString()));
278 return;
279 }
280
281 QTextStream out(&file);
282 out << globTextEdit->toPlainText();
283 }
284}
285
286void CMainWindow::updateFileMenu() {
287 if (!activeMdiChild()) {
288 saveAct->setEnabled(false);
289 saveAsAct->setEnabled(false);
290 pasteAct->setEnabled(false);
291 closeAct->setEnabled(false);
292 closeAllAct->setEnabled(false);
293 tileAct->setEnabled(false);
294 cascadeAct->setEnabled(false);
295 nextAct->setEnabled(false);
296 previousAct->setEnabled(false);
297 separatorAct->setVisible(false);
298 settingsAct->setEnabled(false);
299 layoutAct->setEnabled(false);
300 } else {
301 saveAct->setEnabled(true);
302 saveAsAct->setEnabled(true);
303 pasteAct->setEnabled(true);
304 closeAct->setEnabled(true);
305 closeAllAct->setEnabled(true);
306 tileAct->setEnabled(true);
307 cascadeAct->setEnabled(true);
308 nextAct->setEnabled(true);
309 previousAct->setEnabled(true);
310 separatorAct->setVisible(true);
311 settingsAct->setEnabled(true);
312 layoutAct->setEnabled(true);
313
314 if (activeMdiChild()->textCursor().hasSelection()) {
315 cutAct->setEnabled(true);
316 copyAct->setEnabled(true);
317
318 } else {
319 cutAct->setEnabled(false);
320 copyAct->setEnabled(false);
321 }
322 }
323}
324
325void CMainWindow::slotRefreshMenus() { updateMenus(); }
326
327void CMainWindow::updateMenus() {
328 this->updateFileMenu();
329 this->updateWindowMenu();
330}
331
332void CMainWindow::updateWindowMenu() {
333 mWindow->clear();
334 mWindow->addAction(closeAct);
335 mWindow->addAction(closeAllAct);
336 mWindow->addSeparator();
337 mWindow->addAction(tileAct);
338 mWindow->addAction(cascadeAct);
339 mWindow->addSeparator();
340 mWindow->addAction(nextAct);
341 mWindow->addAction(previousAct);
342 mWindow->addAction(separatorAct);
343
344 QList<QMdiSubWindow *> windows = mdiArea->subWindowList();
345 separatorAct->setVisible(!windows.isEmpty());
346
347 for (int i = 0; i < windows.size(); ++i) {
348 QMdiSubWindow *window = windows.at(i);
349 if (window->widget()->inherits("MdiChild")) {
350 MdiChild *child = qobject_cast<MdiChild *>(window->widget());
351 QString text;
352 if (i < 9) {
353 text = tr("&%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile());
354 } else {
355 text = tr("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile());
356 }
357 QAction *action = mWindow->addAction(text);
358 action->setCheckable(true);
359 action->setChecked(child == activeMdiChild());
360 connect(action, &QAction::triggered, this,
361 [this, window] { activateChild(window); });
362 }
363 }
364}
365
366MdiChild *CMainWindow::createMdiChild() {
367 MdiChild *child = new MdiChild;
368 child->parentFrm = this;
369 QMdiSubWindow *s = mdiArea->addSubWindow(child);
370 s->resize(800, 600);
371 s->move(mdiArea->subWindowList().count() * 5,
372 mdiArea->subWindowList().count() * 5);
373 connect(child, &MdiChild::copyAvailable, cutAct, &QAction::setEnabled);
374 connect(child, &MdiChild::copyAvailable, copyAct, &QAction::setEnabled);
375 child->layoutIdx = dfltLayoutIdx;
376 child->renderIdx = dfltRenderIdx;
377
378 return child;
379}
380
381void CMainWindow::actions() {
382 newAct =
383 new QAction(QIcon(QStringLiteral(":/images/new.png")), tr("&New"), this);
384 newAct->setShortcuts(QKeySequence::New);
385 newAct->setStatusTip(tr("Create a new file"));
386 connect(newAct, &QAction::triggered, this, &CMainWindow::slotNew);
387
388 openAct = new QAction(QIcon(QStringLiteral(":/images/open.png")),
389 tr("&Open..."), this);
390 openAct->setShortcuts(QKeySequence::Open);
391 openAct->setStatusTip(tr("Open an existing file"));
392 connect(openAct, &QAction::triggered, this, &CMainWindow::slotOpen);
393
394 saveAct = new QAction(QIcon(QStringLiteral(":/images/save.png")), tr("&Save"),
395 this);
396 saveAct->setShortcuts(QKeySequence::Save);
397 saveAct->setStatusTip(tr("Save the document to disk"));
398 connect(saveAct, &QAction::triggered, this, &CMainWindow::slotSave);
399
400 saveAsAct = new QAction(tr("Save &As..."), this);
401 saveAsAct->setShortcuts(QKeySequence::SaveAs);
402 saveAsAct->setStatusTip(tr("Save the document under a new name"));
403 connect(saveAsAct, &QAction::triggered, this, &CMainWindow::slotSaveAs);
404
405 exitAct = new QAction(tr("E&xit"), this);
406 exitAct->setShortcuts(QKeySequence::Quit);
407 exitAct->setStatusTip(tr("Exit the application"));
408 connect(exitAct, &QAction::triggered, qApp, &QApplication::closeAllWindows);
409
410 cutAct =
411 new QAction(QIcon(QStringLiteral(":/images/cut.png")), tr("Cu&t"), this);
412 cutAct->setShortcuts(QKeySequence::Cut);
413 cutAct->setStatusTip(tr("Cut the current selection's contents to the "
414 "clipboard"));
415 connect(cutAct, &QAction::triggered, this, &CMainWindow::slotCut);
416
417 copyAct = new QAction(QIcon(QStringLiteral(":/images/copy.png")), tr("&Copy"),
418 this);
419 copyAct->setShortcuts(QKeySequence::Copy);
420 copyAct->setStatusTip(tr("Copy the current selection's contents to the "
421 "clipboard"));
422 connect(copyAct, &QAction::triggered, this, &CMainWindow::slotCopy);
423
424 pasteAct = new QAction(QIcon(QStringLiteral(":/images/paste.png")),
425 tr("&Paste"), this);
426 pasteAct->setShortcuts(QKeySequence::Paste);
427 pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current "
428 "selection"));
429 connect(pasteAct, &QAction::triggered, this, &CMainWindow::slotPaste);
430
431 closeAct = new QAction(tr("Cl&ose"), this);
432 closeAct->setStatusTip(tr("Close the active window"));
433 connect(closeAct, &QAction::triggered, mdiArea,
434 &QMdiArea::closeActiveSubWindow);
435
436 closeAllAct = new QAction(tr("Close &All"), this);
437 closeAllAct->setStatusTip(tr("Close all the windows"));
438 connect(closeAllAct, &QAction::triggered, mdiArea,
439 &QMdiArea::closeAllSubWindows);
440
441 tileAct = new QAction(tr("&Tile"), this);
442 tileAct->setStatusTip(tr("Tile the windows"));
443 connect(tileAct, &QAction::triggered, mdiArea, &QMdiArea::tileSubWindows);
444
445 cascadeAct = new QAction(tr("&Cascade"), this);
446 cascadeAct->setStatusTip(tr("Cascade the windows"));
447 connect(cascadeAct, &QAction::triggered, mdiArea,
448 &QMdiArea::cascadeSubWindows);
449
450 nextAct = new QAction(tr("Ne&xt"), this);
451 nextAct->setShortcuts(QKeySequence::NextChild);
452 nextAct->setStatusTip(tr("Move the focus to the next window"));
453 connect(nextAct, &QAction::triggered, mdiArea,
454 &QMdiArea::activateNextSubWindow);
455
456 previousAct = new QAction(tr("Pre&vious"), this);
457 previousAct->setShortcuts(QKeySequence::PreviousChild);
458 previousAct->setStatusTip(tr("Move the focus to the previous window"));
459 connect(previousAct, &QAction::triggered, mdiArea,
460 &QMdiArea::activatePreviousSubWindow);
461
462 separatorAct = new QAction(this);
463 separatorAct->setSeparator(true);
464
465 aboutAct = new QAction(tr("&About"), this);
466 aboutAct->setStatusTip(tr("Show the application's About box"));
467 connect(aboutAct, &QAction::triggered, this, &CMainWindow::slotAbout);
468
469 settingsAct = new QAction(QIcon(QStringLiteral(":/images/settings.png")),
470 tr("Settings"), this);
471 settingsAct->setStatusTip(tr("Show Graphviz Settings"));
472 connect(settingsAct, &QAction::triggered, this, &CMainWindow::slotSettings);
473 settingsAct->setShortcut(QKeySequence(Qt::SHIFT | Qt::Key_F5));
474
475 layoutAct = new QAction(QIcon(QStringLiteral(":/images/run.png")),
476 tr("Layout"), this);
477 layoutAct->setStatusTip(tr("Layout the active graph"));
478 connect(layoutAct, &QAction::triggered, this, [this] { slotRun(); });
479 layoutAct->setShortcut(QKeySequence(Qt::Key_F5));
480}
481
482void CMainWindow::menus() {
483 mFile = menuBar()->addMenu(tr("&File"));
484 mEdit = menuBar()->addMenu(tr("&Edit"));
485 mWindow = menuBar()->addMenu(tr("&Window"));
486 mGraph = menuBar()->addMenu(tr("&Graph"));
487 mHelp = menuBar()->addMenu(tr("&Help"));
488
489 mFile->addAction(newAct);
490 mFile->addAction(openAct);
491 mFile->addAction(saveAct);
492 mFile->addAction(saveAsAct);
493 mFile->addSeparator();
494
495 mFile->addAction(exitAct);
496
497 mEdit->addAction(cutAct);
498 mEdit->addAction(copyAct);
499 mEdit->addAction(pasteAct);
500
501 mGraph->addAction(settingsAct);
502 mGraph->addAction(layoutAct);
503 mGraph->addSeparator();
504
505 updateWindowMenu();
506 connect(mWindow, &QMenu::aboutToShow, this, &CMainWindow::slotRefreshMenus);
507 mHelp->addAction(aboutAct);
508}
509
510void CMainWindow::toolBars() {
511 tbFile = addToolBar(tr("File"));
512 tbFile->addAction(newAct);
513 tbFile->addAction(openAct);
514 tbFile->addAction(saveAct);
515
516 tbEdit = addToolBar(tr("Edit"));
517 tbEdit->addAction(cutAct);
518 tbEdit->addAction(copyAct);
519 tbEdit->addAction(pasteAct);
520
521 tbGraph = addToolBar(tr("Graph"));
522 tbGraph->addAction(settingsAct);
523 tbGraph->addAction(layoutAct);
524}
525
526void CMainWindow::readSettings() {
527 // first try new settings
528 {
529 QSettings settings(QStringLiteral("Graphviz"), QStringLiteral("gvedit"));
530 if (settings.contains(QStringLiteral("pos")) &&
531 settings.contains(QStringLiteral("size"))) {
532 QPoint pos =
533 settings.value(QStringLiteral("pos"), QPoint(200, 200)).toPoint();
534 QSize size =
535 settings.value(QStringLiteral("size"), QSize(400, 400)).toSize();
536 move(pos);
537 resize(size);
538 return;
539 }
540 }
541
542 // fall back to old settings
543 QSettings settings(QStringLiteral("Trolltech"),
544 QStringLiteral("MDI Example"));
545 QPoint pos =
546 settings.value(QStringLiteral("pos"), QPoint(200, 200)).toPoint();
547 QSize size = settings.value(QStringLiteral("size"), QSize(400, 400)).toSize();
548 move(pos);
549 resize(size);
550}
551
552void CMainWindow::writeSettings() {
553 QSettings settings(QStringLiteral("Graphviz"), QStringLiteral("gvedit"));
554 settings.setValue(QStringLiteral("pos"), pos());
555 settings.setValue(QStringLiteral("size"), size());
556}
557
558MdiChild *CMainWindow::activeMdiChild() {
559 if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow()) {
560 if (activeSubWindow->widget()->inherits("MdiChild"))
561 return qobject_cast<MdiChild *>(activeSubWindow->widget());
562 return qobject_cast<ImageViewer *>(activeSubWindow->widget())->graphWindow;
563 }
564 return 0;
565}
566
567QMdiSubWindow *CMainWindow::findMdiChild(const QString &fileName) {
568 QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();
569
570 foreach (QMdiSubWindow *window, mdiArea->subWindowList()) {
571 if (window->widget()->inherits("MdiChild")) {
572
573 MdiChild *mdiChild = qobject_cast<MdiChild *>(window->widget());
574 if (mdiChild->currentFile() == canonicalFilePath)
575 return window;
576 } else {
577
578 MdiChild *mdiChild =
579 qobject_cast<ImageViewer *>(window->widget())->graphWindow;
580 if (mdiChild->currentFile() == canonicalFilePath)
581 return window;
582 }
583 }
584 return 0;
585}
586
587void CMainWindow::activateChild(QWidget *window) {
588 if (!window)
589 return;
590 mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow *>(window));
591}
static void out(agerrlevel_t level, const char *fmt, va_list args)
Report messages using a user-supplied or default write function.
Definition agerror.c:86
int runSettings(MdiChild *m)
int showSettings(MdiChild *m)
GVC_t * gvc
Definition csettings.h:32
QMdiArea * mdiArea
Definition mainwindow.h:40
void closeEvent(QCloseEvent *event)
void addFile(const QString &fileName)
CMainWindow(const QStringList &files)
QString userFriendlyCurrentFile()
Definition mdichild.cpp:101
bool loadFile(const QString &fileName)
Definition mdichild.cpp:42
int layoutIdx
Definition mdichild.h:32
void newFile()
Definition mdichild.cpp:31
int renderIdx
Definition mdichild.h:33
CMainWindow * parentFrm
Definition mdichild.h:39
QString currentFile()
Definition mdichild.h:31
int errorPipe(char *errMsg)
void free(void *)
agusererrf agseterrf(agusererrf newf)
Definition agerror.c:34
char ** gvPluginList(GVC_t *gvc, const char *kind, int *sz)
Definition gvplugin.c:427
char * gvcVersion(GVC_t *gvc)
Definition gvc.c:234
static uint64_t id
Definition gv2gml.c:42
static GVC_t * gvc
Definition gv.cpp:27
char * fileName(ingraph_state *sp)
Return name of current file being processed.
Definition ingraphs.c:156
QTextEdit * globTextEdit
int errorPipe(char *errMsg)
static int LoadPlugins(QComboBox &cb, GVC_t *gvc, const char *kind, const QStringList &more, std::string_view prefer)
static const QStringList xtra
static void freeList(char **lp, int count)
Definition gvcint.h:81
Definition grammar.c:90