classifierwidget.cpp 49.3 KB
Newer Older
1
2
3
4
5
6
/***************************************************************************
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
Oliver Kellogg's avatar
Oliver Kellogg committed
7
 *   copyright (C) 2004-2014                                               *
Ralf Habacker's avatar
Ralf Habacker committed
8
 *   Umbrello UML Modeller Authors <umbrello-devel@kde.org>                *
9
10
 ***************************************************************************/

11
// own header
12
#include "classifierwidget.h"
13

14
// app includes
15
#include "floatingtextwidget.h"
16
#include "associationwidget.h"
17
#include "associationline.h"
18
#include "classifier.h"
19
#include "cmds.h"
20
#include "debug_utils.h"
21
#include "instance.h"
22
23
#include "listpopupmenu.h"
#include "object_factory.h"
24
#include "operation.h"
25
#include "template.h"
26
#include "uml.h"
27
28
#include "umldoc.h"
#include "umlview.h"
29

30
// qt includes
31
#include <QPainter>
32

33
34
DEBUG_REGISTER_DISABLED(ClassifierWidget)

35
const int ClassifierWidget::CIRCLE_SIZE = 30;
36
const int ClassifierWidget::SOCKET_INCREMENT = 10;
37
38
39
40

/**
 * Constructs a ClassifierWidget.
 *
41
 * @param scene   The parent of this ClassifierWidget.
42
 * @param c       The UMLClassifier to represent.
43
 */
44
ClassifierWidget::ClassifierWidget(UMLScene * scene, UMLClassifier *c)
45
  : UMLWidget(scene, WidgetBase::wt_Class, c),
Ralf Habacker's avatar
Ralf Habacker committed
46
47
    m_pAssocWidget(0),
    m_pInterfaceName(0)
48
{
49
    const Settings::OptionState& ops = m_scene->optionState();
50
51
52
53
    setVisualPropertyCmd(ShowVisibility, ops.classState.showVisibility);
    setVisualPropertyCmd(ShowOperations, ops.classState.showOps);
    setVisualPropertyCmd(ShowPublicOnly, ops.classState.showPublicOnly);
    setVisualPropertyCmd(ShowPackage,    ops.classState.showPackage);
54
55
56
57
58
    m_attributeSignature = Uml::SignatureType::ShowSig;
    /*:TODO:
    setVisualProperty(ShowOperationSignature, ops.classState.showOpSig);
      Cannot do that because we get "pure virtual method called". Open code:
     */
59
    if(!ops.classState.showOpSig) {
60
61
62
63
64
65
66
67
68
69
        if (visualProperty(ShowVisibility))
            m_operationSignature = Uml::SignatureType::NoSig;
        else
            m_operationSignature = Uml::SignatureType::NoSigNoVis;

    } else if (visualProperty(ShowVisibility))
        m_operationSignature = Uml::SignatureType::ShowSig;
    else
        m_operationSignature = Uml::SignatureType::SigNoVis;

70
71
72
    setVisualPropertyCmd(ShowAttributes, ops.classState.showAtts);
    setVisualPropertyCmd(ShowStereotype, ops.classState.showStereoType);
    setVisualPropertyCmd(DrawAsCircle, false);
73

74
    setShowAttSigs(ops.classState.showAttSig);
75
76

    if (c && c->isInterface()) {
77
        setBaseType(WidgetBase::wt_Interface);
78
79
80
        m_visualProperties = ShowOperations | ShowVisibility | ShowStereotype;
        setShowStereotype(true);
        updateSignatureTypes();
Oliver Kellogg's avatar
Oliver Kellogg committed
81
    }
82

Ralf Habacker's avatar
Ralf Habacker committed
83
    if (c && scene->type() == Uml::DiagramType::Object) {
84
85
86
87
        setBaseType(WidgetBase::wt_Instance);
        m_visualProperties =  ShowAttributes;
        updateSignatureTypes();
    }
88
89
}

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
 * Constructs a ClassifierWidget.
 *
 * @param scene   The parent of this ClassifierWidget.
 * @param c       The UMLClassifier to represent.
 */
ClassifierWidget::ClassifierWidget(UMLScene * scene, UMLPackage *o)
  : UMLWidget(scene, WidgetBase::wt_Package, o),
    m_pAssocWidget(0),
    m_pInterfaceName(0)
{
    const Settings::OptionState& ops = m_scene->optionState();
    setVisualPropertyCmd(ShowVisibility, ops.classState.showVisibility);
    setVisualPropertyCmd(ShowOperations, ops.classState.showOps);
    setVisualPropertyCmd(ShowPublicOnly, ops.classState.showPublicOnly);
    setVisualPropertyCmd(ShowPackage,    ops.classState.showPackage);
    m_attributeSignature = Uml::SignatureType::ShowSig;

    if(!ops.classState.showOpSig) {
        if (visualProperty(ShowVisibility))
            m_operationSignature = Uml::SignatureType::NoSig;
        else
            m_operationSignature = Uml::SignatureType::NoSigNoVis;

    } else if (visualProperty(ShowVisibility))
        m_operationSignature = Uml::SignatureType::ShowSig;
    else
        m_operationSignature = Uml::SignatureType::SigNoVis;

    setVisualPropertyCmd(ShowAttributes, ops.classState.showAtts);
    setVisualPropertyCmd(ShowStereotype, ops.classState.showStereoType);
    setVisualPropertyCmd(DrawAsPackage, true);

    setShowAttSigs(ops.classState.showAttSig);
}

126
127
128
/**
 * Destructor.
 */
129
130
ClassifierWidget::~ClassifierWidget()
{
Oliver Kellogg's avatar
Oliver Kellogg committed
131
132
    if (m_pAssocWidget)
        m_pAssocWidget->removeAssocClassLine();
133
134
135
136
    if (m_pInterfaceName) {
        delete m_pInterfaceName;
        m_pInterfaceName = 0;
    }
137
}
138

139
/**
140
141
 * Return the UMLClassifier which this ClassifierWidget
 * represents.
142
 */
143
UMLClassifier *ClassifierWidget::classifier() const
144
{
145
    return m_umlObject->asUMLClassifier();
146
}
Oliver Kellogg's avatar
Oliver Kellogg committed
147

148
149
150
151
152
153
154
/**
 * @return the visual properties
 */
ClassifierWidget::VisualProperties ClassifierWidget::visualProperties() const
{
    return m_visualProperties;
}
Oliver Kellogg's avatar
Oliver Kellogg committed
155

156
157
158
159
160
161
162
163
164
165
/**
 * Set an OR combination of properties stored in \a properties on this
 * widget.
 */
void ClassifierWidget::setVisualProperties(VisualProperties properties)
{
    // Don't do anything if the argument is equal to current status.
    if (quint32(m_visualProperties) == quint32(properties)) {
        return;
    }
166

167
168
169
170
171
172
173
    m_visualProperties = properties;
    updateSignatureTypes();
}

/**
 * @return The status of the property passed in.
 *
174
175
 * @note Use @ref attributeSignature() and @ref
 *       operationSignature() to get signature status.  This
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
 *       method only indicates whether signature is visible or not.
 */
bool ClassifierWidget::visualProperty(VisualProperty property) const
{
    if (property == ShowAttributeSignature) {
        return (m_attributeSignature == Uml::SignatureType::ShowSig
                || m_attributeSignature == Uml::SignatureType::SigNoVis);
    }

    else if(property == ShowOperationSignature) {
        return (m_operationSignature == Uml::SignatureType::ShowSig
                || m_operationSignature == Uml::SignatureType::SigNoVis);
    }

    return m_visualProperties.testFlag(property);
}

/**
 * A convenient method to set and reset individual VisualProperty
 *
196
197
 * Undo command.
 *
198
199
200
201
202
203
204
 * @param property The property to be set/reset.
 * @param enable   True/false to set/reset. (default = true)
 *
 * @note This method handles ShowAttributeSignature and
 *       ShowOperationSignature specially.
 */
void ClassifierWidget::setVisualProperty(VisualProperty property, bool enable)
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
{
    if (visualProperty(property) != enable) {
        UMLApp::app()->executeCommand(new Uml::CmdChangeVisualProperty(this, property, enable));
    }
}

/**
 * A convenient method to set and reset individual VisualProperty
 *
 * @param property The property to be set/reset.
 * @param enable   True/false to set/reset. (default = true)
 *
 * @note This method handles ShowAttributeSignature and
 *       ShowOperationSignature specially.
 */
void ClassifierWidget::setVisualPropertyCmd(VisualProperty property, bool enable)
221
222
223
224
225
226
227
228
{
    // Handle ShowAttributeSignature and ShowOperationSignature
    // specially.

    if (property == ShowAttributeSignature) {
        if (!enable) {
            m_attributeSignature = visualProperty(ShowVisibility) ?
                Uml::SignatureType::NoSig : Uml::SignatureType::NoSigNoVis;
229
        } else {
230
231
232
233
234
235
236
237
238
239
240
            m_attributeSignature = visualProperty(ShowVisibility) ?
                Uml::SignatureType::ShowSig : Uml::SignatureType::SigNoVis;
        }
        //:TODO: updateTextItemGroups();
        updateSignatureTypes();
    }

    else if (property == ShowOperationSignature) {
        if (!enable) {
            m_operationSignature = visualProperty(ShowVisibility) ?
                Uml::SignatureType::NoSig : Uml::SignatureType::NoSigNoVis;
241
        } else {
242
243
244
245
246
247
248
249
250
251
252
            m_operationSignature = visualProperty(ShowVisibility) ?
                Uml::SignatureType::ShowSig : Uml::SignatureType::SigNoVis;
        }
        //:TODO: updateTextItemGroups();
        updateSignatureTypes();
    }

    else if (property == ShowStereotype) {
        // Now just update flag and use base method for actual work.
        if (enable) {
            m_visualProperties |= property;
253
        } else {
254
            m_visualProperties &= ~property;
255
256
257
258
        }
        setShowStereotype(enable);
    }

259
260
261
262
263
264
265
266
267
268
269
270
    else if (property == DrawAsCircle) {
        // Don't do anything if the flag status is same.
        if (visualProperty(property) == enable)
            return;
        if (enable) {
            m_visualProperties |= property;
        } else {
            m_visualProperties &= ~property;
        }
        setDrawAsCircle(enable);
    }

271
272
273
274
275
276
277
278
279
280
    // Some other flag.
    else {
        // Don't do anything if the flag status is same.
        if (visualProperty(property) == enable) {
            return;
        }

        // Call setVisualProperties appropriately based on enbable.
        if (enable) {
            setVisualProperties(visualProperties() | property);
281
        } else {
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
            setVisualProperties(visualProperties() & ~property);
        }
    }
}

/**
 * A convenient method to toggle individual VisualProperty of this
 * widget.
 *
 * @param property The property to be toggled.
 *
 * @note This method handles ShowAttributeSignature and
 *       ShowOperationSignature specially.
 */
void ClassifierWidget::toggleVisualProperty(VisualProperty property)
{
    bool oppositeStatus;
    if (property == ShowOperationSignature) {
        oppositeStatus = !(m_operationSignature == Uml::SignatureType::ShowSig
                           || m_operationSignature == Uml::SignatureType::SigNoVis);
    }
    else if (property == ShowAttributeSignature) {
        oppositeStatus = !(m_attributeSignature == Uml::SignatureType::ShowSig
                           || m_attributeSignature == Uml::SignatureType::SigNoVis);
    }
    else {
        oppositeStatus = !visualProperty(property);
    }

    DEBUG(DBG_SRC) << "VisualProperty: " << property << " to opposite status " << oppositeStatus;
    setVisualProperty(property, oppositeStatus);
313
314
}

315
/**
316
 * Updates m_operationSignature to match m_showVisibility.
317
 */
318
void ClassifierWidget::updateSignatureTypes()
319
{
Oliver Kellogg's avatar
Oliver Kellogg committed
320
    //turn on scope
321
322
323
324
325
    if (visualProperty(ShowVisibility)) {
        if (m_operationSignature == Uml::SignatureType::NoSigNoVis) {
            m_operationSignature = Uml::SignatureType::NoSig;
        } else if (m_operationSignature == Uml::SignatureType::SigNoVis) {
            m_operationSignature = Uml::SignatureType::ShowSig;
Oliver Kellogg's avatar
Oliver Kellogg committed
326
        }
327
328
329
330
331
332
333
    }
    //turn off scope
    else {
        if (m_operationSignature == Uml::SignatureType::ShowSig) {
            m_operationSignature = Uml::SignatureType::SigNoVis;
        } else if (m_operationSignature == Uml::SignatureType::NoSig) {
            m_operationSignature = Uml::SignatureType::NoSigNoVis;
Oliver Kellogg's avatar
Oliver Kellogg committed
334
335
        }
    }
336
337
338
339
340
    if (visualProperty(ShowVisibility)) {
        if (m_attributeSignature == Uml::SignatureType::NoSigNoVis)
            m_attributeSignature = Uml::SignatureType::NoSig;
        else if (m_attributeSignature == Uml::SignatureType::SigNoVis)
            m_attributeSignature = Uml::SignatureType::ShowSig;
Oliver Kellogg's avatar
Oliver Kellogg committed
341
    } else {
342
343
344
345
        if (m_attributeSignature == Uml::SignatureType::ShowSig)
            m_attributeSignature = Uml::SignatureType::SigNoVis;
        else if(m_attributeSignature == Uml::SignatureType::NoSig)
            m_attributeSignature = Uml::SignatureType::NoSigNoVis;
Oliver Kellogg's avatar
Oliver Kellogg committed
346
    }
347
    updateGeometry();
Oliver Kellogg's avatar
Oliver Kellogg committed
348
    update();
349
350
}

351
/**
352
 * Returns whether to show attribute signatures.
353
 * Only applies when m_umlObject->getBaseType() is ot_Class.
354
 *
355
 * @return  Status of how attribute signatures are shown.
356
 */
357
Uml::SignatureType::Enum ClassifierWidget::attributeSignature() const
358
{
359
    return m_attributeSignature;
360
361
}

362
/**
363
 * Sets the type of signature to display for an attribute.
364
 * Only applies when m_umlObject->getBaseType() is ot_Class.
365
 *
366
 * @param sig   Type of signature to display for an attribute.
367
 */
368
void ClassifierWidget::setAttributeSignature(Uml::SignatureType::Enum sig)
369
{
370
371
    m_attributeSignature = sig;
    updateSignatureTypes();
372
    updateGeometry();
Oliver Kellogg's avatar
Oliver Kellogg committed
373
    update();
374
375
}

376
/**
377
 * @return The Uml::SignatureType::Enum value for the operations.
378
 */
379
Uml::SignatureType::Enum ClassifierWidget::operationSignature() const
380
381
382
383
384
385
{
    return m_operationSignature;
}

/**
 * Set the type of signature to display for an Operation
386
 *
387
 * @param sig   Type of signature to display for an operation.
388
 */
389
void ClassifierWidget::setOperationSignature(Uml::SignatureType::Enum sig)
390
{
391
392
    m_operationSignature = sig;
    updateSignatureTypes();
393
    updateGeometry();
Oliver Kellogg's avatar
Oliver Kellogg committed
394
    update();
395
396
}

397
398
/**
 * Sets whether to show attribute signature
399
 * Only applies when m_umlObject->getBaseType() is ot_Class.
400
 *
401
 * @param _status  True if attribute signatures shall be shown.
402
 */
403
404
void ClassifierWidget::setShowAttSigs(bool _status)
{
405
    if(!_status) {
406
407
        if (visualProperty(ShowVisibility))
            m_attributeSignature = Uml::SignatureType::NoSig;
Oliver Kellogg's avatar
Oliver Kellogg committed
408
        else
409
            m_attributeSignature = Uml::SignatureType::NoSigNoVis;
Oliver Kellogg's avatar
Oliver Kellogg committed
410
    }
411
412
    else if (visualProperty(ShowVisibility))
        m_attributeSignature = Uml::SignatureType::ShowSig;
Oliver Kellogg's avatar
Oliver Kellogg committed
413
    else
414
        m_attributeSignature = Uml::SignatureType::SigNoVis;
415
    if (UMLApp::app()->document()->loading())
416
        return;
417
    updateGeometry();
Oliver Kellogg's avatar
Oliver Kellogg committed
418
    update();
419
420
}

421
422
/**
 * Toggles whether to show attribute signatures.
423
 * Only applies when m_umlObject->getBaseType() is ot_Class.
424
 */
425
426
void ClassifierWidget::toggleShowAttSigs()
{
427
428
429
430
    if (m_attributeSignature == Uml::SignatureType::ShowSig ||
            m_attributeSignature == Uml::SignatureType::SigNoVis) {
        if (visualProperty(ShowVisibility)) {
            m_attributeSignature = Uml::SignatureType::NoSig;
Oliver Kellogg's avatar
Oliver Kellogg committed
431
        } else {
432
            m_attributeSignature = Uml::SignatureType::NoSigNoVis;
Oliver Kellogg's avatar
Oliver Kellogg committed
433
        }
434
435
    } else if (visualProperty(ShowVisibility)) {
        m_attributeSignature = Uml::SignatureType::ShowSig;
Oliver Kellogg's avatar
Oliver Kellogg committed
436
    } else {
437
        m_attributeSignature = Uml::SignatureType::SigNoVis;
Oliver Kellogg's avatar
Oliver Kellogg committed
438
    }
439
    updateGeometry();
Oliver Kellogg's avatar
Oliver Kellogg committed
440
    update();
441
442
}

443
444
445
446
/**
 * Return the number of displayed members of the given ObjectType.
 * Takes into consideration m_showPublicOnly but not other settings.
 */
447
int ClassifierWidget::displayedMembers(UMLObject::ObjectType ot) const
448
{
Oliver Kellogg's avatar
Oliver Kellogg committed
449
    int count = 0;
450
451
    UMLClassifier *umlc = this->classifier();
    if (!umlc)
Ralf Habacker's avatar
Ralf Habacker committed
452
        return count;
453
    UMLClassifierListItemList list = umlc->getFilteredList(ot);
Ralf Habacker's avatar
Ralf Habacker committed
454
    foreach (UMLClassifierListItem *m, list) {
455
      if (!(visualProperty(ShowPublicOnly) && m->visibility() != Uml::Visibility::Public))
Oliver Kellogg's avatar
Oliver Kellogg committed
456
457
458
            count++;
    }
    return count;
459
460
}

461
462
463
/**
 * Overrides method from UMLWidget.
 */
464
465
466
467
468
469
470
471
472
473
QSizeF ClassifierWidget::minimumSize() const
{
    return calculateSize();
}

/**
 * Calculate content related size of widget.
 * Overrides method from UMLWidget.
 */
QSizeF ClassifierWidget::calculateSize(bool withExtensions /* = true */) const
474
{
475
    if (!m_umlObject) {
476
        return UMLWidget::minimumSize();
477
    }
478
    if (m_umlObject->baseType() == UMLObject::ot_Package) {
479
480
        return calculateAsPackageSize();
    }
481
482
483
484
485
486
487
488
    UMLClassifier *umlc = this->classifier();
    if (!umlc) {
        uError() << "Internal error - classifier() returns NULL";
        return UMLWidget::minimumSize();
    }
    if (umlc->isInterface() && visualProperty(DrawAsCircle)) {
        return calculateAsCircleSize();
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
489

490
491
492
493
    const bool showNameOnly = !visualProperty(ShowAttributes) &&
                              !visualProperty(ShowOperations) &&
                              !visualProperty(ShowDocumentation);

Oliver Kellogg's avatar
Oliver Kellogg committed
494
495
    const QFontMetrics &fm = getFontMetrics(UMLWidget::FT_NORMAL);
    const int fontHeight = fm.lineSpacing();
496
    // width is the width of the longest 'word'
Oliver Kellogg's avatar
Oliver Kellogg committed
497
498
    int width = 0, height = 0;
    // consider stereotype
499
    if (visualProperty(ShowStereotype) && !m_umlObject->stereotype().isEmpty()) {
Oliver Kellogg's avatar
Oliver Kellogg committed
500
501
502
        height += fontHeight;
        // ... width
        const QFontMetrics &bfm = UMLWidget::getFontMetrics(UMLWidget::FT_BOLD);
Ralf Habacker's avatar
Ralf Habacker committed
503
        const int stereoWidth = bfm.size(0, m_umlObject->stereotype(true)).width();
Oliver Kellogg's avatar
Oliver Kellogg committed
504
505
        if (stereoWidth > width)
            width = stereoWidth;
506
    } else if (showNameOnly) {
507
        height += defaultMargin;
Oliver Kellogg's avatar
Oliver Kellogg committed
508
509
510
511
512
    }

    // consider name
    height += fontHeight;
    // ... width
513
    QString name;
514
    if (visualProperty(ShowPackage))
515
        name = m_umlObject->fullyQualifiedName();
Oliver Kellogg's avatar
Oliver Kellogg committed
516
    else
517
518
519
520
        name = m_umlObject->name();

    QString displayedName;
    if (m_umlObject->isUMLInstance())
521
        displayedName = m_umlObject->asUMLInstance()->instanceName() + QLatin1String(" : ") + name;
522
523
524
    else
        displayedName = name;

525
    const UMLWidget::FontType nft = (m_umlObject->isAbstract() ? FT_BOLD_ITALIC : FT_BOLD);
Ralf Habacker's avatar
Ralf Habacker committed
526
    const int nameWidth = UMLWidget::getFontMetrics(nft).size(0, displayedName).width();
Oliver Kellogg's avatar
Oliver Kellogg committed
527
528
529
    if (nameWidth > width)
        width = nameWidth;

530
531
#ifdef ENABLE_WIDGET_SHOW_DOC
    // consider documentation
532
533
    if (visualProperty(ShowDocumentation)) {
        if (!documentation().isEmpty()) {
534
            QRect brect = fm.boundingRect(QRect(0, 0, this->width()-2*defaultMargin, this->height()-height), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, documentation());
535
536
537
538
539
540
541
542
            height += brect.height();
            if (!visualProperty(ShowOperations) && !visualProperty(ShowAttributes)) {
                if (brect.width() >= width)
                    width = brect.width();
            }
        }
        else
            height += fontHeight / 2;
543
544
545
    }
#endif

Oliver Kellogg's avatar
Oliver Kellogg committed
546
    // consider attributes
547
548
549
550
551
    if (visualProperty(ShowAttributes)) {
        const int numAtts = displayedAttributes();
        if (numAtts > 0) {
            height += fontHeight * numAtts;
            // calculate width of the attributes
552
553
            UMLClassifierListItemList list = umlc->getFilteredList(
                        m_umlObject->isUMLInstance() ? UMLObject::ot_InstanceAttribute : UMLObject::ot_Attribute);
554
555
556
            foreach (UMLClassifierListItem *a, list) {
                if (visualProperty(ShowPublicOnly) && a->visibility() != Uml::Visibility::Public)
                    continue;
557
                const int attWidth = fm.size(0, a->toString(m_attributeSignature, visualProperty(ShowStereotype))).width();
558
559
560
                if (attWidth > width)
                    width = attWidth;
            }
Oliver Kellogg's avatar
Oliver Kellogg committed
561
        }
562
563
        else
            height += fontHeight / 2;
Oliver Kellogg's avatar
Oliver Kellogg committed
564
565
566
    }

    // consider operations
567
568
569
570
571
572
573
574
575
    if (visualProperty(ShowOperations)) {
        const int numOps = displayedOperations();
        if (numOps > 0) {
            height += numOps * fontHeight;
            // ... width
            UMLOperationList list(umlc->getOpList());
            foreach (UMLOperation* op,  list) {
                      if (visualProperty(ShowPublicOnly) && op->visibility() != Uml::Visibility::Public)
                    continue;
576
                const QString displayedOp = op->toString(m_operationSignature, visualProperty(ShowStereotype));
577
578
579
580
581
582
                UMLWidget::FontType oft;
                oft = (op->isAbstract() ? UMLWidget::FT_ITALIC : UMLWidget::FT_NORMAL);
                const int w = UMLWidget::getFontMetrics(oft).size(0, displayedOp).width();
                if (w > width)
                    width = w;
            }
Oliver Kellogg's avatar
Oliver Kellogg committed
583
        }
584
585
        else
            height += fontHeight / 2;
Oliver Kellogg's avatar
Oliver Kellogg committed
586
587
    }

588
589
590
591
592
593
594
595
    if (withExtensions) {
        // consider template box _as last_ !
        QSize templatesBoxSize = calculateTemplatesBoxSize();
        if (templatesBoxSize.width() != 0) {
            // add width to largest 'word'
            width += templatesBoxSize.width() / 2;
        }
        if (templatesBoxSize.height() != 0) {
596
            height += templatesBoxSize.height() - defaultMargin;
597
        }
598
599
    }

Oliver Kellogg's avatar
Oliver Kellogg committed
600
    // allow for height margin
601
    if (showNameOnly) {
602
        height += defaultMargin;
Oliver Kellogg's avatar
Oliver Kellogg committed
603
604
605
    }

    // allow for width margin
606
    width += defaultMargin * 2;
Oliver Kellogg's avatar
Oliver Kellogg committed
607

608
    return QSizeF(width, height);
609
610
}

611
612
/**
 * Calculcates the size of the templates box in the top left
Ralf Habacker's avatar
Ralf Habacker committed
613
 * if it exists, returns QSize(0, 0) if it doesn't.
614
615
616
 *
 * @return  QSize of the templates flap.
 */
617
QSize ClassifierWidget::calculateTemplatesBoxSize() const
618
{
619
620
    if (!classifier())
        return QSize(0, 0);
621
    UMLTemplateList list = classifier()->getTemplateList();
Oliver Kellogg's avatar
Oliver Kellogg committed
622
623
624
625
    int count = list.count();
    if (count == 0) {
        return QSize(0, 0);
    }
626

627
    QFont font = UMLWidget::font();
Oliver Kellogg's avatar
Oliver Kellogg committed
628
629
630
    font.setItalic(false);
    font.setUnderline(false);
    font.setBold(false);
631
    const QFontMetrics fm(font);
632

Ralf Habacker's avatar
Ralf Habacker committed
633
    int width = 0;
634
    int height = count * fm.lineSpacing() + (defaultMargin*2);
635

Ralf Habacker's avatar
Ralf Habacker committed
636
    foreach (UMLTemplate *t, list) {
637
        int textWidth = fm.size(0, t->toString(Uml::SignatureType::NoSig, visualProperty(ShowStereotype))).width();
Oliver Kellogg's avatar
Oliver Kellogg committed
638
639
640
        if (textWidth > width)
            width = textWidth;
    }
641

642
    width += (defaultMargin*2);
Oliver Kellogg's avatar
Oliver Kellogg committed
643
    return QSize(width, height);
644
645
}

646
647
648
/**
 * Return the number of displayed attributes.
 */
649
int ClassifierWidget::displayedAttributes() const
650
{
651
    if (!visualProperty(ShowAttributes))
Oliver Kellogg's avatar
Oliver Kellogg committed
652
        return 0;
653
654
655
    if(baseType() == WidgetBase::wt_Instance)
        return displayedMembers(UMLObject::ot_InstanceAttribute);
    else
656
    return displayedMembers(UMLObject::ot_Attribute);
657
658
}

659
660
661
/**
 * Return the number of displayed operations.
 */
662
int ClassifierWidget::displayedOperations() const
663
664
665
666
667
668
{
    if (!visualProperty(ShowOperations))
        return 0;
    return displayedMembers(UMLObject::ot_Operation);
}

669
670
671
672
673
/**
 * Set the AssociationWidget when this ClassWidget acts as
 * an association class.
 */
void ClassifierWidget::setClassAssociationWidget(AssociationWidget *assocwidget)
674
{
675
676
677
678
    if (!classifier()) {
        uError() << "Class association cannot be applied to package";
        return;
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
679
    m_pAssocWidget = assocwidget;
680
    UMLAssociation *umlassoc = 0;
Oliver Kellogg's avatar
Oliver Kellogg committed
681
    if (assocwidget)
682
        umlassoc = assocwidget->association();
683
    classifier()->setClassAssoc(umlassoc);
684
685
}

686
687
688
689
/**
 * Return the AssociationWidget when this classifier acts as
 * an association class (else return NULL.)
 */
690
AssociationWidget *ClassifierWidget::classAssociationWidget() const
691
{
Oliver Kellogg's avatar
Oliver Kellogg committed
692
    return m_pAssocWidget;
693
694
}

695
696
697
/**
 * Overrides standard method.
 * Auxiliary to reimplementations in the derived classes.
698
 * @NOTE keep fetching attributes in sync with calculateSize()
699
 */
700
void ClassifierWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
701
{
702
703
704
    Q_UNUSED(option);
    Q_UNUSED(widget);
    setPenFromSettings(painter);
705
706
    if (UMLWidget::useFillColor())
        painter->setBrush(UMLWidget::fillColor());
707
708
709
    else {
        painter->setBrush(m_scene->backgroundColor());
    }
710
711
    if (m_umlObject->baseType() == UMLObject::ot_Package) {
        drawAsPackage(painter, option);
712
        UMLWidget::paint(painter, option, widget);
Oliver Kellogg's avatar
Oliver Kellogg committed
713
714
        return;
    }
715
716
717
718
719
720
721
    UMLClassifier *umlc = this->classifier();
    if (!umlc) {
        uError() << "Internal error - classifier() returns NULL";
        return;
    }
    if (umlc->isInterface() && visualProperty(DrawAsCircle)) {
        drawAsCircle(painter, option);
722
        UMLWidget::paint(painter, option, widget);
723
724
        return;
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
725

726
    // Draw the bounding rectangle
Oliver Kellogg's avatar
Oliver Kellogg committed
727
    QSize templatesBoxSize = calculateTemplatesBoxSize();
728
    int bodyOffsetY = 0;
Oliver Kellogg's avatar
Oliver Kellogg committed
729
    if (templatesBoxSize.height() > 0)
730
        bodyOffsetY += templatesBoxSize.height() - defaultMargin;
Oliver Kellogg's avatar
Oliver Kellogg committed
731
732
733
734
735
    int w = width();
    if (templatesBoxSize.width() > 0)
        w -= templatesBoxSize.width() / 2;
    int h = height();
    if (templatesBoxSize.height() > 0)
736
        h -= templatesBoxSize.height() - defaultMargin;
737
    painter->drawRect(0, bodyOffsetY, w, h);
Oliver Kellogg's avatar
Oliver Kellogg committed
738

739
    QFont font = UMLWidget::font();
Oliver Kellogg's avatar
Oliver Kellogg committed
740
741
    font.setUnderline(false);
    font.setItalic(false);
742
    const QFontMetrics &fm = UMLWidget::getFontMetrics(UMLWidget::FT_NORMAL);
Oliver Kellogg's avatar
Oliver Kellogg committed
743
744
745
    const int fontHeight = fm.lineSpacing();

    //If there are any templates then draw them
746
    UMLTemplateList tlist = umlc->getTemplateList();
747
    if (tlist.count() > 0) {
748
749
        setPenFromSettings(painter);
        QPen pen = painter->pen();
750
        pen.setStyle(Qt::DotLine);
751
752
        painter->setPen(pen);
        painter->drawRect(width() - templatesBoxSize.width(), 0,
753
754
                    templatesBoxSize.width(), templatesBoxSize.height());
        painter->setPen(QPen(textColor()));
Oliver Kellogg's avatar
Oliver Kellogg committed
755
        font.setBold(false);
756
        painter->setFont(font);
757
758
        const int x = width() - templatesBoxSize.width() + defaultMargin;
        int y = defaultMargin;
Ralf Habacker's avatar
Ralf Habacker committed
759
        foreach (UMLTemplate *t, tlist) {
760
            QString text = t->toString(Uml::SignatureType::NoSig, visualProperty(ShowStereotype));
Ralf Habacker's avatar
Ralf Habacker committed
761
            painter->drawText(x, y, fm.size(0, text).width(), fontHeight, Qt::AlignVCenter, text);
Oliver Kellogg's avatar
Oliver Kellogg committed
762
763
764
765
            y += fontHeight;
        }
    }

766
767
    const int textX = defaultMargin;
    const int textWidth = w - defaultMargin * 2;
Oliver Kellogg's avatar
Oliver Kellogg committed
768

769
    painter->setPen(QPen(textColor()));
Oliver Kellogg's avatar
Oliver Kellogg committed
770
771
772

    // draw stereotype
    font.setBold(true);
773
774
775
    const bool showNameOnly = !visualProperty(ShowAttributes) &&
                              !visualProperty(ShowOperations) &&
                              !visualProperty(ShowDocumentation);
776

Oliver Kellogg's avatar
Oliver Kellogg committed
777
    int nameHeight = fontHeight;
778
    if (visualProperty(ShowStereotype) && !m_umlObject->stereotype().isEmpty()) {
779
        painter->setFont(font);
780
        painter->drawText(textX, bodyOffsetY, textWidth, fontHeight, Qt::AlignCenter, m_umlObject->stereotype(true));
781
        bodyOffsetY += fontHeight;
782
783
    } else if (showNameOnly) {
        nameHeight = h;
Oliver Kellogg's avatar
Oliver Kellogg committed
784
785
786
787
    }

    // draw name
    QString name;
788
    if (visualProperty(ShowPackage)) {
789
        name = m_umlObject->fullyQualifiedName();
Oliver Kellogg's avatar
Oliver Kellogg committed
790
    } else {
791
        name = m_umlObject->name();
Oliver Kellogg's avatar
Oliver Kellogg committed
792
    }
793
794
795

    QString displayedName;
    if (m_umlObject->isUMLInstance())
796
        displayedName = m_umlObject->asUMLInstance()->instanceName() + QLatin1String(" : ") + name;
797
798
    else
        displayedName = name;
799

800
    font.setItalic(m_umlObject->isAbstract());
801
    painter->setFont(font);
802
    painter->drawText(textX, bodyOffsetY, textWidth, nameHeight, Qt::AlignCenter, displayedName);
803
    bodyOffsetY += fontHeight;
Oliver Kellogg's avatar
Oliver Kellogg committed
804
805
    font.setBold(false);
    font.setItalic(false);
806
    painter->setFont(font);
Oliver Kellogg's avatar
Oliver Kellogg committed
807

808
809
#ifdef ENABLE_WIDGET_SHOW_DOC
    // draw documentation
810
811
812
813
814
815
    if (visualProperty(ShowDocumentation)) {
        setPenFromSettings(painter);
        painter->drawLine(0, bodyOffsetY, w, bodyOffsetY);
        painter->setPen(textColor());

        if (!documentation().isEmpty()) {
816
817
818
            QRect brect = fm.boundingRect(QRect(0, 0, w-2*defaultMargin, h-bodyOffsetY), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, documentation());
            if (brect.width() > width() + 2*defaultMargin)
                brect.setWidth(width()-2*defaultMargin);
819
820
821
            brect.adjust(textX, bodyOffsetY, textX, bodyOffsetY);
            painter->drawText(brect, Qt::AlignCenter | Qt::TextWordWrap, documentation());
            bodyOffsetY += brect.height();
822
        }
823
824
        else
            bodyOffsetY += fontHeight / 2;
825
826
827
    }
#endif
    // draw attributes
828
829
830
831
832
    if (visualProperty(ShowAttributes)) {
        // draw dividing line between doc/name and attributes
        setPenFromSettings(painter);
        painter->drawLine(0, bodyOffsetY, w, bodyOffsetY);
        painter->setPen(textColor());
Oliver Kellogg's avatar
Oliver Kellogg committed
833

834
835
        const int numAtts = displayedAttributes();
        if (numAtts > 0) {
836
837
838
839
840
841
842
            if (baseType() == WidgetBase::wt_Instance) {
                drawMembers(painter, UMLObject::ot_InstanceAttribute, m_attributeSignature, textX,
                            bodyOffsetY, fontHeight);
            } else {
                drawMembers(painter, UMLObject::ot_Attribute, m_attributeSignature, textX,
                            bodyOffsetY, fontHeight);
            }
843
            bodyOffsetY += fontHeight * numAtts;
844
        }
845
846
        else
            bodyOffsetY += fontHeight / 2;
Oliver Kellogg's avatar
Oliver Kellogg committed
847
848
849
    }

    // draw operations
850
851
852
853
854
855
856
857
858
859
860
    if (visualProperty(ShowOperations)) {
        // draw dividing line between attributes and operations
        setPenFromSettings(painter);
        painter->drawLine(0, bodyOffsetY, w, bodyOffsetY);
        painter->setPen(QPen(textColor()));

        const int numOps = displayedOperations();
        if (numOps >= 0) {
            drawMembers(painter, UMLObject::ot_Operation, m_operationSignature, textX,
                        bodyOffsetY, fontHeight);
        }
Oliver Kellogg's avatar
Oliver Kellogg committed
861
862
    }

863
    UMLWidget::paint(painter, option, widget);
864
865
}

866
867
868
869
870
/**
 * @return The shape of the ClassifierWidget.
 */
QPainterPath ClassifierWidget::shape() const
{
871
    QPainterPath path;
872
    if (classifier() && classifier()->isInterface() && visualProperty(DrawAsCircle)) {
873
        path.addEllipse(rect());
874
875
        return path;
    }
876
877
878
#ifdef ENABLE_WIDGET_SHOW_DOC
    QSizeF mainSize = rect().size();
#else
879
    QSizeF mainSize = calculateSize(false);
880
#endif
881
882
883
    QSize templatesBoxSize = calculateTemplatesBoxSize();
    qreal mainY = 0.0;
    if (templatesBoxSize.height() > 0) {
884
        mainY += templatesBoxSize.height() - defaultMargin;
885
886
887
888
889
        path.addRect(QRectF(mainSize.width() - templatesBoxSize.width() / 2, 0.0,
                            templatesBoxSize.width(), templatesBoxSize.height()));
    }
    path.addRect(QRectF(0.0, mainY, mainSize.width(), mainSize.height()));
    return path;
890
891
}

892
/**
893
 * Draws the interface as a circle.
894
 * Only applies when m_umlObject->getBaseType() is ot_Interface.
895
 */
896
void ClassifierWidget::drawAsCircle(QPainter *painter, const QStyleOptionGraphicsItem *option)
897
{
898
899
900
    const int w = width();

    if (m_Assocs.size() > 1) {
901
        painter->drawEllipse(w/2 - CIRCLE_SIZE/2, SOCKET_INCREMENT / 2, CIRCLE_SIZE, CIRCLE_SIZE);
902
903
904
        // Draw socket for required interface.
        const qreal angleSpan = 180;   // 360.0 / (m_Assocs.size() + 1.0);
        const int arcDiameter = CIRCLE_SIZE + SOCKET_INCREMENT;
905
        QRect requireArc(w/2 - arcDiameter/2, 0, arcDiameter, arcDiameter);
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
        const QPointF center(x() + w/2, y() + arcDiameter/2);
        const qreal cX = center.x();
        const qreal cY = center.y();
        foreach (AssociationWidget *aw, m_Assocs) {
            const Uml::AssociationType::Enum aType = aw->associationType();
            if (aType == Uml::AssociationType::UniAssociation ||
                   aType == Uml::AssociationType::Association)  // provider
                continue;
            UMLWidget *otherEnd = aw->widgetForRole(Uml::RoleType::A);
            const WidgetBase::WidgetType oType = otherEnd->baseType();
            if (oType != WidgetBase::wt_Component && oType != WidgetBase::wt_Port)
                continue;

            AssociationLine *assocLine = aw->associationLine();
            const QPointF p(assocLine->endPoint());
            const qreal tolerance = 18.0;
            bool drawArc = true;
            qreal midAngle;
            if (p.x() < cX - tolerance) {
                if (p.y() < cY - tolerance)
                    midAngle = 135;
                else if (p.y() > cY + tolerance)
                    midAngle = 225;
                else
                    midAngle = 180;
            } else if (p.x() > cX + tolerance) {
                if (p.y() < cY - tolerance)
                    midAngle = 45;
                else if (p.y() > cY + tolerance)
                    midAngle = 315;
                else
                    midAngle = 0;
            } else {
                if (p.y() < cY - tolerance)
                    midAngle = 90;
                else if (p.y() > cY + tolerance)
                    midAngle = 270;
                else
                    drawArc = false;
            }
            if (drawArc) {
947
948
949
                // uDebug() << "number of assocs: " << m_Assocs.size()
                //          << ", p: " << p << ", center: " << center
                //          << ", midAngle: " << midAngle << ", angleSpan: " << angleSpan;
950
951
952
953
954
955
956
                painter->drawArc(requireArc, 16 * (midAngle - angleSpan/2), 16 * angleSpan);
            } else {
                uError() << "socket: assocLine endPoint " << p
                         << " too close to own center" << center;
            }
        }
    }
957
958
    else
        painter->drawEllipse(w/2 - CIRCLE_SIZE/2, 0, CIRCLE_SIZE, CIRCLE_SIZE);
959

960
    UMLWidget::paint(painter, option);
961
962
}

963
964
/**
 * Calculates the size of the object when drawn as a circle.
965
 * Only applies when m_umlObject->getBaseType() is ot_Interface.
966
 */
967
QSize ClassifierWidget::calculateAsCircleSize() const
968
{
969
970
971
972
    int circleSize = CIRCLE_SIZE;
    if (m_Assocs.size() > 1)
        circleSize += SOCKET_INCREMENT;
    return QSize(circleSize, circleSize);
973
974
}

975
976
void ClassifierWidget::drawAsPackage(QPainter *painter, const QStyleOptionGraphicsItem *option)
{
Ralf Habacker's avatar
Ralf Habacker committed
977
978
    Q_UNUSED(option);

979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997