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