1 /*
2     SPDX-License-Identifier: GPL-2.0-or-later
3     SPDX-FileCopyrightText: 2002-2020 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
4 */
5 
6 // own header
7 #include "floatingtextwidget.h"
8 
9 // local includes
10 #include "association.h"
11 #include "associationwidget.h"
12 #include "associationpropertiesdialog.h"
13 #include "classifier.h"
14 #include "cmds.h"
15 #include "debug_utils.h"
16 #include "dialog_utils.h"
17 #include "linkwidget.h"
18 #include "classifierwidget.h"
19 #include "listpopupmenu.h"
20 #include "messagewidget.h"
21 #include "model_utils.h"
22 #include "object_factory.h"
23 #include "operation.h"
24 #include "selectoperationdialog.h"
25 #include "uml.h"
26 #include "umldoc.h"
27 #include "umlview.h"
28 
29 // kde includes
30 #if QT_VERSION < 0x050000
31 #include <kfontdialog.h>
32 #endif
33 #include <KLocalizedString>
34 
35 // qt includes
36 #if QT_VERSION >= 0x050000
37 #include <QFontDialog>
38 #endif
39 #include <QPointer>
40 #include <QRegExp>
41 #include <QPainter>
42 #include <QValidator>
43 #include <QXmlStreamWriter>
44 
DEBUG_REGISTER_DISABLED(FloatingTextWidget)45 DEBUG_REGISTER_DISABLED(FloatingTextWidget)
46 
47 /**
48  * Constructs a FloatingTextWidget instance.
49  *
50  * @param scene The parent of this FloatingTextWidget.
51  * @param role The role this FloatingTextWidget will take up.
52  * @param text The main text to display.
53  * @param id The ID to assign (-1 will prompt a new ID.)
54  */
55 FloatingTextWidget::FloatingTextWidget(UMLScene * scene, Uml::TextRole::Enum role, const QString& text, Uml::ID::Type id)
56   : UMLWidget(scene, WidgetBase::wt_Text, id),
57     m_linkWidget(0),
58     m_preText(QString()),
59     m_postText(QString()),
60     m_textRole(role),
61     m_unconstrainedPositionX(0),
62     m_unconstrainedPositionY(0),
63     m_movementDirectionX(0),
64     m_movementDirectionY(0)
65 {
66     m_Text = text;
67     m_resizable = false;
68     setZValue(10); //make sure always on top.
69 }
70 
71 /**
72  * Destructor.
73  */
~FloatingTextWidget()74 FloatingTextWidget::~FloatingTextWidget()
75 {
76 }
77 
78 /**
79  * Use to get the _main body_ of text (e.g. prepended and appended
80  * text is omitted) as currently displayed by the widget.
81  *
82  * @return The main text currently being displayed by the widget.
83  */
text() const84 QString FloatingTextWidget::text() const
85 {
86     // test to make sure not just the ":" between the seq number and
87     // the actual message widget
88 
89     // hmm. this section looks like it could have been avoided by
90     // using pre-, post- text instead of storing in the main body of
91     // the text -b.t.
92     if (m_textRole == Uml::TextRole::Seq_Message || m_textRole == Uml::TextRole::Seq_Message_Self ||
93             m_textRole == Uml::TextRole::Coll_Message || m_textRole == Uml::TextRole::Coll_Message_Self) {
94         if (m_Text.length() <= 1 || m_Text == QLatin1String(": "))
95             return QString();
96     }
97     return m_Text;
98 }
99 
100 /**
101  * Set the main body of text to display.
102  *
103  * @param t The text to display.
104  */
setText(const QString & t)105 void FloatingTextWidget::setText(const QString &t)
106 {
107     if (m_textRole == Uml::TextRole::Seq_Message || m_textRole == Uml::TextRole::Seq_Message_Self) {
108         QString op;
109         if (m_linkWidget)
110             op = m_linkWidget->lwOperationText();
111         if (op.length() > 0) {
112             if (!m_scene->showOpSig())
113                 op.replace(QRegExp(QLatin1String("\\(.*\\)")), QLatin1String("()"));
114             m_Text = op;
115         }
116         else
117             m_Text = t;
118     }
119     else {
120         m_Text = t;
121     }
122 
123     QSizeF s = minimumSize();
124     setSize(s.width(), s.height());
125 
126     updateGeometry();
127     update();
128 }
129 
130 /**
131  * Set some text to be prepended to the main body of text.
132  * @param t The text to prepend to main body which is displayed.
133  */
setPreText(const QString & t)134 void FloatingTextWidget::setPreText(const QString &t)
135 {
136     m_preText = t;
137     updateGeometry();
138     update();
139 }
140 
141 /**
142  * Set some text to be appended to the main body of text.
143  * @param t The text to append to main body which is displayed.
144  */
setPostText(const QString & t)145 void FloatingTextWidget::setPostText(const QString &t)
146 {
147     m_postText = t;
148     updateGeometry();
149     update();
150 }
151 
152 /**
153  * Use to get the total text (prepended + main body + appended)
154  * currently displayed by the widget.
155  *
156  * @return The text currently being displayed by the widget.
157  */
displayText() const158 QString FloatingTextWidget::displayText() const
159 {
160     QString displayText;
161     if (!m_SequenceNumber.isEmpty())
162         displayText = m_SequenceNumber + QLatin1String(": ") + m_Text;
163     else
164         displayText = m_Text;
165     displayText.prepend(m_preText);
166     displayText.append(m_postText);
167     return displayText;
168 }
169 
170 /**
171  * Return state if no pre, post and main text is empty
172  * @return true if widget contains no text
173  */
isEmpty()174 bool FloatingTextWidget::isEmpty()
175 {
176     return m_Text.isEmpty() && m_preText.isEmpty() && m_postText.isEmpty();
177 }
178 
179 /**
180  * Overrides method from UMLWidget.
181  */
minimumSize() const182 QSizeF FloatingTextWidget::minimumSize() const
183 {
184     const QFontMetrics &fm = getFontMetrics(FT_NORMAL);
185     int h = fm.lineSpacing();
186     int w = fm.width(displayText());
187     return QSizeF(w + 8, h + 4);  // give a small margin
188 }
189 
190 /**
191  * Method used by setText: its called by  cmdsetTxt, Don't use it!
192  *
193  * @param t The text to display.
194  */
setTextcmd(const QString & t)195 void FloatingTextWidget::setTextcmd(const QString &t)
196 {
197     UMLApp::app()->executeCommand(new Uml::CmdSetTxt(this, t));
198 }
199 
200 /**
201  * Displays a dialog box to change the text.
202  */
showChangeTextDialog()203 void FloatingTextWidget::showChangeTextDialog()
204 {
205     QString newText = text();
206     bool ok = Dialog_Utils::askName(i18n("Change Text"),
207                                     i18n("Enter new text:"),
208                                     newText);
209     if (ok && newText != text() && isTextValid(newText)) {
210         setText(newText);
211         setVisible(!text().isEmpty());
212         updateGeometry();
213         update();
214     }
215     if (!isTextValid(newText))
216         hide();
217 }
218 
219 /**
220  * Shows an operation dialog box.
221  *
222  * @param enableAutoIncrement Enable auto increment checkbox
223  */
showOperationDialog(bool enableAutoIncrement)224 bool FloatingTextWidget::showOperationDialog(bool enableAutoIncrement)
225 {
226     if (!m_linkWidget) {
227         uError() << "m_linkWidget is NULL";
228         return false;
229     }
230     if (!m_linkWidget->lwClassifier()) {
231         uError() << "m_linkWidget->lwClassifier() returns a NULL classifier";
232         return false;
233     }
234     bool result = false;
235 
236     QPointer<SelectOperationDialog> selectDialog = new SelectOperationDialog(m_scene->activeView(), m_linkWidget->lwClassifier(), m_linkWidget, enableAutoIncrement);
237     if (selectDialog->exec()) {
238         selectDialog->apply();
239         setMessageText();
240         result = true;
241     }
242     delete selectDialog;
243     return result;
244 }
245 
246 /**
247  * Show the properties for a FloatingTextWidget.
248  * Depending on the role of the floating text widget, the options dialog
249  * for the floating text widget, the rename dialog for floating text or
250  * the options dialog for the link widget are shown.
251  */
showPropertiesDialog()252 bool FloatingTextWidget::showPropertiesDialog()
253 {
254     UMLWidget *p = dynamic_cast<UMLWidget*>(parentItem());
255     if (p && p->isInterfaceWidget()) {
256         if (p->showPropertiesDialog())
257             setText(p->name());
258     } else if (m_textRole == Uml::TextRole::Coll_Message || m_textRole == Uml::TextRole::Coll_Message_Self ||
259             m_textRole == Uml::TextRole::Seq_Message || m_textRole == Uml::TextRole::Seq_Message_Self) {
260         return showOperationDialog(false);
261     } else if (m_textRole == Uml::TextRole::Floating) {
262         // double clicking on a text line opens the dialog to change the text
263         return handleRename();
264     } else if (m_linkWidget) {
265         return m_linkWidget->showPropertiesDialog();
266     }
267     return false;
268 }
269 
270 /**
271  * Use to get the pre-text which is prepended to the main body of
272  * text to be displayed.
273  *
274  * @return The pre-text currently displayed by the widget.
275  */
preText() const276 QString FloatingTextWidget::preText() const
277 {
278     return m_preText;
279 }
280 
281 /**
282  * Use to get the post-text which is appended to the main body of
283  * text to be displayed.
284  *
285  * @return The post-text currently displayed by the widget.
286  */
postText() const287 QString FloatingTextWidget::postText() const
288 {
289     return m_postText;
290 }
291 
292 /**
293  * Activate the FloatingTextWidget after the saved data has been loaded
294  *
295  * @param ChangeLog Pointer to the IDChangeLog.
296  * @return  true for success
297  */
activate(IDChangeLog * ChangeLog)298 bool FloatingTextWidget::activate(IDChangeLog* ChangeLog /*= 0 */)
299 {
300     if (! UMLWidget::activate(ChangeLog))
301         return false;
302     update();
303     return true;
304 }
305 
306 /**
307  * Set the LinkWidget that this FloatingTextWidget is related to.
308  *
309  * @param l The related LinkWidget.
310  */
setLink(LinkWidget * l)311 void FloatingTextWidget::setLink(LinkWidget * l)
312 {
313     m_linkWidget = l;
314 }
315 
316 /**
317  * Returns the LinkWidget this floating text is related to.
318  *
319  * @return The LinkWidget this floating text is related to.
320  */
link() const321 LinkWidget * FloatingTextWidget::link() const
322 {
323     return m_linkWidget;
324 }
325 
326 /**
327  * Sets the role type of this FloatingTextWidget.
328  *
329  * @param role  The TextRole::Enum of this FloatingTextWidget.
330  */
setTextRole(Uml::TextRole::Enum role)331 void FloatingTextWidget::setTextRole(Uml::TextRole::Enum role)
332 {
333     m_textRole = role;
334 }
335 
336 /**
337  * Return the role of the text widget
338  * @return The TextRole::Enum of this FloatingTextWidget.
339  */
textRole() const340 Uml::TextRole::Enum FloatingTextWidget::textRole() const
341 {
342     return m_textRole;
343 }
344 
345 /**
346  * Handle the ListPopupMenu::mt_Rename case of the slotMenuSelection.
347  * Given an own method because it requires rather lengthy code.
348  */
handleRename()349 bool FloatingTextWidget::handleRename()
350 {
351     QString t;
352     if (m_textRole == Uml::TextRole::RoleAName || m_textRole == Uml::TextRole::RoleBName) {
353         t = i18n("Enter role name:");
354     } else if (m_textRole == Uml::TextRole::MultiA || m_textRole == Uml::TextRole::MultiB) {
355         t = i18n("Enter multiplicity:");
356         /*
357         // NO! shouldn't be allowed
358         } else if (m_textRole == Uml::TextRole::ChangeA || m_textRole == Uml::TextRole::ChangeB) {
359         t = i18n("Enter changeability");
360         */
361     } else if (m_textRole == Uml::TextRole::Name) {
362         t = i18n("Enter association name:");
363     } else if (m_textRole == Uml::TextRole::Floating) {
364         t = i18n("Enter new text:");
365     } else {
366         t = i18n("ERROR");
367     }
368     QString newText = text();
369     bool ok = Dialog_Utils::askName(i18n("Rename"), t, newText);
370     if (!ok || newText == text()) {
371         return false;
372     }
373 
374     UMLApp::app()->executeCommand(new Uml::CmdHandleRename(this, newText));
375     return true;
376 }
377 
378 /**
379  * Changes the text of linked widget.
380  * @param newText   the new text
381  */
changeName(const QString & newText)382 void FloatingTextWidget::changeName(const QString& newText)
383 {
384     if (m_linkWidget && !isTextValid(newText)) {
385         AssociationWidget *assoc = dynamic_cast<AssociationWidget*>(m_linkWidget);
386         if (assoc) {
387             switch (m_textRole) {
388               case Uml::TextRole::MultiA:
389                 assoc->setMultiplicity(QString(), Uml::RoleType::A);
390                 break;
391               case Uml::TextRole::MultiB:
392                 assoc->setMultiplicity(QString(), Uml::RoleType::B);
393                 break;
394               case Uml::TextRole::RoleAName:
395                 assoc->setRoleName(QString(), Uml::RoleType::A);
396                 break;
397               case Uml::TextRole::RoleBName:
398                 assoc->setRoleName(QString(), Uml::RoleType::B);
399                 break;
400               case Uml::TextRole::ChangeA:
401                 assoc->setChangeability(Uml::Changeability::Changeable, Uml::RoleType::A);
402                 break;
403               case Uml::TextRole::ChangeB:
404                 assoc->setChangeability(Uml::Changeability::Changeable, Uml::RoleType::B);
405                 break;
406               default:
407                 assoc->setName(QString());
408                 break;
409             }
410         }
411         else {
412             MessageWidget *msg = dynamic_cast<MessageWidget*>(m_linkWidget);
413             if (msg) {
414                 msg->setName(QString());
415                 m_scene->removeWidget(this);
416             }
417         }
418         return;
419     }
420 
421     if (m_linkWidget && m_textRole != Uml::TextRole::Seq_Message
422                      && m_textRole != Uml::TextRole::Seq_Message_Self) {
423         m_linkWidget->setText(this, newText);
424     }
425     else {
426         setText(newText);
427         UMLApp::app()->document()->setModified(true);
428     }
429 
430     setVisible(true);
431     updateGeometry();
432     update();
433 }
434 
435 /**
436  * Write property of QString m_SequenceNumber.
437  */
setSequenceNumber(const QString & sequenceNumber)438 void FloatingTextWidget::setSequenceNumber(const QString &sequenceNumber)
439 {
440     m_SequenceNumber = sequenceNumber;
441 }
442 
443 /**
444  * Read property of QString m_SequenceNumber.
445  */
sequenceNumber() const446 QString FloatingTextWidget::sequenceNumber() const
447 {
448     return m_SequenceNumber;
449 }
450 
451 /**
452  * For a text to be valid it must be non-empty, i.e. have a length
453  * larger than zero, and have at least one non whitespace character.
454  *
455  * @param text The string to analyze.
456  * @return True if the given text is valid.
457  */
isTextValid(const QString & text)458 bool FloatingTextWidget::isTextValid(const QString &text)
459 {
460     int length = text.length();
461     if(length < 1)
462         return false;
463     for(int i=0;i<length;i++) {
464         if(!text.at(i).isSpace()) {
465             return true;
466         }
467     }
468     return false;
469 }
470 
471 /**
472  * Returns a constrained position for the widget after applying the position
473  * difference.
474  * If no link widget exists, the position returned is the current widget
475  * position with the difference applied. If there's a link, the position
476  * to be returned is constrained using constrainTextPos method from the
477  * LinkWidget, if any.
478  *
479  * @param diffX The difference between current X position and new X position.
480  * @param diffY The difference between current Y position and new Y position.
481  * @return A QPointF with the constrained new position.
482  */
constrainPosition(qreal diffX,qreal diffY)483 QPointF FloatingTextWidget::constrainPosition(qreal diffX, qreal diffY)
484 {
485     qreal newX = x() + diffX;
486     qreal newY = y() + diffY;
487 
488     if (link()) {
489         link()->constrainTextPos(newX, newY, width(), height(), textRole());
490     }
491 
492     return QPointF(newX, newY);
493 }
494 
495 /**
496  * Overridden from UMLWidget.
497  * Moves the widget to a new position using the difference between the
498  * current position and the new position.
499  * If the floating text widget is part of a sequence message, and the
500  * message widget is selected, it does nothing: the message widget will
501  * update the text position when it's moved.
502  * In any other case, the floating text widget constrains its move using
503  * constrainPosition. When the position of the floating text is constrained,
504  * it's kept at that position until it can be moved to another valid
505  * position (m_unconstrainedPositionX/Y and m_movementDirectionX/Y are
506  * used for that).
507  * Moreover, if is part of a sequence message (and the message widget
508  * isn't selected), it updates the position of the message widget.
509  * @see constrainPosition
510  *
511  * @param diffX The difference between current X position and new X position.
512  * @param diffY The difference between current Y position and new Y position.
513  */
moveWidgetBy(qreal diffX,qreal diffY)514 void FloatingTextWidget::moveWidgetBy(qreal diffX, qreal diffY)
515 {
516     if (textRole() == Uml::TextRole::Seq_Message_Self)
517         return;
518 
519     if (textRole() == Uml::TextRole::Seq_Message
520             && link() && dynamic_cast<MessageWidget*>(link())
521             && dynamic_cast<MessageWidget*>(link())->isSelected()) {
522         return;
523     }
524 
525     m_unconstrainedPositionX += diffX;
526     m_unconstrainedPositionY += diffY;
527     QPointF constrainedPosition = constrainPosition(diffX, diffY);
528 
529     qreal newX = constrainedPosition.x();
530     qreal newY = constrainedPosition.y();
531 
532     if (!m_movementDirectionX) {
533         if (m_unconstrainedPositionX != constrainedPosition.x()) {
534             m_movementDirectionX = (diffX > 0)? 1: -1;
535         }
536     } else if ((m_movementDirectionX < 0 && m_unconstrainedPositionX > x()) ||
537                (m_movementDirectionX > 0 && m_unconstrainedPositionX < x()) ) {
538         newX = m_unconstrainedPositionX;
539         m_movementDirectionX = 0;
540     }
541 
542     if (!m_movementDirectionY) {
543         if (m_unconstrainedPositionY != constrainedPosition.y()) {
544             m_movementDirectionY = (diffY > 0)? 1: -1;
545         }
546     } else if ((m_movementDirectionY < 0 && m_unconstrainedPositionY > y()) ||
547                (m_movementDirectionY > 0 && m_unconstrainedPositionY < y()) ) {
548         newY = m_unconstrainedPositionY;
549         m_movementDirectionY = 0;
550     }
551 
552     setX(newX);
553     setY(newY);
554 
555     if (link()) {
556         link()->calculateNameTextSegment();
557         if (textRole() == Uml::TextRole::Seq_Message) {
558             MessageWidget* messageWidget = (MessageWidget*)link();
559             messageWidget->setY(newY + height());
560         }
561     }
562 }
563 
564 /**
565  * Overridden from UMLWidget.
566  * Modifies the value of the diffX and diffY variables used to move the
567  * widgets.
568  * The values are constrained using constrainPosition.
569  * @see constrainPosition
570  *
571  * @param diffX The difference between current X position and new X position.
572  * @param diffY The difference between current Y position and new Y position.
573  */
constrainMovementForAllWidgets(qreal & diffX,qreal & diffY)574 void FloatingTextWidget::constrainMovementForAllWidgets(qreal &diffX, qreal &diffY)
575 {
576     QPointF constrainedPosition = constrainPosition(diffX, diffY);
577 
578     diffX = constrainedPosition.x() - x();
579     diffY = constrainedPosition.y() - y();
580 }
581 
582 /**
583  * Override method from UMLWidget in order to additionally check widget parentage.
584  *
585  * @param p Point to be checked.
586  *
587  * @return 'this' if UMLWidget::onWidget(p) returns non 0;
588  *         else NULL.
589  */
onWidget(const QPointF & p)590 UMLWidget* FloatingTextWidget::onWidget(const QPointF &p)
591 {
592     WidgetBase *pw = dynamic_cast<WidgetBase*>(parentItem());
593     if (pw == 0) {
594         return WidgetBase::onWidget(p);
595     }
596     const WidgetBase::WidgetType t = pw->baseType();
597     bool isWidgetChild = false;
598     if (t == WidgetBase::wt_Interface) {
599         ClassifierWidget *cw = static_cast<ClassifierWidget*>(pw);
600         isWidgetChild = cw->getDrawAsCircle();
601     } else if (t == wt_Association ||
602                t == WidgetBase::wt_Port || t == WidgetBase::wt_Pin) {
603         isWidgetChild = true;
604     }
605     if (!isWidgetChild) {
606         return WidgetBase::onWidget(p);
607     }
608     const qreal w = width();
609     const qreal h = height();
610     const qreal left = pw->x() + x();
611     const qreal right = left + w;
612     const qreal top = pw->y() + y();
613     const qreal bottom = top + h;
614     // uDebug() << "child_of_pw; p=(" << p.x() << "," << p.y() << "), "
615     //          << "x=" << left << ", y=" << top << ", w=" << w << ", h=" << h
616     //          << "; right=" << right << ", bottom=" << bottom;
617     if (p.x() >= left && p.x() <= right &&
618         p.y() >= top && p.y() <= bottom) { // Qt coord.sys. origin in top left corner
619         // uDebug() << "floatingtext: " << m_Text;
620         return this;
621     }
622     return 0;
623 }
624 
625 /**
626  * Overrides default method
627  */
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)628 void FloatingTextWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
629 {
630     Q_UNUSED(option);
631     Q_UNUSED(widget);
632 
633     int w = width();
634     int h = height();
635     painter->setFont(UMLWidget::font());
636     painter->setPen(textColor());
637     painter->drawText(0, 0, w, h, Qt::AlignCenter, displayText());
638 
639     UMLWidget::paint(painter, option, widget);
640 }
641 
642 /**
643  * Loads the "floatingtext" XMI element.
644  */
loadFromXMI1(QDomElement & qElement)645 bool FloatingTextWidget::loadFromXMI1(QDomElement & qElement)
646 {
647     if(!UMLWidget::loadFromXMI1(qElement))
648         return false;
649 
650     m_unconstrainedPositionX = x();
651     m_unconstrainedPositionY = y();
652     QString role = qElement.attribute(QLatin1String("role"));
653     if(!role.isEmpty())
654         m_textRole = Uml::TextRole::fromInt(role.toInt());
655 
656     m_preText = qElement.attribute(QLatin1String("pretext"));
657     m_postText = qElement.attribute(QLatin1String("posttext"));
658     setText(qElement.attribute(QLatin1String("text")));  // use setText for geometry update
659     // If all texts are empty then this is a useless widget.
660     // In that case we return false.
661     // CAVEAT: The caller should not interpret the false return value
662     //  as an indication of failure since previous umbrello versions
663     //  saved lots of these empty FloatingTexts.
664     bool usefulWidget = !isEmpty();
665     return usefulWidget;
666 }
667 
668 /**
669  * Reimplemented from UMLWidget::saveToXMI1 to save the widget
670  * data into XMI 'floatingtext' element.
671  */
saveToXMI1(QXmlStreamWriter & writer)672 void FloatingTextWidget::saveToXMI1(QXmlStreamWriter& writer)
673 {
674     if (isEmpty())
675         return;
676 
677     writer.writeStartElement(QLatin1String("floatingtext"));
678     UMLWidget::saveToXMI1(writer);
679     writer.writeAttribute(QLatin1String("text"), m_Text);
680     writer.writeAttribute(QLatin1String("pretext"), m_preText);
681     writer.writeAttribute(QLatin1String("posttext"), m_postText);
682 
683     /* No need to save these - the messagewidget already did it.
684     m_Operation  = qElement.attribute("operation");
685     m_SeqNum = qElement.attribute("seqnum");
686      */
687 
688     writer.writeAttribute(QLatin1String("role"), QString::number(m_textRole));
689     writer.writeEndElement();
690 }
691 
692 /**
693  * Called when a menu selection has been made.
694  *
695  * @param action  The action that has been selected.
696  */
slotMenuSelection(QAction * action)697 void FloatingTextWidget::slotMenuSelection(QAction* action)
698 {
699     ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(action);
700     switch(sel) {
701     case ListPopupMenu::mt_Properties:
702         showPropertiesDialog();
703         break;
704 
705     case ListPopupMenu::mt_Delete:
706         hide();   // This is only a workaround; if set then m_linkWidget should
707                   // really delete this FloatingTextWidget.
708         update();
709         m_scene->removeWidget(this);
710         break;
711 
712     case ListPopupMenu::mt_New_Operation: // needed by AssociationWidget
713     case ListPopupMenu::mt_Operation:
714         {
715             if (m_linkWidget == 0) {
716                 DEBUG(DBG_SRC) << "mt_Operation: m_linkWidget is NULL";
717                 return;
718             }
719             UMLClassifier* c = m_linkWidget->operationOwner();
720             if (c == 0) {
721                 QString opText = text();
722                 bool ok = Dialog_Utils::askName(i18nc("operation name", "Name"),
723                                                 i18n("Enter operation name:"),
724                                                 opText);
725                 if (ok)
726                     m_linkWidget->setCustomOpText(opText);
727                 return;
728             }
729             UMLClassifierListItem* umlObj = Object_Factory::createChildObject(c, UMLObject::ot_Operation);
730             if (umlObj) {
731                 UMLOperation* newOperation = umlObj->asUMLOperation();
732                 m_linkWidget->setOperation(newOperation);
733             }
734         }
735         break;
736 
737     case ListPopupMenu::mt_Select_Operation:
738         showOperationDialog(false);
739         break;
740 
741     case ListPopupMenu::mt_Rename:
742         handleRename();
743         break;
744 
745     case ListPopupMenu::mt_Change_Font:
746         {
747 #if QT_VERSION >= 0x050000
748             bool ok = false;
749             QFont fnt = QFontDialog::getFont(&ok, font(), m_scene->activeView());
750             if (ok) {
751 #else
752             QFont fnt = font();
753             if(KFontDialog::getFont(fnt, KFontChooser::NoDisplayFlags, m_scene->activeView())) {
754 #endif
755                 if(m_textRole == Uml::TextRole::Floating || m_textRole == Uml::TextRole::Seq_Message) {
756                     setFont(fnt);
757                 } else if (m_linkWidget) {
758                     m_linkWidget->lwSetFont(fnt);
759                 }
760             }
761         }
762         break;
763 
764     case ListPopupMenu::mt_Reset_Label_Positions:
765         if (m_linkWidget)
766             m_linkWidget->resetTextPositions();
767         break;
768 
769     default:
770         UMLWidget::slotMenuSelection(action);
771         break;
772     }//end switch
773 }
774 
775 /**
776  * Sets the text for this label if it is acting as a sequence diagram
777  * message or a collaboration diagram message.
778  */
779 void FloatingTextWidget::setMessageText()
780 {
781     if (m_linkWidget) {
782         m_linkWidget->setMessageText(this);
783         QSizeF s = minimumSize();
784         setSize(s.width(), s.height());
785 
786     }
787     setVisible(!text().isEmpty());
788 }
789 
790