classifierwidget.cpp 49.9 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 515 516 517 518 519 520 521
    UMLObject *o;
    if (m_umlObject && m_umlObject->isUMLInstance() && m_umlObject->asUMLInstance()->classifier())
        o = m_umlObject->asUMLInstance()->classifier();
    else
        o = m_umlObject;
    if (!o)
        name = m_Text;
    else
522
    if (visualProperty(ShowPackage))
523
        name = o->fullyQualifiedName();
Oliver Kellogg's avatar
Oliver Kellogg committed
524
    else
525
        name = o->name();
526 527 528

    QString displayedName;
    if (m_umlObject->isUMLInstance())
529
        displayedName = m_umlObject->name() + QLatin1String(" : ") + name;
530 531 532
    else
        displayedName = name;

533
    const UMLWidget::FontType nft = (m_umlObject->isAbstract() ? FT_BOLD_ITALIC : FT_BOLD);
Ralf Habacker's avatar
Ralf Habacker committed
534
    const int nameWidth = UMLWidget::getFontMetrics(nft).size(0, displayedName).width();
Oliver Kellogg's avatar
Oliver Kellogg committed
535 536 537
    if (nameWidth > width)
        width = nameWidth;

538 539
#ifdef ENABLE_WIDGET_SHOW_DOC
    // consider documentation
540 541
    if (visualProperty(ShowDocumentation)) {
        if (!documentation().isEmpty()) {
542
            QRect brect = fm.boundingRect(QRect(0, 0, this->width()-2*defaultMargin, this->height()-height), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, documentation());
543 544 545 546 547 548 549 550
            height += brect.height();
            if (!visualProperty(ShowOperations) && !visualProperty(ShowAttributes)) {
                if (brect.width() >= width)
                    width = brect.width();
            }
        }
        else
            height += fontHeight / 2;
551 552 553
    }
#endif

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

    // consider operations
575 576 577 578 579 580 581 582 583
    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;
584
                const QString displayedOp = op->toString(m_operationSignature, visualProperty(ShowStereotype));
585 586 587 588 589 590
                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
591
        }
592 593
        else
            height += fontHeight / 2;
Oliver Kellogg's avatar
Oliver Kellogg committed
594 595
    }

596 597 598 599 600 601 602 603
    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) {
604
            height += templatesBoxSize.height() - defaultMargin;
605
        }
606 607
    }

Oliver Kellogg's avatar
Oliver Kellogg committed
608
    // allow for height margin
609
    if (showNameOnly) {
610
        height += defaultMargin;
Oliver Kellogg's avatar
Oliver Kellogg committed
611 612 613
    }

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

616
    return QSizeF(width, height);
617 618
}

619 620
/**
 * Calculcates the size of the templates box in the top left
Ralf Habacker's avatar
Ralf Habacker committed
621
 * if it exists, returns QSize(0, 0) if it doesn't.
622 623 624
 *
 * @return  QSize of the templates flap.
 */
625
QSize ClassifierWidget::calculateTemplatesBoxSize() const
626
{
627 628
    if (!classifier())
        return QSize(0, 0);
629
    UMLTemplateList list = classifier()->getTemplateList();
Oliver Kellogg's avatar
Oliver Kellogg committed
630 631 632 633
    int count = list.count();
    if (count == 0) {
        return QSize(0, 0);
    }
634

635
    QFont font = UMLWidget::font();
Oliver Kellogg's avatar
Oliver Kellogg committed
636 637 638
    font.setItalic(false);
    font.setUnderline(false);
    font.setBold(false);
639
    const QFontMetrics fm(font);
640

Ralf Habacker's avatar
Ralf Habacker committed
641
    int width = 0;
642
    int height = count * fm.lineSpacing() + (defaultMargin*2);
643

Ralf Habacker's avatar
Ralf Habacker committed
644
    foreach (UMLTemplate *t, list) {
645
        int textWidth = fm.size(0, t->toString(Uml::SignatureType::NoSig, visualProperty(ShowStereotype))).width();
Oliver Kellogg's avatar
Oliver Kellogg committed
646 647 648
        if (textWidth > width)
            width = textWidth;
    }
649

650
    width += (defaultMargin*2);
Oliver Kellogg's avatar
Oliver Kellogg committed
651
    return QSize(width, height);
652 653
}

654 655 656
/**
 * Return the number of displayed attributes.
 */
657
int ClassifierWidget::displayedAttributes() const
658
{
659
    if (!visualProperty(ShowAttributes))
Oliver Kellogg's avatar
Oliver Kellogg committed
660
        return 0;
661 662 663
    if(baseType() == WidgetBase::wt_Instance)
        return displayedMembers(UMLObject::ot_InstanceAttribute);
    else
664
    return displayedMembers(UMLObject::ot_Attribute);
665 666
}

667 668 669
/**
 * Return the number of displayed operations.
 */
670
int ClassifierWidget::displayedOperations() const
671 672 673 674 675 676
{
    if (!visualProperty(ShowOperations))
        return 0;
    return displayedMembers(UMLObject::ot_Operation);
}

677 678 679 680 681
/**
 * Set the AssociationWidget when this ClassWidget acts as
 * an association class.
 */
void ClassifierWidget::setClassAssociationWidget(AssociationWidget *assocwidget)
682
{
683 684 685 686
    if (!classifier()) {
        uError() << "Class association cannot be applied to package";
        return;
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
687
    m_pAssocWidget = assocwidget;
688
    UMLAssociation *umlassoc = 0;
Oliver Kellogg's avatar
Oliver Kellogg committed
689
    if (assocwidget)
690
        umlassoc = assocwidget->association();
691
    classifier()->setClassAssoc(umlassoc);
692 693
}

694 695 696 697
/**
 * Return the AssociationWidget when this classifier acts as
 * an association class (else return NULL.)
 */
698
AssociationWidget *ClassifierWidget::classAssociationWidget() const
699
{
Oliver Kellogg's avatar
Oliver Kellogg committed
700
    return m_pAssocWidget;
701 702
}

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

734
    // Draw the bounding rectangle
Oliver Kellogg's avatar
Oliver Kellogg committed
735
    QSize templatesBoxSize = calculateTemplatesBoxSize();
736
    int bodyOffsetY = 0;
Oliver Kellogg's avatar
Oliver Kellogg committed
737
    if (templatesBoxSize.height() > 0)
738
        bodyOffsetY += templatesBoxSize.height() - defaultMargin;
Oliver Kellogg's avatar
Oliver Kellogg committed
739 740 741 742 743
    int w = width();
    if (templatesBoxSize.width() > 0)
        w -= templatesBoxSize.width() / 2;
    int h = height();
    if (templatesBoxSize.height() > 0)
744
        h -= templatesBoxSize.height() - defaultMargin;
745
    painter->drawRect(0, bodyOffsetY, w, h);
Oliver Kellogg's avatar
Oliver Kellogg committed
746

747
    QFont font = UMLWidget::font();
Oliver Kellogg's avatar
Oliver Kellogg committed
748 749
    font.setUnderline(false);
    font.setItalic(false);
750
    const QFontMetrics &fm = UMLWidget::getFontMetrics(UMLWidget::FT_NORMAL);
Oliver Kellogg's avatar
Oliver Kellogg committed
751 752 753
    const int fontHeight = fm.lineSpacing();

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

774 775
    const int textX = defaultMargin;
    const int textWidth = w - defaultMargin * 2;
Oliver Kellogg's avatar
Oliver Kellogg committed
776

777
    painter->setPen(QPen(textColor()));
Oliver Kellogg's avatar
Oliver Kellogg committed
778 779 780

    // draw stereotype
    font.setBold(true);
781 782 783
    const bool showNameOnly = !visualProperty(ShowAttributes) &&
                              !visualProperty(ShowOperations) &&
                              !visualProperty(ShowDocumentation);
784

Oliver Kellogg's avatar
Oliver Kellogg committed
785
    int nameHeight = fontHeight;
786
    if (visualProperty(ShowStereotype) && !m_umlObject->stereotype().isEmpty()) {
787
        painter->setFont(font);
788
        painter->drawText(textX, bodyOffsetY, textWidth, fontHeight, Qt::AlignCenter, m_umlObject->stereotype(true));
789
        bodyOffsetY += fontHeight;
790 791
    } else if (showNameOnly) {
        nameHeight = h;
Oliver Kellogg's avatar
Oliver Kellogg committed
792 793 794 795
    }

    // draw name
    QString name;
796 797 798 799 800 801 802 803 804 805 806
    UMLObject *o;
    if (m_umlObject && m_umlObject->isUMLInstance() && m_umlObject->asUMLInstance()->classifier())
        o = m_umlObject->asUMLInstance()->classifier();
    else
        o = m_umlObject;
    if (!o)
        name = m_Text;
    else if (visualProperty(ShowPackage))
        name = o->fullyQualifiedName();
    else
        name = o->name();
807 808 809

    QString displayedName;
    if (m_umlObject->isUMLInstance())
810
        displayedName = m_umlObject->name() + QLatin1String(" : ") + name;
811 812
    else
        displayedName = name;
813

814
    font.setItalic(m_umlObject->isAbstract());
815
    painter->setFont(font);
816
    painter->drawText(textX, bodyOffsetY, textWidth, nameHeight, Qt::AlignCenter, displayedName);
817
    bodyOffsetY += fontHeight;
Oliver Kellogg's avatar
Oliver Kellogg committed
818 819
    font.setBold(false);
    font.setItalic(false);
820
    painter->setFont(font);
Oliver Kellogg's avatar
Oliver Kellogg committed
821

822 823
#ifdef ENABLE_WIDGET_SHOW_DOC
    // draw documentation
824 825 826 827 828 829
    if (visualProperty(ShowDocumentation)) {
        setPenFromSettings(painter);
        painter->drawLine(0, bodyOffsetY, w, bodyOffsetY);
        painter->setPen(textColor());

        if (!documentation().isEmpty()) {
830 831 832
            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);
833 834 835
            brect.adjust(textX, bodyOffsetY, textX, bodyOffsetY);
            painter->drawText(brect, Qt::AlignCenter | Qt::TextWordWrap, documentation());
            bodyOffsetY += brect.height();
836
        }
837 838
        else
            bodyOffsetY += fontHeight / 2;
839 840 841
    }
#endif
    // draw attributes
842 843 844 845 846
    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
847

848 849
        const int numAtts = displayedAttributes();
        if (numAtts > 0) {
850 851 852 853 854 855 856
            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);
            }
857
            bodyOffsetY += fontHeight * numAtts;
858
        }
859 860
        else
            bodyOffsetY += fontHeight / 2;
Oliver Kellogg's avatar
Oliver Kellogg committed
861 862 863
    }

    // draw operations
864 865 866 867 868 869 870 871 872 873 874
    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
875 876
    }

877
    UMLWidget::paint(painter, option, widget);
878 879
}

880 881 882 883 884
/**
 * @return The shape of the ClassifierWidget.
 */
QPainterPath ClassifierWidget::shape() const
{
885
    QPainterPath path;
886
    if (classifier() && classifier()->isInterface() && visualProperty(DrawAsCircle)) {
887
        path.addEllipse(rect());
888 889
        return path;
    }
890 891 892
#ifdef ENABLE_WIDGET_SHOW_DOC
    QSizeF mainSize = rect().size();
#else
893
    QSizeF mainSize = calculateSize(false);
894
#endif
895 896 897
    QSize templatesBoxSize = calculateTemplatesBoxSize();
    qreal mainY = 0.0;
    if (templatesBoxSize.height() > 0) {
898
        mainY += templatesBoxSize.height() - defaultMargin;
899 900 901 902 903
        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;
904 905
}

906
/**
907
 * Draws the interface as a circle.
908
 * Only applies when m_umlObject->getBaseType() is ot_Interface.
909
 */
910
void ClassifierWidget::drawAsCircle(QPainter *painter, const QStyleOptionGraphicsItem *option)
911
{
912 913
    const int w = width();

914
    if (associationWidgetList().size() > 1) {
915
        painter->drawEllipse(w/2 - CIRCLE_SIZE/2, SOCKET_INCREMENT / 2, CIRCLE_SIZE, CIRCLE_SIZE);
916 917 918
        // Draw socket for required interface.
        const qreal angleSpan = 180;   // 360.0 / (m_Assocs.size() + 1.0);
        const int arcDiameter = CIRCLE_SIZE + SOCKET_INCREMENT;
919
        QRect requireArc(w/2 - arcDiameter/2, 0, arcDiameter, arcDiameter);
920 921 922
        const QPointF center(x() + w/2, y() + arcDiameter/2);
        const qreal cX = center.x();
        const qreal cY = center.y();
923
        foreach (AssociationWidget *aw, associationWidgetList()) {
924 925 926 927 928 929 930 931 932 933 934 935