~ubuntu-branches/ubuntu/vivid/kdesdk/vivid

« back to all changes in this revision

Viewing changes to umbrello/umbrello/dotgenerator.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2012-06-06 11:49:54 UTC
  • mfrom: (0.4.21)
  • Revision ID: package-import@ubuntu.com-20120606114954-rdls73fzlpzxglbx
Tags: 4:4.8.80-0ubuntu1
* New uptream beta release
* Update dont_export_private_classes.diff

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   This program is free software; you can redistribute it and/or modify  *
 
3
 *   it under the terms of the GNU General Public License as published by  *
 
4
 *   the Free Software Foundation; either version 2 of the License, or     *
 
5
 *   (at your option) any later version.                                   *
 
6
 *                                                                         *
 
7
 *   copyright (C) 2012                                                    *
 
8
 *   Umbrello UML Modeller Authors <uml-devel@uml.sf.net>                  *
 
9
 ***************************************************************************/
 
10
 
 
11
// self includes
 
12
#include "dotgenerator.h"
 
13
 
 
14
// app includes
 
15
#include "activitywidget.h"
 
16
#include "associationwidget.h"
 
17
#include "classifierwidget.h"
 
18
#include "signalwidget.h"
 
19
#include "statewidget.h"
 
20
#include "debug_utils.h"
 
21
#include "umlwidget.h"
 
22
 
 
23
// kde includes
 
24
#include <KConfigGroup>
 
25
#include <KDesktopFile>
 
26
#include <KStandardDirs>
 
27
 
 
28
// qt includes
 
29
#include <QFile>
 
30
#include <QPaintEngine>
 
31
#include <QProcess>
 
32
#include <QRectF>
 
33
#include <QRegExp>
 
34
#include <QString>
 
35
#include <QTemporaryFile>
 
36
#include <QTextStream>
 
37
#include <QtDebug>
 
38
 
 
39
/**
 
40
 * dot specific paint engine
 
41
 */
 
42
class DotPaintEngine : public QPaintEngine
 
43
{
 
44
public:
 
45
    DotPaintEngine(PaintEngineFeatures caps = 0 ) {}
 
46
    virtual ~DotPaintEngine() {}
 
47
    virtual bool begin (QPaintDevice * pdev)
 
48
    {
 
49
        return true;
 
50
    }
 
51
    virtual void drawEllipse(const QRectF & rect) {}
 
52
    virtual void drawEllipse(const QRect & rect) {}
 
53
    virtual void drawImage(const QRectF & rectangle, const QImage & image, const QRectF & sr, Qt::ImageConversionFlags flags = Qt::AutoColor) {}
 
54
    virtual void drawLines(const QLineF * lines, int lineCount) {}
 
55
    virtual void drawLines(const QLine * lines, int lineCount) {}
 
56
    virtual void drawwPath(const QPainterPath & path) {}
 
57
    virtual void drawPixmap(const QRectF & r, const QPixmap & pm, const QRectF & sr) {}
 
58
    virtual void drawPoints(const QPointF * points, int pointCount) {}
 
59
    virtual void drawPoints(const QPoint * points, int pointCount) {}
 
60
    virtual void drawPolygon(const QPointF * points, int pointCount, PolygonDrawMode mode) {}
 
61
    virtual void drawPolygon(const QPoint * points, int pointCount, PolygonDrawMode mode) {}
 
62
    virtual void drawRects(const QRectF * rects, int rectCount) {}
 
63
    virtual void drawRects(const QRect * rects, int rectCount) {}
 
64
    virtual void drawTextItem(const QPointF & p, const QTextItem & textItem)
 
65
    {
 
66
        m_data << textItem.text();
 
67
    }
 
68
    virtual void drawTiledPixmap(const QRectF & rect, const QPixmap & pixmap, const QPointF & p) {}
 
69
    virtual bool end()
 
70
    {
 
71
        return true;
 
72
    }
 
73
    virtual Type type() const
 
74
    {
 
75
        return QPaintEngine::User;
 
76
    }
 
77
    virtual void updateState(const QPaintEngineState & state) {}
 
78
 
 
79
    QStringList m_data;
 
80
};
 
81
 
 
82
/**
 
83
 * dot specific paint device
 
84
 */
 
85
class DotPaintDevice : public QPaintDevice
 
86
{
 
87
public:
 
88
    DotPaintDevice() : m_engine(new DotPaintEngine)
 
89
    {
 
90
    }
 
91
 
 
92
    ~DotPaintDevice()
 
93
    {
 
94
        delete m_engine;
 
95
    }
 
96
 
 
97
    virtual QPaintEngine* paintEngine() const
 
98
    {
 
99
        return m_engine;
 
100
    }
 
101
 
 
102
    QStringList &data()
 
103
    {
 
104
        return m_engine->m_data;
 
105
    }
 
106
 
 
107
protected:
 
108
    virtual int metric(PaintDeviceMetric metric) const
 
109
    {
 
110
        switch(metric) {
 
111
            case QPaintDevice::PdmDpiX: return 1;
 
112
            case QPaintDevice::PdmDpiY: return 1;
 
113
            case QPaintDevice::PdmWidth: return 100;
 
114
            case QPaintDevice::PdmHeight: return 100;
 
115
            default: return 0;
 
116
        }
 
117
        return 0;
 
118
    }
 
119
 
 
120
    DotPaintEngine *m_engine;
 
121
};
 
122
 
 
123
#define DOTGENERATOR_DEBUG
 
124
/**
 
125
 * constructor
 
126
 */
 
127
DotGenerator::DotGenerator()
 
128
  : m_scale(72),
 
129
    m_usePosition(false),
 
130
    m_useFullNodeLabels(true)
 
131
{
 
132
}
 
133
 
 
134
/**
 
135
 * return usage of position attribute
 
136
 *
 
137
 * @return true if position are used
 
138
 */
 
139
bool DotGenerator::usePosition()
 
140
{
 
141
    return m_usePosition;
 
142
}
 
143
 
 
144
/**
 
145
 * set usage of position attribute in dot file
 
146
 *
 
147
 * @param state The new state
 
148
 */
 
149
void DotGenerator::setUsePosition(bool state)
 
150
{
 
151
    m_usePosition = state;
 
152
}
 
153
 
 
154
/**
 
155
 * return usage of full node labels
 
156
 *
 
157
 * @return true if position are used
 
158
 */
 
159
bool DotGenerator::useFullNodeLabels()
 
160
{
 
161
    return m_useFullNodeLabels;
 
162
}
 
163
 
 
164
/**
 
165
 * Set usage of full node labels.
 
166
 * When set to true labels are extracted from the
 
167
 * text output generated by the widget's paint method.
 
168
 *
 
169
 * @param state The new state
 
170
 */
 
171
void DotGenerator::setUseFullNodeLabels(bool state)
 
172
{
 
173
    m_useFullNodeLabels = state;
 
174
}
 
175
 
 
176
/**
 
177
 * Return a list of available templates for a given scene type
 
178
 *
 
179
 * @param scene The diagram
 
180
 * @param configFiles will contain the collected list of config files
 
181
 * @return true if collecting succeeds
 
182
 */
 
183
bool DotGenerator::availableConfigFiles(UMLScene *scene, QHash<QString,QString> &configFiles)
 
184
{
 
185
    QString diagramType = scene->type().toString().toLower();
 
186
    KStandardDirs dirs;
 
187
 
 
188
    QStringList fileNames = dirs.findAllResources("data", QString("umbrello/layouts/%1*.desktop").arg(diagramType));
 
189
    foreach(const QString &fileName, fileNames) {
 
190
        QFileInfo fi(fileName);
 
191
        QString baseName;
 
192
        if (fi.baseName().contains("-"))
 
193
            baseName = fi.baseName().remove(diagramType + "-");
 
194
        else if (fi.baseName() == diagramType)
 
195
            baseName = fi.baseName();
 
196
        else
 
197
            baseName = "default";
 
198
        KDesktopFile desktopFile(fileName);
 
199
        configFiles[baseName] = desktopFile.readName();
 
200
    }
 
201
    return true;
 
202
}
 
203
 
 
204
/**
 
205
 * Read a layout config file
 
206
 *
 
207
 * @param diagramType String identifing the diagram
 
208
 * @param variant String identifing the variant
 
209
 * @return true on success
 
210
 */
 
211
bool DotGenerator::readConfigFile(QString diagramType, const QString &variant)
 
212
{
 
213
    QStringList fileNames;
 
214
 
 
215
    if (!variant.isEmpty())
 
216
        fileNames << QString("%1-%2.desktop").arg(diagramType).arg(variant);
 
217
    fileNames << QString("%1-default.desktop").arg(diagramType);
 
218
    fileNames << "default.desktop";
 
219
 
 
220
    QString configFileName;
 
221
    foreach(const QString &fileName, fileNames) {
 
222
        configFileName = KStandardDirs::locate("data", QString("umbrello/layouts/%1").arg(fileName));
 
223
        if (!configFileName.isEmpty())
 
224
            break;
 
225
    }
 
226
 
 
227
    if (configFileName.isEmpty()) {
 
228
        uError() << "could not find layout config file name for diagram type" << diagramType << "and variant" << variant;
 
229
        return false;
 
230
    }
 
231
    uDebug() << "reading config file" << configFileName;
 
232
    m_configFileName = configFileName;
 
233
    KDesktopFile desktopFile(configFileName);
 
234
    KConfigGroup edgesAttributes(&desktopFile,"X-UMBRELLO-Dot-Edges");
 
235
    KConfigGroup nodesAttributes(&desktopFile,"X-UMBRELLO-Dot-Nodes");
 
236
    KConfigGroup attributes(&desktopFile,"X-UMBRELLO-Dot-Attributes");
 
237
    // settings are not needed by dotgenerator
 
238
    KConfigGroup settings(&desktopFile,"X-UMBRELLO-Dot-Settings");
 
239
 
 
240
    m_edgeParameters.clear();
 
241
    m_nodeParameters.clear();
 
242
    m_dotParameters.clear();
 
243
 
 
244
    foreach(const QString &key, attributes.keyList()) {
 
245
        QString value = attributes.readEntry(key);
 
246
        if (!value.isEmpty())
 
247
            m_dotParameters[key] = value;
 
248
    }
 
249
 
 
250
    foreach(const QString &key, nodesAttributes.keyList()) {
 
251
        QString value = nodesAttributes.readEntry(key);
 
252
        m_nodeParameters[key] = value;
 
253
    }
 
254
 
 
255
    foreach(const QString &key, edgesAttributes.keyList()) {
 
256
        QString value = edgesAttributes.readEntry(key);
 
257
        if (m_edgeParameters.contains(key)) {
 
258
            m_edgeParameters[key] += ',' + value;
 
259
        } else {
 
260
            m_edgeParameters[key] = value;
 
261
        }
 
262
    }
 
263
 
 
264
    QString value = settings.readEntry("origin");
 
265
    QStringList a = value.split(",");
 
266
    if (a.size() == 2)
 
267
        m_origin = QPointF(a[0].toDouble(), a[1].toDouble());
 
268
    else
 
269
        uError() << "illegal format of entry 'origin'" << value;
 
270
 
 
271
    m_generator = settings.readEntry("generator","dot");
 
272
 
 
273
#ifdef LAYOUTGENERATOR_DATA_DEBUG
 
274
    uDebug() << m_edgeParameters;
 
275
    uDebug() << m_nodeParameters;
 
276
    uDebug() << m_dotParameters;
 
277
#endif
 
278
    return true;
 
279
}
 
280
 
 
281
/**
 
282
 * Create dot file using displayed widgets
 
283
 * and associations of the provided scene
 
284
 * @note This method could also be used as a base to export diagrams as dot file
 
285
 *
 
286
 * @param fileName Filename where to create the dot file
 
287
 * @param scene The diagram from which the widget informations are fetched
 
288
 *
 
289
 * @return true if generating finished successfully
 
290
 */
 
291
bool DotGenerator::createDotFile(UMLScene *scene, const QString &fileName, const QString &variant)
 
292
{
 
293
    QFile file(fileName);
 
294
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
 
295
        return false;
 
296
 
 
297
    QString diagramType = scene->type().toString().toLower();
 
298
    if (!readConfigFile(diagramType, variant))
 
299
        return false;
 
300
 
 
301
    QString data;
 
302
    QTextStream out(&data);
 
303
 
 
304
    foreach(UMLWidget *widget, scene->widgetList()) {
 
305
        QStringList params;
 
306
 
 
307
        if (m_nodeParameters.contains("all"))
 
308
            params << m_nodeParameters["all"].split(',');
 
309
 
 
310
        if (usePosition())
 
311
            params  << QString("pos=\"%1,%2\"").arg(widget->x()+widget->width()/2).arg(widget->y()+widget->height()/2);
 
312
 
 
313
        QString type = QString(widget->baseTypeStr()).toLower().remove("wt_");
 
314
 
 
315
        if (type == "state") {
 
316
            StateWidget *w = static_cast<StateWidget *>(widget);
 
317
            type = w->stateTypeStr().toLower();
 
318
        }
 
319
        else if (type == "activity") {
 
320
            ActivityWidget *w = static_cast<ActivityWidget *>(widget);
 
321
            type = w->activityTypeStr().toLower();
 
322
        }
 
323
        else if (type == "signal") {
 
324
            SignalWidget *w = static_cast<SignalWidget *>(widget);
 
325
            type = w->signalTypeStr().toLower();
 
326
        }
 
327
 
 
328
        QString key = "type::" + type;
 
329
 
 
330
        QString label;
 
331
 
 
332
        if (!useFullNodeLabels())
 
333
            label = widget->name() + "\\n" + type;
 
334
        else {
 
335
            DotPaintDevice d;
 
336
            QPainter p(&d);
 
337
            widget->paint(p, 0, 0);
 
338
            label = d.data().join("\\n");
 
339
        }
 
340
    
 
341
        if (m_nodeParameters.contains(key))
 
342
            params << m_nodeParameters[key].split(',');
 
343
        else if (m_nodeParameters.contains("type::default"))
 
344
            params << m_nodeParameters["type::default"].split(',');
 
345
 
 
346
        if (!findItem(params,"label="))
 
347
            params << QString("label=\"%1\"").arg(label);
 
348
 
 
349
        if (!findItem(params,"width="))
 
350
            params << QString("width=\"%1\"").arg(widget->width()/m_scale);
 
351
 
 
352
        if (!findItem(params,"height="))
 
353
            params << QString("height=\"%1\"").arg(widget->height()/m_scale);
 
354
 
 
355
#ifdef DOTGENERATOR_DATA_DEBUG
 
356
        uDebug() << type << params;
 
357
#endif
 
358
        QString id = fixID(ID2STR(widget->id()));
 
359
        if (widget->baseType() != WidgetBase::wt_Text)
 
360
            out << "\"" << id << "\""
 
361
                << " [" << params.join(",") << "];\n";
 
362
    }
 
363
 
 
364
    foreach(AssociationWidget *assoc, scene->associationList()) {
 
365
        QString type = assoc->associationType().toString().toLower();
 
366
        QString key = "type::" + type;
 
367
        bool swapId = false;
 
368
 
 
369
        if (m_edgeParameters.contains("id::" + key))
 
370
            swapId = m_edgeParameters["id::" + key] == "swap";
 
371
        else if (m_edgeParameters.contains("id::type::default"))
 
372
            swapId = m_edgeParameters["id::type::default"] == "swap";
 
373
 
 
374
        QString label;
 
375
        if (!useFullNodeLabels())
 
376
            label = assoc->name() + "\\n" + type;
 
377
        else
 
378
            label = assoc->name();
 
379
 
 
380
        QString headLabel = assoc->roleName(swapId ? Uml::B : Uml::A);
 
381
        QString tailLabel = assoc->roleName(swapId ? Uml::A : Uml::B);
 
382
 
 
383
        if (!headLabel.isEmpty())
 
384
            headLabel.prepend("+");
 
385
        if (!tailLabel.isEmpty())
 
386
            tailLabel.prepend("+");
 
387
 
 
388
        headLabel += QLatin1String("  ") + assoc->multiplicity(swapId ? Uml::B : Uml::A);
 
389
        tailLabel += QLatin1String("  ") + assoc->multiplicity(swapId ? Uml::A : Uml::B);
 
390
 
 
391
        QString edgeParameters;
 
392
        QStringList params;
 
393
        QString rkey = QLatin1String("ranking::") + key;
 
394
        if (m_edgeParameters.contains(rkey))
 
395
            edgeParameters = m_edgeParameters[rkey];
 
396
        else if (m_edgeParameters.contains("ranking::type::default")) {
 
397
            edgeParameters = m_edgeParameters["ranking::type::default"];
 
398
        }
 
399
        params << edgeParameters.split(',');
 
400
 
 
401
        QString vkey = QLatin1String("visual::") + key;
 
402
        if (m_edgeParameters.contains(vkey))
 
403
            edgeParameters = m_edgeParameters[vkey];
 
404
        else if (m_edgeParameters.contains("visual::type::default")) {
 
405
            edgeParameters = m_edgeParameters["visual::type::default"];
 
406
        }
 
407
        params << edgeParameters.split(',');
 
408
 
 
409
        if (!findItem(params,"label="))
 
410
            params << QString("label=\"%1\"").arg(label);
 
411
 
 
412
        if (!findItem(params,"headlabel="))
 
413
            params << QString("headlabel=\"%1\"").arg(headLabel);
 
414
 
 
415
        if (!findItem(params,"taillabel="))
 
416
            params << QString("taillabel=\"%1\"").arg(tailLabel);
 
417
 
 
418
#ifdef DOTGENERATOR_DATA_DEBUG
 
419
        uDebug() << type << params;
 
420
#endif
 
421
        QString aID = fixID(ID2STR(assoc->widgetIDForRole(swapId ? Uml::A : Uml::B)));
 
422
        QString bID = fixID(ID2STR(assoc->widgetIDForRole(swapId ? Uml::B : Uml::A)));
 
423
 
 
424
        out << "\"" << aID << "\" -> \"" << bID << "\"" << " [" << params.join(",") << "];\n";
 
425
    }
 
426
 
 
427
    QTextStream o(&file);
 
428
    o << "# generated from " << m_configFileName << "\n";
 
429
    o << "digraph G {\n";
 
430
 
 
431
    foreach(const QString &key, m_dotParameters.keys()) {
 
432
        o << "\t" << key << " [" << m_dotParameters[key] << "];\n";
 
433
    }
 
434
 
 
435
    o << data << "\n";
 
436
    o << "}\n";
 
437
 
 
438
    return true;
 
439
}
 
440
 
 
441
/**
 
442
 * Find string starting with the search string in string list
 
443
 * @params params string list to search in
 
444
 * @params search string
 
445
 * @return true, when search string has been found
 
446
 */
 
447
bool DotGenerator::findItem(QStringList &params, const QString &search)
 
448
{
 
449
    foreach(const QString &s, params) {
 
450
        if (s.startsWith(search))
 
451
            return true;
 
452
    }
 
453
    return false;
 
454
}
 
455
 
 
456
/**
 
457
 * There are id wrapped with '"', remove it.
 
458
 */
 
459
QString DotGenerator::fixID(const QString &_id)
 
460
{
 
461
    // FIXME: some widget's ids returned from the list are wrapped with "\"", find and fix them
 
462
    QString id(_id);
 
463
    id.remove("\"");
 
464
    return id;
 
465
}
 
466
 
 
467
#if 0
 
468
static QDebug operator<<(QDebug out, LayoutGenerator &c)
 
469
{
 
470
    out << "LayoutGenerator:"
 
471
        << "m_boundingRect:" << c.m_boundingRect
 
472
        << "m_nodes:" << c.m_nodes
 
473
        << "m_edges:" << c.m_edges
 
474
        << "m_scale:" << c.m_scale
 
475
        << "m_executable:" << c.m_executable;
 
476
    return out;
 
477
}
 
478
#endif