1 /*
2     SPDX-License-Identifier: GPL-2.0-or-later
3     SPDX-FileCopyrightText: 2004-2021 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
4 */
5 
6 // own header
7 #include "classifierwidget.h"
8 
9 // app includes
10 #include "floatingtextwidget.h"
11 #include "associationwidget.h"
12 #include "associationline.h"
13 #include "classifier.h"
14 #include "cmds.h"
15 #include "debug_utils.h"
16 #include "diagram_utils.h"
17 #include "dialog_utils.h"
18 #include "instance.h"
19 #include "listpopupmenu.h"
20 #include "object_factory.h"
21 #include "operation.h"
22 #include "optionstate.h"
23 #include "template.h"
24 #include "uml.h"
25 #include "umldoc.h"
26 #include "umlview.h"
27 #include "widget_utils.h"
28 
29 // qt includes
30 #include <QPainter>
31 #include <QXmlStreamWriter>
32 
33 DEBUG_REGISTER_DISABLED(ClassifierWidget)
34 
35 const int ClassifierWidget::CIRCLE_SIZE = 30;
36 const int ClassifierWidget::SOCKET_INCREMENT = 10;
37 
38 /**
39  * Constructs a ClassifierWidget for a UMLClassifier.
40  *
41  * @param scene   The parent of this ClassifierWidget.
42  * @param umlc    The UMLClassifier to represent.
43  */
ClassifierWidget(UMLScene * scene,UMLClassifier * umlc)44 ClassifierWidget::ClassifierWidget(UMLScene * scene, UMLClassifier *umlc)
45   : UMLWidget(scene, WidgetBase::wt_Class, umlc),
46     m_pAssocWidget(nullptr),
47     m_pInterfaceName(nullptr)
48 {
49     DiagramProxyWidget::setShowLinkedDiagram(false);
50     const Settings::OptionState& ops = m_scene->optionState();
51     setVisualPropertyCmd(ShowVisibility, ops.classState.showVisibility);
52     setVisualPropertyCmd(ShowOperations, ops.classState.showOps);
53     setVisualPropertyCmd(ShowPublicOnly, ops.classState.showPublicOnly);
54     setVisualPropertyCmd(ShowPackage,    ops.classState.showPackage);
55     m_attributeSignature = Uml::SignatureType::ShowSig;
56     /*:TODO:
57     setVisualProperty(ShowOperationSignature, ops.classState.showOpSig);
58       Cannot do that because we get "pure virtual method called". Open code:
59      */
60     if(!ops.classState.showOpSig) {
61         if (visualProperty(ShowVisibility))
62             m_operationSignature = Uml::SignatureType::NoSig;
63         else
64             m_operationSignature = Uml::SignatureType::NoSigNoVis;
65 
66     } else if (visualProperty(ShowVisibility))
67         m_operationSignature = Uml::SignatureType::ShowSig;
68     else
69         m_operationSignature = Uml::SignatureType::SigNoVis;
70 
71     setVisualPropertyCmd(ShowAttributes, ops.classState.showAtts);
72 
73     // Do not call setShowStereotype here, it is a virtual method
74     // and setup of the vtbl_ptr has not yet been finalized.
75     m_showStereotype = ops.classState.showStereoType;
76     if (m_showStereotype != Uml::ShowStereoType::None)
77         m_visualProperties |= ShowStereotype;
78 
79     setVisualPropertyCmd(DrawAsCircle, false);
80 
81     setShowAttSigs(ops.classState.showAttSig);
82 
83     if (umlc && umlc->isInterface()) {
84         setBaseType(WidgetBase::wt_Interface);
85         m_visualProperties = ShowOperations | ShowVisibility;
86         setShowStereotype(Uml::ShowStereoType::Tags);
87         updateSignatureTypes();
88     }
89 }
90 
91 /**
92  * Constructs a ClassifierWidget for a UMLInstance.
93  *
94  * @param scene   The parent of this ClassifierWidget.
95  * @param umli    The UMLInstance to represent.
96  */
ClassifierWidget(UMLScene * scene,UMLInstance * umli)97 ClassifierWidget::ClassifierWidget(UMLScene * scene, UMLInstance * umli)
98   : UMLWidget(scene, WidgetBase::wt_Instance, umli),
99     m_pAssocWidget(nullptr),
100     m_pInterfaceName(nullptr)
101 {
102     DiagramProxyWidget::setShowLinkedDiagram(false);
103     const Settings::OptionState& ops = m_scene->optionState();
104     setVisualPropertyCmd(ShowVisibility, ops.classState.showVisibility);
105     setVisualPropertyCmd(ShowPublicOnly, ops.classState.showPublicOnly);
106     setVisualPropertyCmd(ShowPackage,    ops.classState.showPackage);
107     m_attributeSignature = Uml::SignatureType::ShowSig;
108 
109     setVisualPropertyCmd(ShowAttributes, ops.classState.showAtts);
110 
111     setShowAttSigs(ops.classState.showAttSig);
112 
113     if (umli) {
114         setBaseType(WidgetBase::wt_Instance);
115         m_visualProperties =  ShowAttributes;
116         updateSignatureTypes();
117     }
118 }
119 
120 /**
121  * Constructs a ClassifierWidget for a UMLPackage.
122  *
123  * @param scene   The parent of this ClassifierWidget.
124  * @param o       The UMLPackage to represent.
125  */
ClassifierWidget(UMLScene * scene,UMLPackage * o)126 ClassifierWidget::ClassifierWidget(UMLScene * scene, UMLPackage *o)
127   : UMLWidget(scene, WidgetBase::wt_Package, o),
128     m_pAssocWidget(nullptr),
129     m_pInterfaceName(nullptr)
130 {
131     const Settings::OptionState& ops = m_scene->optionState();
132     setVisualPropertyCmd(ShowVisibility, ops.classState.showVisibility);
133     setVisualPropertyCmd(ShowOperations, ops.classState.showOps);
134     setVisualPropertyCmd(ShowPublicOnly, ops.classState.showPublicOnly);
135     setVisualPropertyCmd(ShowPackage,    ops.classState.showPackage);
136     m_attributeSignature = Uml::SignatureType::ShowSig;
137 
138     if(!ops.classState.showOpSig) {
139         if (visualProperty(ShowVisibility))
140             m_operationSignature = Uml::SignatureType::NoSig;
141         else
142             m_operationSignature = Uml::SignatureType::NoSigNoVis;
143 
144     } else if (visualProperty(ShowVisibility))
145         m_operationSignature = Uml::SignatureType::ShowSig;
146     else
147         m_operationSignature = Uml::SignatureType::SigNoVis;
148 
149     setVisualPropertyCmd(ShowAttributes, ops.classState.showAtts);
150     setShowStereotype(ops.classState.showStereoType);
151     setVisualPropertyCmd(DrawAsPackage, true);
152 
153     setShowAttSigs(ops.classState.showAttSig);
154 }
155 
156 /**
157  * Destructor.
158  */
~ClassifierWidget()159 ClassifierWidget::~ClassifierWidget()
160 {
161     if (m_pAssocWidget)
162         m_pAssocWidget->removeAssocClassLine();
163     if (m_pInterfaceName) {
164         delete m_pInterfaceName;
165         m_pInterfaceName = nullptr;
166     }
167 }
168 
169 /**
170  * Return the UMLClassifier which this ClassifierWidget
171  * represents.
172  */
classifier() const173 UMLClassifier *ClassifierWidget::classifier() const
174 {
175     return m_umlObject->asUMLClassifier();
176 }
177 
178 /**
179  * Reimplement method from UMLWidget.
180  */
setShowStereotype(Uml::ShowStereoType::Enum flag)181 void ClassifierWidget::setShowStereotype(Uml::ShowStereoType::Enum flag)
182 {
183     if (flag == Uml::ShowStereoType::None)
184         m_visualProperties &= ~ShowStereotype;
185     else
186         m_visualProperties |= ShowStereotype;
187     UMLWidget::setShowStereotype(flag);
188 }
189 
190 /**
191  * @return the visual properties
192  */
visualProperties() const193 ClassifierWidget::VisualProperties ClassifierWidget::visualProperties() const
194 {
195     return m_visualProperties;
196 }
197 
198 /**
199  * Set an OR combination of properties stored in \a properties on this
200  * widget.
201  */
setVisualProperties(VisualProperties properties)202 void ClassifierWidget::setVisualProperties(VisualProperties properties)
203 {
204     // Don't do anything if the argument is equal to current status.
205     if (quint32(m_visualProperties) == quint32(properties)) {
206         return;
207     }
208 
209     m_visualProperties = properties;
210     updateSignatureTypes();
211 }
212 
213 /**
214  * @return The status of the property passed in.
215  *
216  * @note Use @ref attributeSignature() and @ref
217  *       operationSignature() to get signature status.  This
218  *       method only indicates whether signature is visible or not.
219  */
visualProperty(VisualProperty property) const220 bool ClassifierWidget::visualProperty(VisualProperty property) const
221 {
222     if (property == ShowAttributeSignature) {
223         return (m_attributeSignature == Uml::SignatureType::ShowSig
224                 || m_attributeSignature == Uml::SignatureType::SigNoVis);
225     }
226 
227     else if (property == ShowOperationSignature) {
228         return (m_operationSignature == Uml::SignatureType::ShowSig
229                 || m_operationSignature == Uml::SignatureType::SigNoVis);
230     }
231 
232     else if (property == ShowStereotype) {
233         return (m_showStereotype != Uml::ShowStereoType::None);
234     }
235 
236     return m_visualProperties.testFlag(property);
237 }
238 
239 /**
240  * A convenient method to set and reset individual VisualProperty
241  *
242  * Undo command.
243  *
244  * @param property The property to be set/reset.
245  * @param enable   True/false to set/reset. (default = true)
246  *
247  * @note This method handles ShowAttributeSignature and
248  *       ShowOperationSignature specially.
249  */
setVisualProperty(VisualProperty property,bool enable)250 void ClassifierWidget::setVisualProperty(VisualProperty property, bool enable)
251 {
252     if (visualProperty(property) != enable) {
253         UMLApp::app()->executeCommand(new Uml::CmdChangeVisualProperty(this, property, enable));
254     }
255 }
256 
257 /**
258  * A convenient method to set and reset individual VisualProperty
259  *
260  * @param property The property to be set/reset.
261  * @param enable   True/false to set/reset. (default = true)
262  *
263  * @note This method handles ShowAttributeSignature and
264  *       ShowOperationSignature specially.
265  */
setVisualPropertyCmd(VisualProperty property,bool enable)266 void ClassifierWidget::setVisualPropertyCmd(VisualProperty property, bool enable)
267 {
268     // Handle ShowAttributeSignature and ShowOperationSignature
269     // specially.
270 
271     if (property == ShowAttributeSignature) {
272         if (!enable) {
273             m_attributeSignature = visualProperty(ShowVisibility) ?
274                 Uml::SignatureType::NoSig : Uml::SignatureType::NoSigNoVis;
275         } else {
276             m_attributeSignature = visualProperty(ShowVisibility) ?
277                 Uml::SignatureType::ShowSig : Uml::SignatureType::SigNoVis;
278         }
279         //:TODO: updateTextItemGroups();
280         updateSignatureTypes();
281     }
282 
283     else if (property == ShowOperationSignature) {
284         if (!enable) {
285             m_operationSignature = visualProperty(ShowVisibility) ?
286                 Uml::SignatureType::NoSig : Uml::SignatureType::NoSigNoVis;
287         } else {
288             m_operationSignature = visualProperty(ShowVisibility) ?
289                 Uml::SignatureType::ShowSig : Uml::SignatureType::SigNoVis;
290         }
291         //:TODO: updateTextItemGroups();
292         updateSignatureTypes();
293     }
294 
295     else if (property == ShowStereotype) {
296         setShowStereotype(enable ? Uml::ShowStereoType::Tags
297                                  : Uml::ShowStereoType::None);
298     }
299 
300     else if (property == DrawAsCircle) {
301         // Don't do anything if the flag status is same.
302         if (visualProperty(property) == enable)
303             return;
304         if (enable) {
305             m_visualProperties |= property;
306         } else {
307             m_visualProperties &= ~property;
308         }
309         setDrawAsCircle(enable);
310     }
311 
312     // Some other flag.
313     else {
314         // Don't do anything if the flag status is same.
315         if (visualProperty(property) == enable) {
316             return;
317         }
318 
319         // Call setVisualProperties appropriately based on enable.
320         if (enable) {
321             setVisualProperties(visualProperties() | property);
322         } else {
323             setVisualProperties(visualProperties() & ~property);
324         }
325     }
326 }
327 
328 /**
329  * A convenient method to toggle individual VisualProperty of this
330  * widget.
331  *
332  * @param property The property to be toggled.
333  *
334  * @note This method handles ShowAttributeSignature and
335  *       ShowOperationSignature specially.
336  */
toggleVisualProperty(VisualProperty property)337 void ClassifierWidget::toggleVisualProperty(VisualProperty property)
338 {
339     bool oppositeStatus;
340     if (property == ShowOperationSignature) {
341         oppositeStatus = !(m_operationSignature == Uml::SignatureType::ShowSig
342                            || m_operationSignature == Uml::SignatureType::SigNoVis);
343     }
344     else if (property == ShowAttributeSignature) {
345         oppositeStatus = !(m_attributeSignature == Uml::SignatureType::ShowSig
346                            || m_attributeSignature == Uml::SignatureType::SigNoVis);
347     }
348     else {
349         oppositeStatus = !visualProperty(property);
350     }
351 
352     DEBUG(DBG_SRC) << "VisualProperty: " << property << " to opposite status " << oppositeStatus;
353     setVisualProperty(property, oppositeStatus);
354 }
355 
356 /**
357  * Updates m_operationSignature to match m_showVisibility.
358  */
updateSignatureTypes()359 void ClassifierWidget::updateSignatureTypes()
360 {
361     //turn on scope
362     if (visualProperty(ShowVisibility)) {
363         if (m_operationSignature == Uml::SignatureType::NoSigNoVis) {
364             m_operationSignature = Uml::SignatureType::NoSig;
365         } else if (m_operationSignature == Uml::SignatureType::SigNoVis) {
366             m_operationSignature = Uml::SignatureType::ShowSig;
367         }
368     }
369     //turn off scope
370     else {
371         if (m_operationSignature == Uml::SignatureType::ShowSig) {
372             m_operationSignature = Uml::SignatureType::SigNoVis;
373         } else if (m_operationSignature == Uml::SignatureType::NoSig) {
374             m_operationSignature = Uml::SignatureType::NoSigNoVis;
375         }
376     }
377     if (visualProperty(ShowVisibility)) {
378         if (m_attributeSignature == Uml::SignatureType::NoSigNoVis)
379             m_attributeSignature = Uml::SignatureType::NoSig;
380         else if (m_attributeSignature == Uml::SignatureType::SigNoVis)
381             m_attributeSignature = Uml::SignatureType::ShowSig;
382     } else {
383         if (m_attributeSignature == Uml::SignatureType::ShowSig)
384             m_attributeSignature = Uml::SignatureType::SigNoVis;
385         else if(m_attributeSignature == Uml::SignatureType::NoSig)
386             m_attributeSignature = Uml::SignatureType::NoSigNoVis;
387     }
388     updateGeometry();
389     update();
390 }
391 
392 /**
393  * Returns whether to show attribute signatures.
394  * Only applies when m_umlObject->getBaseType() is ot_Class.
395  *
396  * @return  Status of how attribute signatures are shown.
397  */
attributeSignature() const398 Uml::SignatureType::Enum ClassifierWidget::attributeSignature() const
399 {
400     return m_attributeSignature;
401 }
402 
403 /**
404  * Sets the type of signature to display for an attribute.
405  * Only applies when m_umlObject->getBaseType() is ot_Class.
406  *
407  * @param sig   Type of signature to display for an attribute.
408  */
setAttributeSignature(Uml::SignatureType::Enum sig)409 void ClassifierWidget::setAttributeSignature(Uml::SignatureType::Enum sig)
410 {
411     m_attributeSignature = sig;
412     updateSignatureTypes();
413     updateGeometry();
414     update();
415 }
416 
417 /**
418  * @return The Uml::SignatureType::Enum value for the operations.
419  */
operationSignature() const420 Uml::SignatureType::Enum ClassifierWidget::operationSignature() const
421 {
422     return m_operationSignature;
423 }
424 
425 /**
426  * Set the type of signature to display for an Operation
427  *
428  * @param sig   Type of signature to display for an operation.
429  */
setOperationSignature(Uml::SignatureType::Enum sig)430 void ClassifierWidget::setOperationSignature(Uml::SignatureType::Enum sig)
431 {
432     m_operationSignature = sig;
433     updateSignatureTypes();
434     updateGeometry();
435     update();
436 }
437 
438 /**
439  * Sets whether to show attribute signature
440  * Only applies when m_umlObject->getBaseType() is ot_Class.
441  *
442  * @param _status  True if attribute signatures shall be shown.
443  */
setShowAttSigs(bool _status)444 void ClassifierWidget::setShowAttSigs(bool _status)
445 {
446     if(!_status) {
447         if (visualProperty(ShowVisibility))
448             m_attributeSignature = Uml::SignatureType::NoSig;
449         else
450             m_attributeSignature = Uml::SignatureType::NoSigNoVis;
451     }
452     else if (visualProperty(ShowVisibility))
453         m_attributeSignature = Uml::SignatureType::ShowSig;
454     else
455         m_attributeSignature = Uml::SignatureType::SigNoVis;
456     if (UMLApp::app()->document()->loading())
457         return;
458     updateGeometry();
459     update();
460 }
461 
462 /**
463  * Toggles whether to show attribute signatures.
464  * Only applies when m_umlObject->getBaseType() is ot_Class.
465  */
toggleShowAttSigs()466 void ClassifierWidget::toggleShowAttSigs()
467 {
468     if (m_attributeSignature == Uml::SignatureType::ShowSig ||
469             m_attributeSignature == Uml::SignatureType::SigNoVis) {
470         if (visualProperty(ShowVisibility)) {
471             m_attributeSignature = Uml::SignatureType::NoSig;
472         } else {
473             m_attributeSignature = Uml::SignatureType::NoSigNoVis;
474         }
475     } else if (visualProperty(ShowVisibility)) {
476         m_attributeSignature = Uml::SignatureType::ShowSig;
477     } else {
478         m_attributeSignature = Uml::SignatureType::SigNoVis;
479     }
480     updateGeometry();
481     update();
482 }
483 
484 /**
485  * Return the number of displayed members of the given ObjectType.
486  * Takes into consideration m_showPublicOnly but not other settings.
487  */
displayedMembers(UMLObject::ObjectType ot) const488 int ClassifierWidget::displayedMembers(UMLObject::ObjectType ot) const
489 {
490     int count = 0;
491     UMLClassifier *umlc = this->classifier();
492     if (!umlc && m_umlObject && m_umlObject->isUMLInstance() && ot == UMLObject::ot_Attribute)
493         umlc = m_umlObject->asUMLInstance()->classifier();
494     if (!umlc)
495         return count;
496     UMLClassifierListItemList list = umlc->getFilteredList(ot);
497     foreach (UMLClassifierListItem *m, list) {
498         if (!(visualProperty(ShowPublicOnly) && m->visibility() != Uml::Visibility::Public))
499             count++;
500     }
501     return count;
502 }
503 
504 /**
505  * Overrides method from UMLWidget.
506  */
minimumSize() const507 QSizeF ClassifierWidget::minimumSize() const
508 {
509     return calculateSize();
510 }
511 
512 /**
513  * Calculate content related size of widget.
514  * Overrides method from UMLWidget.
515  */
calculateSize(bool withExtensions) const516 QSizeF ClassifierWidget::calculateSize(bool withExtensions /* = true */) const
517 {
518     if (!m_umlObject) {
519         return UMLWidget::minimumSize();
520     }
521     if (m_umlObject->baseType() == UMLObject::ot_Package) {
522         return calculateAsPackageSize();
523     }
524     UMLClassifier *umlc = this->classifier();
525     if (umlc) {
526         if (umlc->isInterface() && visualProperty(DrawAsCircle))
527             return calculateAsCircleSize();
528     } else if (m_umlObject && m_umlObject->isUMLInstance()) {
529        umlc = m_umlObject->asUMLInstance()->classifier();
530     }
531 
532     const bool showNameOnly = !visualProperty(ShowAttributes) &&
533                               !visualProperty(ShowOperations) &&
534                               !visualProperty(ShowDocumentation);
535 
536     const QFontMetrics &fm = getFontMetrics(UMLWidget::FT_NORMAL);
537     const int fontHeight = fm.lineSpacing();
538     // width is the width of the longest 'word'
539     int width = 0, height = 0;
540     // consider stereotype
541     if (m_showStereotype != Uml::ShowStereoType::None &&
542                              m_umlObject && !m_umlObject->stereotype().isEmpty()) {
543         height += fontHeight;
544         QString taggedValues;
545         if (m_showStereotype == Uml::ShowStereoType::Tags) {
546             taggedValues = tags();
547             if (!taggedValues.isEmpty())
548                 height += fontHeight;
549         }
550         // ... width
551         const QFontMetrics &bfm = UMLWidget::getFontMetrics(UMLWidget::FT_BOLD);
552         const int stereoWidth = bfm.size(0, m_umlObject->stereotype(true)).width();
553         if (stereoWidth > width)
554             width = stereoWidth;
555         if (!taggedValues.isEmpty()) {
556             const int tagsWidth = bfm.size(0, taggedValues).width();
557             if (tagsWidth > width)
558                 width = tagsWidth;
559         }
560     } else if (showNameOnly) {
561         height += defaultMargin;
562     }
563 
564     // consider name
565     height += fontHeight;
566     // ... width
567     QString name;
568     UMLObject *o;
569     if (m_umlObject && m_umlObject->isUMLInstance() && umlc)
570         o = umlc;
571     else
572         o = m_umlObject;
573     if (!o)
574         name = m_Text;
575     else if (visualProperty(ShowPackage))
576         name = o->fullyQualifiedName();
577     else
578         name = o->name();
579 
580     QString displayedName;
581     if (m_umlObject && m_umlObject->isUMLInstance())
582         displayedName = m_umlObject->name() + QLatin1String(" : ") + name;
583     else
584         displayedName = name;
585 
586     const UMLWidget::FontType nft = (m_umlObject && m_umlObject->isAbstract() ? FT_BOLD_ITALIC : FT_BOLD);
587     const int nameWidth = UMLWidget::getFontMetrics(nft).size(0, displayedName).width();
588     if (nameWidth > width)
589         width = nameWidth;
590 
591 #ifdef ENABLE_WIDGET_SHOW_DOC
592     // consider documentation
593     if (visualProperty(ShowDocumentation)) {
594         if (!documentation().isEmpty()) {
595             QRect brect = fm.boundingRect(QRect(0, 0, this->width()-2*defaultMargin, this->height()-height), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, documentation());
596             height += brect.height();
597             if (!visualProperty(ShowOperations) && !visualProperty(ShowAttributes)) {
598                 if (brect.width() >= width)
599                     width = brect.width();
600             }
601         }
602         else
603             height += fontHeight / 2;
604     }
605 #endif
606 
607     // consider attributes
608     if (visualProperty(ShowAttributes)) {
609         const int numAtts = displayedAttributes();
610         if (numAtts > 0) {
611             height += fontHeight * numAtts;
612             // calculate width of the attributes
613             UMLClassifierListItemList list = umlc->getFilteredList(UMLObject::ot_Attribute);
614             foreach (UMLClassifierListItem *a, list) {
615                 if (visualProperty(ShowPublicOnly) && a->visibility() != Uml::Visibility::Public)
616                     continue;
617                 const int attWidth = fm.size(0, a->toString(m_attributeSignature, visualProperty(ShowStereotype))).width();
618                 if (attWidth > width)
619                     width = attWidth;
620             }
621         }
622         else
623             height += fontHeight / 2;
624     }
625 
626     // consider operations
627     if (visualProperty(ShowOperations)) {
628         const int numOps = displayedOperations();
629         if (numOps > 0) {
630             height += numOps * fontHeight;
631             // ... width
632             UMLOperationList list(umlc->getOpList());
633             foreach (UMLOperation* op,  list) {
634                       if (visualProperty(ShowPublicOnly) && op->visibility() != Uml::Visibility::Public)
635                     continue;
636                 const QString displayedOp = op->toString(m_operationSignature, visualProperty(ShowStereotype));
637                 UMLWidget::FontType oft;
638                 oft = (op->isAbstract() ? UMLWidget::FT_ITALIC : UMLWidget::FT_NORMAL);
639                 const int w = UMLWidget::getFontMetrics(oft).size(0, displayedOp).width();
640                 if (w > width)
641                     width = w;
642             }
643         }
644         else
645             height += fontHeight / 2;
646     }
647 
648     if (withExtensions) {
649         // consider template box _as last_ !
650         QSize templatesBoxSize = calculateTemplatesBoxSize();
651         if (templatesBoxSize.width() != 0) {
652             // add width to largest 'word'
653             width += templatesBoxSize.width() / 2;
654         }
655         if (templatesBoxSize.height() != 0) {
656             height += templatesBoxSize.height() - defaultMargin;
657         }
658     }
659 
660     // allow for height margin
661     if (showNameOnly) {
662         height += defaultMargin;
663     }
664 
665     // allow for width margin
666     width += defaultMargin * 2;
667 
668     if (DiagramProxyWidget::linkedDiagram() || DiagramProxyWidget::diagramLink() != Uml::ID::None)
669         width += 2 * DiagramProxyWidget::iconRect().width();
670 
671     return QSizeF(width, height);
672 }
673 
674 /**
675  * Calculcates the size of the templates box in the top left
676  * if it exists, returns QSize(0, 0) if it doesn't.
677  *
678  * @return  QSize of the templates flap.
679  */
calculateTemplatesBoxSize() const680 QSize ClassifierWidget::calculateTemplatesBoxSize() const
681 {
682     if (!classifier())
683         return QSize(0, 0);
684     UMLTemplateList list = classifier()->getTemplateList();
685     int count = list.count();
686     if (count == 0) {
687         return QSize(0, 0);
688     }
689 
690     QFont font = UMLWidget::font();
691     font.setItalic(false);
692     font.setUnderline(false);
693     font.setBold(false);
694     const QFontMetrics fm(font);
695 
696     int width = 0;
697     int height = count * fm.lineSpacing() + (defaultMargin*2);
698 
699     foreach (UMLTemplate *t, list) {
700         int textWidth = fm.size(0, t->toString(Uml::SignatureType::NoSig, visualProperty(ShowStereotype))).width();
701         if (textWidth > width)
702             width = textWidth;
703     }
704 
705     width += (defaultMargin*2);
706     return QSize(width, height);
707 }
708 
709 /**
710  * Return the number of displayed attributes.
711  */
displayedAttributes() const712 int ClassifierWidget::displayedAttributes() const
713 {
714     if (!visualProperty(ShowAttributes))
715         return 0;
716     return displayedMembers(UMLObject::ot_Attribute);
717 }
718 
719 /**
720  * Return the number of displayed operations.
721  */
displayedOperations() const722 int ClassifierWidget::displayedOperations() const
723 {
724     if (!visualProperty(ShowOperations))
725         return 0;
726     return displayedMembers(UMLObject::ot_Operation);
727 }
728 
729 /**
730  * Set the AssociationWidget when this ClassWidget acts as
731  * an association class.
732  */
setClassAssociationWidget(AssociationWidget * assocwidget)733 void ClassifierWidget::setClassAssociationWidget(AssociationWidget *assocwidget)
734 {
735     if (!classifier()) {
736         uError() << "Class association cannot be applied to non classifier";
737         return;
738     }
739     m_pAssocWidget = assocwidget;
740 }
741 
742 /**
743  * Return the AssociationWidget when this classifier acts as
744  * an association class (else return NULL.)
745  */
classAssociationWidget() const746 AssociationWidget *ClassifierWidget::classAssociationWidget() const
747 {
748     return m_pAssocWidget;
749 }
750 
751 /**
752  * Overrides standard method.
753  * Auxiliary to reimplementations in the derived classes.
754  * @note keep fetching attributes in sync with calculateSize()
755  */
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)756 void ClassifierWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
757 {
758     Q_UNUSED(option);
759     Q_UNUSED(widget);
760     setPenFromSettings(painter);
761     if (UMLWidget::useFillColor())
762         painter->setBrush(UMLWidget::fillColor());
763     else {
764         painter->setBrush(m_scene->backgroundColor());
765     }
766     if (m_umlObject->baseType() == UMLObject::ot_Package) {
767         drawAsPackage(painter, option);
768         UMLWidget::paint(painter, option, widget);
769         return;
770     }
771     UMLClassifier *umlc = this->classifier();
772     if (!umlc) {
773         if (m_umlObject && m_umlObject->isUMLInstance()) {
774             umlc = m_umlObject->asUMLInstance()->classifier();
775         } else {
776             uError() << "ClassifierWidget::paint internal error - classifier() returns NULL";
777             return;
778         }
779     }
780     if (umlc && umlc->isInterface() && visualProperty(DrawAsCircle)) {
781         drawAsCircle(painter, option);
782         UMLWidget::paint(painter, option, widget);
783         return;
784     }
785 
786     // Draw the bounding rectangle
787     QSize templatesBoxSize = calculateTemplatesBoxSize();
788     int bodyOffsetY = 0;
789     if (templatesBoxSize.height() > 0)
790         bodyOffsetY += templatesBoxSize.height() - defaultMargin;
791     int w = width();
792     if (templatesBoxSize.width() > 0)
793         w -= templatesBoxSize.width() / 2;
794     int h = height();
795     if (templatesBoxSize.height() > 0)
796         h -= templatesBoxSize.height() - defaultMargin;
797     painter->drawRect(0, bodyOffsetY, w, h);
798 
799     QFont font = UMLWidget::font();
800     font.setUnderline(false);
801     font.setItalic(false);
802     const QFontMetrics &fm = UMLWidget::getFontMetrics(UMLWidget::FT_NORMAL);
803     const int fontHeight = fm.lineSpacing();
804 
805     //If there are any templates then draw them
806     UMLTemplateList tlist;
807     if (umlc)
808         tlist = umlc->getTemplateList();
809     if (tlist.count() > 0) {
810         setPenFromSettings(painter);
811         QPen pen = painter->pen();
812         pen.setStyle(Qt::DotLine);
813         painter->setPen(pen);
814         painter->drawRect(width() - templatesBoxSize.width(), 0,
815                     templatesBoxSize.width(), templatesBoxSize.height());
816         painter->setPen(QPen(textColor()));
817         font.setBold(false);
818         painter->setFont(font);
819         const int x = width() - templatesBoxSize.width() + defaultMargin;
820         int y = defaultMargin;
821         const int templateWidth = templatesBoxSize.width() - 2 * defaultMargin;
822         foreach (UMLTemplate *t, tlist) {
823             QString text = t->toString(Uml::SignatureType::NoSig, m_showStereotype != Uml::ShowStereoType::None);
824             painter->drawText(x, y, templateWidth, fontHeight, Qt::AlignVCenter, text);
825             y += fontHeight;
826         }
827     }
828 
829     const int textX = defaultMargin;
830     const int textWidth = w - defaultMargin * 2;
831 
832     painter->setPen(QPen(textColor()));
833 
834     // draw stereotype
835     font.setBold(true);
836     const bool showNameOnly = !visualProperty(ShowAttributes) &&
837                               !visualProperty(ShowOperations) &&
838                               !visualProperty(ShowDocumentation);
839 
840     int nameHeight = fontHeight;
841     if (m_showStereotype != Uml::ShowStereoType::None && !m_umlObject->stereotype().isEmpty()) {
842         painter->setFont(font);
843         painter->drawText(textX, bodyOffsetY, textWidth, fontHeight, Qt::AlignCenter, m_umlObject->stereotype(true));
844         bodyOffsetY += fontHeight;
845         if (m_showStereotype == Uml::ShowStereoType::Tags) {
846             QString taggedValues = tags();
847             if (!taggedValues.isEmpty()) {
848                 painter->drawText(textX, bodyOffsetY, textWidth, fontHeight, Qt::AlignCenter, taggedValues);
849                 bodyOffsetY += fontHeight;
850             }
851         }
852     } else if (showNameOnly) {
853         nameHeight = h;
854     }
855 
856     // draw name
857     QString displayedName;
858     if (!m_umlObject) {
859         displayedName = m_Text;
860     } else if (m_umlObject->isUMLInstance()) {
861         displayedName = m_umlObject->name() + QLatin1String(" : ");
862         if (umlc) {
863             if (visualProperty(ShowPackage))
864                 displayedName.append(umlc->fullyQualifiedName());
865             else
866                 displayedName.append(umlc->name());
867         }
868     } else if (visualProperty(ShowPackage)) {
869         displayedName = m_umlObject->fullyQualifiedName();
870     } else {
871         displayedName = m_umlObject->name();
872     }
873 
874     if (baseType() == WidgetBase::wt_Object || baseType() == WidgetBase::wt_Instance)
875         font.setUnderline(true);
876 
877     font.setItalic(m_umlObject->isAbstract());
878     painter->setFont(font);
879     painter->drawText(textX, bodyOffsetY, textWidth, nameHeight, Qt::AlignCenter, displayedName);
880     bodyOffsetY += fontHeight;
881     font.setBold(false);
882     font.setItalic(false);
883     font.setUnderline(false);
884     painter->setFont(font);
885 
886 #ifdef ENABLE_WIDGET_SHOW_DOC
887     // draw documentation
888     if (visualProperty(ShowDocumentation)) {
889         setPenFromSettings(painter);
890         painter->drawLine(0, bodyOffsetY, w, bodyOffsetY);
891         painter->setPen(textColor());
892 
893         if (!documentation().isEmpty()) {
894             QRect brect = fm.boundingRect(QRect(0, 0, w-2*defaultMargin, h-bodyOffsetY), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, documentation());
895             if (brect.width() > width() + 2*defaultMargin)
896                 brect.setWidth(width()-2*defaultMargin);
897             brect.adjust(textX, bodyOffsetY, textX, bodyOffsetY);
898             painter->drawText(brect, Qt::AlignLeft | Qt::TextWordWrap, documentation());
899             bodyOffsetY += brect.height();
900         }
901         else
902             bodyOffsetY += fontHeight / 2;
903     }
904 #endif
905     // draw attributes
906     if (visualProperty(ShowAttributes)) {
907         // draw dividing line between doc/name and attributes
908         setPenFromSettings(painter);
909         painter->drawLine(0, bodyOffsetY, w, bodyOffsetY);
910         painter->setPen(textColor());
911 
912         const int numAtts = displayedAttributes();
913         if (numAtts > 0) {
914             drawMembers(painter, UMLObject::ot_Attribute, m_attributeSignature, textX,
915                         bodyOffsetY, textWidth, fontHeight);
916             bodyOffsetY += fontHeight * numAtts;
917         }
918         else
919             bodyOffsetY += fontHeight / 2;
920     }
921 
922     // draw operations
923     if (visualProperty(ShowOperations)) {
924         // draw dividing line between attributes and operations
925         setPenFromSettings(painter);
926         painter->drawLine(0, bodyOffsetY, w, bodyOffsetY);
927         painter->setPen(QPen(textColor()));
928 
929         const int numOps = displayedOperations();
930         if (numOps >= 0) {
931             drawMembers(painter, UMLObject::ot_Operation, m_operationSignature, textX,
932                         bodyOffsetY, textWidth, fontHeight);
933         }
934     }
935 
936     if (DiagramProxyWidget::linkedDiagram()) {
937         DiagramProxyWidget::paint(painter, option, widget);
938     }
939 
940     UMLWidget::paint(painter, option, widget);
941 }
942 
943 /**
944  * @return The shape of the ClassifierWidget.
945  */
shape() const946 QPainterPath ClassifierWidget::shape() const
947 {
948     QPainterPath path;
949     if (classifier() && classifier()->isInterface() && visualProperty(DrawAsCircle)) {
950         path.addEllipse(QRectF(QPointF(), calculateAsCircleSize()));
951         return path;
952     }
953     QSizeF mainSize = rect().size();
954     QSize templatesBoxSize = calculateTemplatesBoxSize();
955     qreal mainY = 0.0;
956     if (templatesBoxSize.height() > 0) {
957         mainY += templatesBoxSize.height() - defaultMargin;
958         path.addRect(QRectF(mainSize.width() - templatesBoxSize.width() / 2, 0.0,
959                             templatesBoxSize.width(), templatesBoxSize.height()));
960     }
961     path.addRect(QRectF(0.0, mainY, mainSize.width(), mainSize.height()));
962     return path;
963 }
964 
965 /**
966  * Draws the interface as a circle.
967  * Only applies when m_umlObject->getBaseType() is ot_Interface.
968  */
drawAsCircle(QPainter * painter,const QStyleOptionGraphicsItem * option)969 void ClassifierWidget::drawAsCircle(QPainter *painter, const QStyleOptionGraphicsItem *option)
970 {
971     Q_UNUSED(option);
972 
973     const int w = width();
974     bool showProvider = associationWidgetList().size() == 0;
975     bool showRequired = false;
976     AssociationWidgetList requiredAssocs;
977 
978     foreach (AssociationWidget *aw, associationWidgetList()) {
979         const Uml::AssociationType::Enum aType = aw->associationType();
980         UMLWidget *otherEnd = aw->widgetForRole(Uml::RoleType::A);
981         UMLWidget *thisEnd = aw->widgetForRole(Uml::RoleType::B);
982         if (aType == Uml::AssociationType::UniAssociation ||
983                 aType == Uml::AssociationType::Association) {
984             if (otherEnd->baseType() == WidgetBase::wt_Component ||
985                     otherEnd->baseType() == WidgetBase::wt_Port)  // provider
986                 showProvider = true;
987             else if (thisEnd->baseType() == WidgetBase::wt_Component ||
988                      thisEnd->baseType() == WidgetBase::wt_Port) {
989                 showRequired = true;
990                 requiredAssocs.push_back(aw);
991             }
992         }
993     }
994 
995     if (showProvider || !showRequired)
996         painter->drawEllipse(w/2 - CIRCLE_SIZE/2, SOCKET_INCREMENT / 2, CIRCLE_SIZE, CIRCLE_SIZE);
997 
998     if (showRequired) {
999         // Draw socket for required interface.
1000         const qreal angleSpan = 180;   // 360.0 / (m_Assocs.size() + 1.0);
1001         const int arcDiameter = CIRCLE_SIZE + SOCKET_INCREMENT;
1002         QRect requireArc(w/2 - arcDiameter/2, 0, arcDiameter, arcDiameter);
1003         const QPointF center(x() + w/2, y() + arcDiameter/2);
1004         const qreal cX = center.x();
1005         const qreal cY = center.y();
1006 
1007         foreach (AssociationWidget *aw, requiredAssocs) {
1008             AssociationLine *assocLine = aw->associationLine();
1009             const QPointF p(assocLine->endPoint());
1010             const qreal tolerance = 18.0;
1011             bool drawArc = true;
1012             qreal midAngle;
1013             if (p.x() < cX - tolerance) {
1014                 if (p.y() < cY - tolerance)
1015                     midAngle = 135;
1016                 else if (p.y() > cY + tolerance)
1017                     midAngle = 225;
1018                 else
1019                     midAngle = 180;
1020             } else if (p.x() > cX + tolerance) {
1021                 if (p.y() < cY - tolerance)
1022                     midAngle = 45;
1023                 else if (p.y() > cY + tolerance)
1024                     midAngle = 315;
1025                 else
1026                     midAngle = 0;
1027             } else {
1028                 if (p.y() < cY - tolerance)
1029                     midAngle = 90;
1030                 else if (p.y() > cY + tolerance)
1031                     midAngle = 270;
1032                 else
1033                     drawArc = false;
1034             }
1035             if (drawArc) {
1036                 // uDebug() << "number of assocs: " << m_Assocs.size()
1037                 //          << ", p: " << p << ", center: " << center
1038                 //          << ", midAngle: " << midAngle << ", angleSpan: " << angleSpan;
1039                 painter->drawArc(requireArc, 16 * (midAngle - angleSpan/2), 16 * angleSpan);
1040             } else {
1041                 uError() << "socket: assocLine endPoint " << p
1042                          << " too close to own center" << center;
1043             }
1044         }
1045     }
1046 }
1047 
1048 /**
1049  * Calculates the size of the object when drawn as a circle.
1050  * Only applies when m_umlObject->getBaseType() is ot_Interface.
1051  */
calculateAsCircleSize() const1052 QSize ClassifierWidget::calculateAsCircleSize() const
1053 {
1054     int circleSize = CIRCLE_SIZE;
1055     circleSize += SOCKET_INCREMENT;
1056     return QSize(circleSize, circleSize);
1057 }
1058 
drawAsPackage(QPainter * painter,const QStyleOptionGraphicsItem * option)1059 void ClassifierWidget::drawAsPackage(QPainter *painter, const QStyleOptionGraphicsItem *option)
1060 {
1061     Q_UNUSED(option);
1062 
1063     int w = width();
1064     int h = height();
1065     QFont font = UMLWidget::font();
1066     font.setBold(true);
1067     //FIXME italic is true when a package is first created until you click elsewhere, not sure why
1068     font.setItalic(false);
1069     const QFontMetrics &fm = getFontMetrics(FT_BOLD);
1070     const int fontHeight  = fm.lineSpacing();
1071 
1072     painter->drawRect(0, 0, 50, fontHeight);
1073     if (m_umlObject->stereotype() == QLatin1String("subsystem")) {
1074         const int fHalf = fontHeight / 2;
1075         const int symY = fHalf;
1076         const int symX = 38;
1077         painter->drawLine(symX, symY, symX, symY + fHalf - 2);          // left leg
1078         painter->drawLine(symX + 8, symY, symX + 8, symY + fHalf - 2);  // right leg
1079         painter->drawLine(symX, symY, symX + 8, symY);                  // waist
1080         painter->drawLine(symX + 4, symY, symX + 4, symY - fHalf + 2);  // head
1081     }
1082     painter->drawRect(0, fontHeight - 1, w, h - fontHeight);
1083 
1084     painter->setPen(textColor());
1085     painter->setFont(font);
1086 
1087     int lines = 1;
1088     QString stereotype = m_umlObject->stereotype();
1089     if (!stereotype.isEmpty()) {
1090         painter->drawText(0, fontHeight + defaultMargin,
1091                    w, fontHeight, Qt::AlignCenter, m_umlObject->stereotype(true));
1092         lines = 2;
1093     }
1094 
1095     painter->drawText(0, (fontHeight*lines) + defaultMargin,
1096                w, fontHeight, Qt::AlignCenter, name());
1097 }
1098 
calculateAsPackageSize() const1099 QSize ClassifierWidget::calculateAsPackageSize() const
1100 {
1101     const QFontMetrics &fm = getFontMetrics(FT_BOLD_ITALIC);
1102     const int fontHeight = fm.lineSpacing();
1103 
1104     int lines = 1;
1105 
1106     int width = fm.width(m_umlObject->name());
1107 
1108     int tempWidth = 0;
1109     if (!m_umlObject->stereotype().isEmpty()) {
1110         tempWidth = fm.width(m_umlObject->stereotype(true));
1111         lines = 2;
1112     }
1113     if (tempWidth > width)
1114         width = tempWidth;
1115     width += defaultMargin * 2;
1116     if (width < 70)
1117         width = 70;  // minumin width of 70
1118 
1119     int height = (lines*fontHeight) + fontHeight + (defaultMargin * 2);
1120 
1121     return QSize(width, height);
1122 }
1123 
1124 /**
1125  * Auxiliary method for draw() of child classes:
1126  * Draw the attributes or operations.
1127  *
1128  * @param p          QPainter to paint to.
1129  * @param ot         Object type to draw, either ot_Attribute or ot_Operation.
1130  * @param sigType    Governs details of the member display.
1131  * @param x          X coordinate at which to draw the texts.
1132  * @param y          Y coordinate at which text drawing commences.
1133  * @param width      The text width.
1134  * @param height     The text height.
1135  */
drawMembers(QPainter * painter,UMLObject::ObjectType ot,Uml::SignatureType::Enum sigType,int x,int y,int width,int height)1136 void ClassifierWidget::drawMembers(QPainter * painter,
1137                                    UMLObject::ObjectType ot,
1138                                    Uml::SignatureType::Enum sigType,
1139                                    int x, int y, int width, int height)
1140 {
1141     UMLClassifier *umlc = classifier();
1142     const bool drawInstanceAttributes = (!umlc && m_umlObject && m_umlObject->isUMLInstance()
1143                                                             && ot == UMLObject::ot_Attribute);
1144     if (!umlc && !drawInstanceAttributes) {
1145         return;
1146     }
1147     QFont f = UMLWidget::font();
1148     f.setBold(false);
1149     painter->setClipping(true);
1150     painter->setClipRect(rect());
1151     UMLObjectList ialist;
1152     if (drawInstanceAttributes) {
1153         UMLInstance *umlinst = m_umlObject->asUMLInstance();
1154         umlc = umlinst->classifier();
1155         ialist = umlinst->subordinates();
1156     }
1157     UMLClassifierListItemList list = umlc->getFilteredList(ot);
1158     for (int i = 0; i < list.count(); i++) {
1159         UMLClassifierListItem *obj = list.at(i);
1160         uIgnoreZeroPointer(obj);
1161         if (visualProperty(ShowPublicOnly) && obj->visibility() != Uml::Visibility::Public)
1162             continue;
1163         QString text;
1164         if (drawInstanceAttributes) {
1165             UMLInstanceAttribute *iatt = ialist.at(i)->asUMLInstanceAttribute();
1166             if (!iatt) {
1167                 uDebug() << "ClassifierWidget::drawMembers(" << obj->name()
1168                          << ") : skipping non InstanceAttribute subordinate";
1169                 continue;
1170             }
1171             /* CHECK: Do we want visibility indication on instance attributes?
1172             if (sigType == Uml::SignatureType::ShowSig || sigType == Uml::SignatureType::NoSig)
1173                 text = Uml::Visibility::toString(iatt->visibility(), true) + QLatin1Char(' ');
1174              */
1175             text.append(iatt->toString());
1176         } else {
1177             text = obj->toString(sigType, visualProperty(ShowStereotype));
1178         }
1179         f.setItalic(obj->isAbstract());
1180         f.setUnderline(obj->isStatic());
1181         painter->setFont(f);
1182         painter->drawText(x, y, width, height, Qt::AlignVCenter, text);
1183         f.setItalic(false);
1184         f.setUnderline(false);
1185         painter->setFont(f);
1186         y += height;
1187     }
1188     painter->setClipping(false);
1189 }
1190 
1191 /**
1192  * Override method from UMLWidget in order to additionally check m_pInterfaceName.
1193  *
1194  * @param p Point to be checked.
1195  *
1196  * @return 'this' if UMLWidget::onWidget(p) returns non 0;
1197  *         m_pInterfaceName if m_pName is non NULL and
1198  *         m_pInterfaceName->onWidget(p) returns non 0; else NULL.
1199  */
onWidget(const QPointF & p)1200 UMLWidget* ClassifierWidget::onWidget(const QPointF &p)
1201 {
1202     if (UMLWidget::onWidget(p) != nullptr)
1203         return this;
1204     if (getDrawAsCircle() && m_pInterfaceName) {
1205         uDebug() << "floatingtext: " << m_pInterfaceName->text();
1206         return m_pInterfaceName->onWidget(p);
1207     }
1208     return nullptr;
1209 }
1210 
1211 /**
1212  * Reimplement function from UMLWidget.
1213  */
widgetWithID(Uml::ID::Type id)1214 UMLWidget* ClassifierWidget::widgetWithID(Uml::ID::Type id)
1215 {
1216     if (UMLWidget::widgetWithID(id))
1217         return this;
1218     if (getDrawAsCircle() && m_pInterfaceName && m_pInterfaceName->widgetWithID(id))
1219         return m_pInterfaceName;
1220     return nullptr;
1221 }
1222 
setDocumentation(const QString & doc)1223 void ClassifierWidget::setDocumentation(const QString &doc)
1224 {
1225     WidgetBase::setDocumentation(doc);
1226     updateGeometry();
1227 }
1228 
1229 /**
1230  * Sets whether to draw as circle.
1231  * Only applies when m_umlObject->getBaseType() is ot_Interface.
1232  *
1233  * @param drawAsCircle   True if widget shall be drawn as circle.
1234  */
setDrawAsCircle(bool drawAsCircle)1235 void ClassifierWidget::setDrawAsCircle(bool drawAsCircle)
1236 {
1237     setVisualPropertyCmd(DrawAsCircle, drawAsCircle);
1238     const int circleSize = CIRCLE_SIZE + SOCKET_INCREMENT;
1239     if (drawAsCircle) {
1240         setX(x() + (width()/2 - circleSize/2));
1241         setY(y() + (height()/2 - circleSize/2));
1242         setSize(circleSize, circleSize);
1243         if (m_pInterfaceName) {
1244             m_pInterfaceName->show();
1245         } else {
1246             m_pInterfaceName = new FloatingTextWidget(m_scene, Uml::TextRole::Floating, name());
1247             m_pInterfaceName->setParentItem(this);
1248             m_pInterfaceName->setText(name());  // to get geometry update
1249             m_pInterfaceName->setX(circleSize/2 - m_pInterfaceName->width() / 2);
1250             m_pInterfaceName->setY(circleSize + SOCKET_INCREMENT);
1251         }
1252         m_resizable = false;
1253     } else {
1254         setSize(ClassifierWidget::minimumSize());
1255         setX(x() - (width()/2 - circleSize/2));
1256         setY(y() - (height()/2 - circleSize/2));
1257         if (m_pInterfaceName)
1258             m_pInterfaceName->hide();
1259         m_resizable = true;
1260     }
1261     setChangesShape(drawAsCircle);
1262     updateGeometry();
1263     update();
1264 }
1265 
1266 /**
1267  * Returns whether to draw as circle.
1268  * Only applies when m_umlObject->getBaseType() is ot_Interface.
1269  *
1270  * @return   True if widget is drawn as circle.
1271  */
getDrawAsCircle() const1272 bool ClassifierWidget::getDrawAsCircle() const
1273 {
1274     return visualProperty(DrawAsCircle);
1275 }
1276 
1277 /**
1278  * Toggles whether to draw as circle.
1279  * Only applies when m_umlObject->getBaseType() is ot_Interface.
1280  */
toggleDrawAsCircle()1281 void ClassifierWidget::toggleDrawAsCircle()
1282 {
1283     toggleVisualProperty(DrawAsCircle);
1284     updateSignatureTypes();
1285     updateGeometry();
1286     update();
1287 }
1288 
1289 /**
1290  * Changes this classifier from an interface to a class.
1291  * Attributes and stereotype visibility is got from the view OptionState.
1292  * This widget is also updated.
1293  */
changeToClass()1294 void ClassifierWidget::changeToClass()
1295 {
1296     setBaseType(WidgetBase::wt_Class);
1297     m_umlObject->setBaseType(UMLObject::ot_Class);
1298     setVisualPropertyCmd(DrawAsCircle, false);
1299     const Settings::OptionState& ops = m_scene->optionState();
1300     setVisualProperty(ShowAttributes, ops.classState.showAtts);
1301     setShowStereotype(ops.classState.showStereoType);
1302 
1303     updateGeometry();
1304     update();
1305 }
1306 
1307 /**
1308  * Changes this classifier from a class to an interface.
1309  * Attributes are hidden and stereotype is shown.
1310  * This widget is also updated.
1311  */
changeToInterface()1312 void ClassifierWidget::changeToInterface()
1313 {
1314     setBaseType(WidgetBase::wt_Interface);
1315     m_umlObject->setBaseType(UMLObject::ot_Interface);
1316 
1317     setVisualProperty(ShowAttributes, false);
1318     setShowStereotype(Settings::optionState().classState.showStereoType);
1319 
1320     updateGeometry();
1321     update();
1322 }
1323 
1324 /**
1325  * Changes this classifier from an "class-or-package" to a package.
1326  * This widget is also updated.
1327  */
changeToPackage()1328 void ClassifierWidget::changeToPackage()
1329 {
1330     setBaseType(WidgetBase::wt_Package);
1331     m_umlObject->setBaseType(UMLObject::ot_Package);
1332 
1333     setVisualProperty(ShowAttributes, false);
1334     setShowStereotype(Uml::ShowStereoType::Name);
1335 
1336     updateGeometry();
1337     update();
1338 }
1339 
1340 /**
1341  * Loads the "classwidget" or "interfacewidget" XML element.
1342  */
loadFromXMI1(QDomElement & qElement)1343 bool ClassifierWidget::loadFromXMI1(QDomElement & qElement)
1344 {
1345     if (!UMLWidget::loadFromXMI1(qElement)) {
1346         return false;
1347     }
1348     if (DiagramProxyWidget::linkedDiagram())
1349         DiagramProxyWidget::setShowLinkedDiagram(false);
1350 
1351     bool loadShowAttributes = true;
1352     if (umlObject() && (umlObject()->isUMLPackage() || umlObject()->isUMLInstance())) {
1353         loadShowAttributes = false;
1354     }
1355     if (loadShowAttributes) {
1356         QString showatts = qElement.attribute(QLatin1String("showattributes"), QLatin1String("0"));
1357         QString showops = qElement.attribute(QLatin1String("showoperations"), QLatin1String("1"));
1358         QString showpubliconly = qElement.attribute(QLatin1String("showpubliconly"), QLatin1String("0"));
1359         QString showattsigs = qElement.attribute(QLatin1String("showattsigs"), QLatin1String("600"));
1360         QString showopsigs = qElement.attribute(QLatin1String("showopsigs"), QLatin1String("600"));
1361         QString showpackage = qElement.attribute(QLatin1String("showpackage"), QLatin1String("0"));
1362         QString showscope = qElement.attribute(QLatin1String("showscope"), QLatin1String("0"));
1363         QString drawascircle = qElement.attribute(QLatin1String("drawascircle"), QLatin1String("0"));
1364         QString showstereotype = qElement.attribute(QLatin1String("showstereotype"), QLatin1String("1"));
1365         setVisualPropertyCmd(ShowAttributes, (bool)showatts.toInt());
1366         setVisualPropertyCmd(ShowOperations, (bool)showops.toInt());
1367         setVisualPropertyCmd(ShowPublicOnly, (bool)showpubliconly.toInt());
1368         setVisualPropertyCmd(ShowPackage,    (bool)showpackage.toInt());
1369         setVisualPropertyCmd(ShowVisibility, (bool)showscope.toInt());
1370         setVisualPropertyCmd(DrawAsCircle,   (bool)drawascircle.toInt());
1371         setShowStereotype((Uml::ShowStereoType::Enum)showstereotype.toInt());
1372         m_attributeSignature = Uml::SignatureType::fromInt(showattsigs.toInt());
1373         m_operationSignature = Uml::SignatureType::fromInt(showopsigs.toInt());
1374     }
1375 
1376 #ifdef ENABLE_WIDGET_SHOW_DOC
1377     QString showDocumentation = qElement.attribute(QLatin1String("showdocumentation"), QLatin1String("0"));
1378     setVisualPropertyCmd(ShowDocumentation, (bool)showDocumentation.toInt());
1379 #endif
1380 
1381     if (!getDrawAsCircle())
1382         return true;
1383 
1384     // Optional child element: floatingtext
1385     QDomNode node = qElement.firstChild();
1386     QDomElement element = node.toElement();
1387     if (!element.isNull()) {
1388         QString tag = element.tagName();
1389         if (tag == QLatin1String("floatingtext")) {
1390             if (m_pInterfaceName == nullptr) {
1391                 m_pInterfaceName = new FloatingTextWidget(m_scene,
1392                                                           Uml::TextRole::Floating,
1393                                                           name(), Uml::ID::Reserved);
1394                 m_pInterfaceName->setParentItem(this);
1395             }
1396             if (!m_pInterfaceName->loadFromXMI1(element)) {
1397                 // Most likely cause: The FloatingTextWidget is empty.
1398                 delete m_pInterfaceName;
1399                 m_pInterfaceName = nullptr;
1400             } else {
1401                 m_pInterfaceName->activate();
1402                 m_pInterfaceName->update();
1403             }
1404         } else {
1405             uError() << "unknown tag " << tag;
1406         }
1407     }
1408 
1409     return true;
1410 }
1411 
1412 /**
1413  * Creates the "classwidget" or "interfacewidget" XML element.
1414  */
saveToXMI1(QXmlStreamWriter & writer)1415 void ClassifierWidget::saveToXMI1(QXmlStreamWriter& writer)
1416 {
1417     bool saveShowAttributes = true;
1418     UMLClassifier *umlc = classifier();
1419     QString tag;
1420     if (umlObject()->baseType() == UMLObject::ot_Package) {
1421         tag = QLatin1String("packagewidget");
1422         saveShowAttributes = false;
1423     } else if (umlObject()->baseType() == UMLObject::ot_Instance) {
1424         tag = QLatin1String("instancewidget");
1425         saveShowAttributes = false;
1426     } else if (umlc && umlc->isInterface()) {
1427         tag = QLatin1String("interfacewidget");
1428     } else {
1429         tag = QLatin1String("classwidget");
1430     }
1431     writer.writeStartElement(tag);
1432 
1433     UMLWidget::saveToXMI1(writer);
1434     if (saveShowAttributes) {
1435         writer.writeAttribute(QLatin1String("showoperations"), QString::number(visualProperty(ShowOperations)));
1436         writer.writeAttribute(QLatin1String("showpubliconly"), QString::number(visualProperty(ShowPublicOnly)));
1437         writer.writeAttribute(QLatin1String("showopsigs"),     QString::number(m_operationSignature));
1438         writer.writeAttribute(QLatin1String("showpackage"),    QString::number(visualProperty(ShowPackage)));
1439         writer.writeAttribute(QLatin1String("showscope"),      QString::number(visualProperty(ShowVisibility)));
1440         writer.writeAttribute(QLatin1String("showattributes"), QString::number(visualProperty(ShowAttributes)));
1441         writer.writeAttribute(QLatin1String("showattsigs"),    QString::number(m_attributeSignature));
1442         writer.writeAttribute(QLatin1String("showstereotype"), QString::number(m_showStereotype));
1443     }
1444 #ifdef ENABLE_WIDGET_SHOW_DOC
1445     writer.writeAttribute(QLatin1String("showdocumentation"), QString::number(visualProperty(ShowDocumentation)));
1446 #endif
1447     if (umlc && (umlc->isInterface() || umlc->isAbstract())) {
1448         writer.writeAttribute(QLatin1String("drawascircle"), QString::number(visualProperty(DrawAsCircle)));
1449         if (visualProperty(DrawAsCircle) && m_pInterfaceName) {
1450             m_pInterfaceName->saveToXMI1(writer);
1451         }
1452     }
1453     writer.writeEndElement();
1454 }
1455 
1456 /**
1457  * Will be called when a menu selection has been made from the
1458  * popup menu.
1459  *
1460  * @param action   The action that has been selected.
1461  */
slotMenuSelection(QAction * action)1462 void ClassifierWidget::slotMenuSelection(QAction* action)
1463 {
1464     ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(action);
1465     uDebug() << "ClassifierWidget::slotMenuSelection sel = " << sel;
1466     switch (sel) {
1467     case ListPopupMenu::mt_Attribute:
1468     case ListPopupMenu::mt_Operation:
1469     case ListPopupMenu::mt_Template:
1470         {
1471             UMLObject::ObjectType ot = ListPopupMenu::convert_MT_OT(sel);
1472             UMLClassifier *umlc = classifier();
1473             if (!umlc) {
1474                 uError() << "Internal error - classifier() returns NULL";
1475                 return;
1476             }
1477             if (Object_Factory::createChildObject(umlc, ot)) {
1478                 updateGeometry();
1479                 update();
1480                 UMLApp::app()->document()->setModified();
1481             }
1482             break;
1483         }
1484 
1485     case ListPopupMenu::mt_Class:
1486     case ListPopupMenu::mt_Datatype:
1487     case ListPopupMenu::mt_Enum:
1488     case ListPopupMenu::mt_Interface:
1489         {
1490             UMLObject::ObjectType ot = ListPopupMenu::convert_MT_OT(sel);
1491             UMLClassifier *umlc = classifier();
1492             if (!umlc) {
1493                 uError() << "Internal error - classifier() returns NULL";
1494                 return;
1495             }
1496             umlScene()->setCreateObject(true);
1497             if (Object_Factory::createUMLObject(ot, QString(), umlc)) {
1498                 updateGeometry();
1499                 update();
1500                 UMLApp::app()->document()->setModified();
1501             }
1502             break;
1503         }
1504 
1505     case ListPopupMenu::mt_Show_Operations:
1506         toggleVisualProperty(ShowOperations);
1507         break;
1508 
1509     case ListPopupMenu::mt_Show_Attributes:
1510         toggleVisualProperty(ShowAttributes);
1511         break;
1512 
1513     case ListPopupMenu::mt_Show_Documentation:
1514         toggleVisualProperty(ShowDocumentation);
1515         break;
1516 
1517     case ListPopupMenu::mt_Show_Public_Only:
1518         toggleVisualProperty(ShowPublicOnly);
1519         break;
1520 
1521     case ListPopupMenu::mt_Show_Operation_Signature:
1522         toggleVisualProperty(ShowOperationSignature);
1523         break;
1524 
1525     case ListPopupMenu::mt_Show_Attribute_Signature:
1526         toggleVisualProperty(ShowAttributeSignature);
1527         break;
1528 
1529     case ListPopupMenu::mt_Visibility:
1530         toggleVisualProperty(ShowVisibility);
1531         break;
1532 
1533     case ListPopupMenu::mt_Show_Packages:
1534         toggleVisualProperty(ShowPackage);
1535         break;
1536 
1537     case ListPopupMenu::mt_Show_Stereotypes:
1538         toggleVisualProperty(ShowStereotype);
1539         break;
1540 
1541     case ListPopupMenu::mt_DrawAsCircle:
1542         toggleVisualProperty(DrawAsCircle);
1543         break;
1544 
1545     case ListPopupMenu::mt_ChangeToClass:
1546         changeToClass();
1547         break;
1548 
1549     case ListPopupMenu::mt_ChangeToInterface:
1550         changeToInterface();
1551         break;
1552 
1553     case ListPopupMenu::mt_ChangeToPackage:
1554         changeToPackage();
1555         break;
1556 
1557     default:
1558         DiagramProxyWidget::slotMenuSelection(action);
1559         break;
1560     }
1561 }
1562 
1563 /**
1564  * Slot to show/hide attributes based on \a state.
1565  */
slotShowAttributes(bool state)1566 void ClassifierWidget::slotShowAttributes(bool state)
1567 {
1568     setVisualProperty(ShowAttributes, state);
1569 }
1570 
1571 /**
1572  * Slot to show/hide operations based on \a state.
1573  */
slotShowOperations(bool state)1574 void ClassifierWidget::slotShowOperations(bool state)
1575 {
1576     setVisualProperty(ShowOperations, state);
1577 }
1578 
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)1579 void ClassifierWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
1580 {
1581     if (linkedDiagram())
1582         DiagramProxyWidget::mouseDoubleClickEvent(event);
1583     if (event->isAccepted())
1584         UMLWidget::mouseDoubleClickEvent(event);
1585 }
1586 
1587 /**
1588  * Show a properties dialog for a ClassifierWidget
1589  */
showPropertiesDialog()1590 bool ClassifierWidget::showPropertiesDialog()
1591 {
1592     if (UMLWidget::showPropertiesDialog()) {
1593         if (isInterfaceWidget() && visualProperty(DrawAsCircle))
1594             m_pInterfaceName->setText(name());
1595         return true;
1596     }
1597     return false;
1598 }
1599 
1600 /**
1601  * Overriding the method from WidgetBase because we need to do
1602  * something extra in case this ClassifierWidget represents
1603  * an interface widget used in component diagrams.
1604  */
setUMLObject(UMLObject * obj)1605 void ClassifierWidget::setUMLObject(UMLObject *obj)
1606 {
1607     WidgetBase::setUMLObject(obj);
1608     if (isInterfaceWidget() && visualProperty(DrawAsCircle))
1609         m_pInterfaceName->setText(obj->name());
1610 }
1611