Graphviz 16.1.0
Loading...
Searching...
No Matches
csettings.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#ifdef _WIN32
14#include "windows.h"
15#endif
16#include "csettings.h"
17#include "mainwindow.h"
18#include "mdichild.h"
19#include "qfiledialog.h"
20#include "qmessagebox.h"
21#include "string.h"
22#include <QTemporaryFile>
23#include <QtWidgets>
24#include <cgraph/rdr.h>
25#include <cstdlib>
26#include <qfile.h>
27#include <string>
28#include <util/gv_find_me.h>
29#include <util/path.h>
30
31#include "moc_csettings.cpp"
32
33extern int errorPipe(char *errMsg);
34
35#define WIDGET(t, f) (findChild<t *>(QStringLiteral(#f)))
36
38static std::string find_me() {
39 char *const me = gv_find_me();
40 const std::string me_s = me == nullptr ? "" : me;
41 free(me);
42 return me_s;
43}
44
46static std::string find_share(void) {
47
48 // find the path to the `gvedit` binary
49 std::string gvedit_exe = find_me();
50 if (gvedit_exe == "")
51 return "";
52
53 // assume it is of the form …/bin/gvedit[.exe] and construct
54 // …/share/graphviz/gvedit
55
56 size_t slash = gvedit_exe.rfind(PATH_SEPARATOR);
57 if (slash == std::string::npos) {
58 errout << "no path separator in path to self, " << gvedit_exe.c_str()
59 << '\n';
60 return "";
61 }
62
63 std::string bin = gvedit_exe.substr(0, slash);
64 slash = bin.rfind(PATH_SEPARATOR);
65 if (slash == std::string::npos) {
66 errout << "no path separator in directory containing self, " << bin.c_str()
67 << '\n';
68 return "";
69 }
70
71 std::string install_prefix = bin.substr(0, slash);
72
73 return install_prefix + PATH_SEPARATOR + "share" + PATH_SEPARATOR +
74 "graphviz" + PATH_SEPARATOR + "gvedit";
75}
76
77bool loadAttrs(const QString &fileName, QComboBox *cbNameG, QComboBox *cbNameN,
78 QComboBox *cbNameE) {
79 QFile file(fileName);
80 if (file.open(QIODevice::ReadOnly)) {
81 QTextStream stream(&file);
82 QString line;
83 while (!stream.atEnd()) {
84 line = stream.readLine(); // line of text excluding '\n'
85 if (line.left(1) == QLatin1String(":")) {
86 QString attrName;
87 QStringList sl = line.split(u':');
88 for (int id = 0; id < sl.count(); id++) {
89 if (id == 1)
90 attrName = sl[id];
91 if (id == 2) {
92 if (sl[id].contains(u'G'))
93 cbNameG->addItem(attrName);
94 if (sl[id].contains(u'N'))
95 cbNameN->addItem(attrName);
96 if (sl[id].contains(u'E'))
97 cbNameE->addItem(attrName);
98 }
99 }
100 }
101 }
102 } else {
103 errout << "Could not open attribute name file \"" << fileName
104 << "\" for reading\n";
105 errout.flush();
106 return true;
107 }
108
109 return false;
110}
111
112QString stripFileExtension(const QString &fileName) {
113 // `lastIndexOf` returns -1 if not found and `left` takes a negative number to
114 // mean “the entire string”, so this is a no-op if the filename has no
115 // extension
116 return fileName.left(fileName.lastIndexOf(u'.', fileName.size() - 1));
117}
118
120 this->gvc = gvContext();
121 Ui_Dialog tempDia;
122 tempDia.setupUi(this);
123 graph = nullptr;
124 activeWindow = nullptr;
125 QString pathname;
126 char *s = nullptr;
127#ifndef _WIN32
128 s = getenv("GVEDIT_PATH");
129#endif
130 if (s)
131 pathname = QString::fromUtf8(s);
132 else
133 pathname = QString::fromStdString(find_share());
134
135 connect(WIDGET(QPushButton, pbAdd), &QPushButton::clicked, this,
136 &CFrmSettings::addSlot);
137 connect(WIDGET(QPushButton, pbNew), &QPushButton::clicked, this,
138 &CFrmSettings::newSlot);
139 connect(WIDGET(QPushButton, pbOpen), &QPushButton::clicked, this,
140 &CFrmSettings::openSlot);
141 connect(WIDGET(QPushButton, pbSave), &QPushButton::clicked, this,
142 &CFrmSettings::saveSlot);
143 connect(WIDGET(QPushButton, btnOK), &QPushButton::clicked, this,
144 &CFrmSettings::okSlot);
145 connect(WIDGET(QPushButton, btnCancel), &QPushButton::clicked, this,
146 &CFrmSettings::cancelSlot);
147 connect(WIDGET(QPushButton, pbOut), &QPushButton::clicked, this,
148 &CFrmSettings::outputSlot);
149 connect(WIDGET(QPushButton, pbHelp), &QPushButton::clicked, this,
150 &CFrmSettings::helpSlot);
151
152 connect(WIDGET(QComboBox, cbScope),
153 QOverload<int>::of(&QComboBox::currentIndexChanged), this,
154 &CFrmSettings::scopeChangedSlot);
155 scopeChangedSlot(0);
156
157 if (!pathname.isEmpty()) {
158 loadAttrs(pathname + QLatin1String("/attrs.txt"),
159 WIDGET(QComboBox, cbNameG), WIDGET(QComboBox, cbNameN),
160 WIDGET(QComboBox, cbNameE));
161 }
162 setWindowIcon(QIcon(QStringLiteral(":/images/icon.png")));
163}
164
165void CFrmSettings::outputSlot() {
166 QString _filter = QStringLiteral("Output File(*.%1)")
167 .arg(WIDGET(QComboBox, cbExtension)->currentText());
168 QString fileName = QFileDialog::getSaveFileName(this, tr("Save Graph As.."),
169 QStringLiteral("/"), _filter);
170 if (!fileName.isEmpty())
171 WIDGET(QLineEdit, leOutput)->setText(fileName);
172}
173
174void CFrmSettings::scopeChangedSlot(int id) {
175 WIDGET(QComboBox, cbNameG)->setVisible(id == 0);
176 WIDGET(QComboBox, cbNameN)->setVisible(id == 1);
177 WIDGET(QComboBox, cbNameE)->setVisible(id == 2);
178}
179
180void CFrmSettings::addSlot() {
181 QString _scope = WIDGET(QComboBox, cbScope)->currentText();
182 QString _name;
183 switch (WIDGET(QComboBox, cbScope)->currentIndex()) {
184 case 0:
185 _name = WIDGET(QComboBox, cbNameG)->currentText();
186 break;
187 case 1:
188 _name = WIDGET(QComboBox, cbNameN)->currentText();
189 break;
190 case 2:
191 _name = WIDGET(QComboBox, cbNameE)->currentText();
192 break;
193 }
194 QString _value = WIDGET(QLineEdit, leValue)->text();
195
196 if (_value.trimmed().isEmpty())
197 QMessageBox::warning(this, tr("GvEdit"),
198 tr("Please enter a value for selected attribute!"),
199 QMessageBox::Ok, QMessageBox::Ok);
200 else {
201 QString str = _scope + QLatin1Char(u'[') + _name + QLatin1String("=\"");
202 if (WIDGET(QTextEdit, teAttributes)->toPlainText().contains(str)) {
203 QMessageBox::warning(this, tr("GvEdit"),
204 tr("Attribute is already defined!"), QMessageBox::Ok,
205 QMessageBox::Ok);
206 return;
207 }
208 str = str + _value + QLatin1String("\"]");
209 WIDGET(QTextEdit, teAttributes)
210 ->setPlainText(WIDGET(QTextEdit, teAttributes)->toPlainText() + str +
211 QLatin1Char('\n'));
212 }
213}
214
215void CFrmSettings::helpSlot() {
216 QDesktopServices::openUrl(
217 QUrl(QStringLiteral("http://www.graphviz.org/doc/info/attrs.html")));
218}
219
220void CFrmSettings::cancelSlot() { this->reject(); }
221
222void CFrmSettings::okSlot() {
223 saveContent();
224 this->done(drawGraph());
225}
226
227void CFrmSettings::newSlot() {
228 WIDGET(QTextEdit, teAttributes)->setPlainText(tr(""));
229}
230
231void CFrmSettings::openSlot() {
232 QString fileName = QFileDialog::getOpenFileName(
233 this, tr("Open File"), QStringLiteral("/"), tr("Text file (*.*)"));
234 if (!fileName.isEmpty()) {
235 QFile file(fileName);
236 if (!file.open(QFile::ReadOnly | QFile::Text)) {
237 QMessageBox::warning(this, tr("MDI"),
238 tr("Cannot read file %1:\n%2.")
239 .arg(fileName)
240 .arg(file.errorString()));
241 return;
242 }
243
244 QTextStream in(&file);
245 WIDGET(QTextEdit, teAttributes)->setPlainText(in.readAll());
246 }
247}
248
249void CFrmSettings::saveSlot() {
250
251 if (WIDGET(QTextEdit, teAttributes)->toPlainText().trimmed().isEmpty()) {
252 QMessageBox::warning(this, tr("GvEdit"), tr("Nothing to save!"),
253 QMessageBox::Ok, QMessageBox::Ok);
254 return;
255 }
256
257 QString fileName = QFileDialog::getSaveFileName(
258 this, tr("Open File"), QStringLiteral("/"), tr("Text File(*.*)"));
259 if (!fileName.isEmpty()) {
260
261 QFile file(fileName);
262 if (!file.open(QFile::WriteOnly | QFile::Text)) {
263 QMessageBox::warning(this, tr("MDI"),
264 tr("Cannot write file %1:\n%2.")
265 .arg(fileName)
266 .arg(file.errorString()));
267 return;
268 }
269
270 QTextStream out(&file);
271 out << WIDGET(QTextEdit, teAttributes)->toPlainText();
272 }
273}
274
275bool CFrmSettings::loadGraph(MdiChild *m) {
276 if (graph) {
277 agclose(graph);
278 graph = nullptr;
279 }
280 graphData.clear();
281 graphData.append(m->toPlainText());
282 setActiveWindow(m);
283 return true;
284}
285
286bool CFrmSettings::createLayout() {
287 rdr_t rdr;
288 // first attach attributes to graph
289 int _pos = graphData.indexOf(tr("{"));
290 graphData.replace(_pos, 1,
291 QLatin1Char('{') +
292 WIDGET(QTextEdit, teAttributes)->toPlainText());
293
294 QByteArray bytes = graphData.toUtf8();
295 rdr.data = bytes.constData();
296 rdr.len = strlen(rdr.data);
297 rdr.cur = 0;
298 graph = agmemread(rdr.data);
299 if (!graph)
300 return false;
301 if (agerrors()) {
302 agclose(graph);
303 graph = nullptr;
304 return false;
305 }
306 Agraph_t *G = this->graph;
307 QString layout;
308
309 layout = WIDGET(QComboBox, cbLayout)->currentText();
310
311 gvLayout(gvc, G, layout.toUtf8().constData()); /* library function */
312 return true;
313}
314
315void CFrmSettings::doPreview(const QString &fileName) {
316 if (getActiveWindow()->previewFrm != nullptr) {
317 getActiveWindow()->parentFrm->mdiArea->removeSubWindow(
318 getActiveWindow()->previewFrm->subWindowRef);
319 getActiveWindow()->previewFrm = nullptr;
320 }
321
322 if (fileName.isNull() ||
323 !getActiveWindow()->loadPreview(fileName)) { // create preview
324 QTemporaryFile tempFile;
325 if (!tempFile.open())
326 return;
327 QString prevFile(tempFile.fileName());
328 gvRenderFilename(gvc, graph, "png", prevFile.toUtf8().constData());
329 getActiveWindow()->loadPreview(prevFile);
330 }
331}
332
333bool CFrmSettings::renderLayout() {
334 if (!graph)
335 return false;
336 QString sfx = WIDGET(QComboBox, cbExtension)->currentText();
337 QString fileName(WIDGET(QLineEdit, leOutput)->text());
338
339 if (fileName.isEmpty() || sfx == QLatin1String("NONE"))
340 doPreview(QString());
341 else {
343 fileName = fileName + QLatin1Char('.') + sfx;
344 if (fileName != activeWindow->outputFile)
345 activeWindow->outputFile = fileName;
346
347#ifdef _WIN32
348 if (!fileName.contains(u'/') && !fileName.contains(u'\\'))
349#else
350 if (!fileName.contains(u'/'))
351#endif
352 { // no directory info => can we create/write the file?
353 QFile outf(fileName);
354 if (!outf.open(QIODevice::WriteOnly)) {
355 QString pathName = QDir::homePath();
356 pathName.append(u'/').append(fileName);
357 fileName = QDir::toNativeSeparators(pathName);
358 const QString msg =
359 QStringLiteral("Output written to %1\n").arg(fileName);
360 errorPipe(msg.toLatin1().data());
361 }
362 }
363
364 if (gvRenderFilename(gvc, graph, sfx.toUtf8().constData(),
365 fileName.toUtf8().constData()))
366 return false;
367
368 doPreview(fileName);
369 }
370 return true;
371}
372
373bool CFrmSettings::loadLayouts() { return false; }
374
375bool CFrmSettings::loadRenderers() { return false; }
376
377void CFrmSettings::refreshContent() {
378
379 WIDGET(QComboBox, cbLayout)->setCurrentIndex(activeWindow->layoutIdx);
380 WIDGET(QComboBox, cbExtension)->setCurrentIndex(activeWindow->renderIdx);
381 if (!activeWindow->outputFile.isEmpty())
382 WIDGET(QLineEdit, leOutput)->setText(activeWindow->outputFile);
383 else
384 WIDGET(QLineEdit, leOutput)
385 ->setText(stripFileExtension(activeWindow->currentFile()) +
386 QLatin1Char('.') +
387 WIDGET(QComboBox, cbExtension)->currentText());
388
389 WIDGET(QTextEdit, teAttributes)->setText(activeWindow->attributes);
390
391 WIDGET(QLineEdit, leValue)->clear();
392}
393
394void CFrmSettings::saveContent() {
395 activeWindow->layoutIdx = WIDGET(QComboBox, cbLayout)->currentIndex();
396 activeWindow->renderIdx = WIDGET(QComboBox, cbExtension)->currentIndex();
397 activeWindow->outputFile = WIDGET(QLineEdit, leOutput)->text();
398 activeWindow->attributes = WIDGET(QTextEdit, teAttributes)->toPlainText();
399}
400
402 if (createLayout() && renderLayout()) {
403 getActiveWindow()->settingsSet = false;
404 }
406
407 return QDialog::Accepted;
408}
409
411 if (this->loadGraph(m))
412 return drawGraph();
413
414 if (m && m == getActiveWindow()) {
415 if (this->loadGraph(m))
416 return drawGraph();
417 return QDialog::Rejected;
418 }
419
420 return showSettings(m);
421}
422
424
425 if (this->loadGraph(m)) {
426 refreshContent();
427 return this->exec();
428 }
429 return QDialog::Rejected;
430}
431
432void CFrmSettings::setActiveWindow(MdiChild *m) { this->activeWindow = m; }
433
434MdiChild *CFrmSettings::getActiveWindow() { return activeWindow; }
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
QString graphData
Definition csettings.h:31
int runSettings(MdiChild *m)
int showSettings(MdiChild *m)
GVC_t * gvc
Definition csettings.h:32
MdiChild * getActiveWindow()
QMdiArea * mdiArea
Definition mainwindow.h:40
std::unique_ptr< ImageViewer > previewFrm
Definition mdichild.h:38
bool loadPreview(const QString &fileName)
Definition mdichild.cpp:144
QString attributes
Definition mdichild.h:37
QString outputFile
Definition mdichild.h:34
int layoutIdx
Definition mdichild.h:32
bool settingsSet
Definition mdichild.h:42
int renderIdx
Definition mdichild.h:33
CMainWindow * parentFrm
Definition mdichild.h:39
QString currentFile()
Definition mdichild.h:31
static int in(Extype_t lhs, Exid_t *rhs, Exdisc_t *disc)
Definition compile.c:1641
#define WIDGET(t, f)
Definition csettings.cpp:35
bool loadAttrs(const QString &fileName, QComboBox *cbNameG, QComboBox *cbNameN, QComboBox *cbNameE)
Definition csettings.cpp:77
QString stripFileExtension(const QString &fileName)
int errorPipe(char *errMsg)
static std::string find_me()
wrapper around gv_find_me to convert to a C++ type
Definition csettings.cpp:38
static std::string find_share(void)
find an absolute path to where Gvedit auxiliary files are stored
Definition csettings.cpp:46
#define G
Definition gdefs.h:7
void free(void *)
int agreseterrors(void)
Definition agerror.c:185
int agerrors(void)
Definition agerror.c:183
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:97
Agraph_t * agmemread(const char *cp)
reads a graph from the input string
Definition io.c:75
int gvLayout(GVC_t *gvc, graph_t *g, const char *engine)
Definition gvc.c:52
GVC_t * gvContext(void)
Definition gvc.c:24
int gvRenderFilename(GVC_t *gvc, graph_t *g, const char *format, const char *filename)
Definition gvc.c:114
static uint64_t id
Definition gv2gml.c:42
char * gv_find_me(void)
Definition gv_find_me.c:69
platform abstraction for finding the path to yourself
textitem scanner parser str
Definition htmlparse.y:218
char * fileName(ingraph_state *sp)
Return name of current file being processed.
Definition ingraphs.c:156
static int layout(graph_t *g, layout_info *infop, size_t *counter)
Definition layout.c:800
QTextStream errout
File system path helpers.
#define PATH_SEPARATOR
character for separating directory components in a file system path
Definition path.h:10
graph or subgraph
Definition cgraph.h:424
Definition rdr.h:10
size_t cur
Definition rdr.h:13
const char * data
Definition rdr.h:11
size_t len
Definition rdr.h:12
Definition grammar.c:90