Bug 127628

Summary: multiplicity labels often are placed incorrectly
Product: [Applications] umbrello Reporter: Mark Davies <mark>
Component: generalAssignee: Oliver Kellogg <okellogg>
Status: RESOLVED FIXED    
Severity: normal    
Priority: NOR    
Version: unspecified   
Target Milestone: ---   
Platform: NetBSD pkgsrc   
OS: NetBSD   
Latest Commit: Version Fixed In:
Sentry Crash Report:
Attachments: Umbrello Screenshot.

Description Mark Davies 2006-05-19 00:15:21 UTC
Version:            (using KDE KDE 3.5.2)
Installed from:    NetBSD pkgsrc
Compiler:          gcc 3.3.3 
OS:                NetBSD

This may be related to bug 120598 but its occurring in 3.5.2 where that bug is supposed to be fixed.

Multiplicity label placement messes up when there are multiple aggregation relationships.

Repeat by:
- Create a new class Diagram
- Create 2 classes
- Create an aggregation relationship between them
- Right click the line, select properties -> Roles
- Change the multiplicity labels of both role a and role b
- Create a second aggregation relationship between the two classes
- Add multiplicity labels for it as well.
- Drag the classes around a bit

Note that the labels (for one of the aggregations at least) move erratically around the screen sometimes in the opposite direction to the class object.
Comment 1 Roel Adriaans 2007-02-04 19:27:22 UTC
Created attachment 19552 [details]
Umbrello Screenshot.
Comment 2 Roel Adriaans 2007-02-04 19:27:38 UTC
Problem still exist in version 1.5.6. I've atached a screenshot that shows the this.

Comment 3 Oliver Kellogg 2007-02-25 20:21:08 UTC
SVN commit 637211 by okellogg:

saveIdealTextPositions(): New method for saving the original ideally computed
 text widget positions.  This is necessary because a single invocation of
 calculateEndingPoints() renews the LinePath ending points on ALL
 AssociationWidgets, thus preventing consideration of the original ideal text
 positions for all but the first AssociationWidget.
 Adjustment of associations subsequent to a widget move now has two steps:
 1) Loop over all attached AssociationWidgets and call saveIdealTextPositions()
    for each AssociationWidget.
 2) Loop over all attached AssociationWidgets and call widgetMoved() for each
    AssociationWidget.

floatingText(): Redundant, use getTextWidgetByRole() instead (cosmetic change.)

BUG:127628


 M  +22 -49    associationwidget.cpp  
 M  +47 -6     associationwidget.h  
 M  +13 -2     umlwidget.cpp  


--- branches/KDE/3.5/kdesdk/umbrello/umbrello/associationwidget.cpp #637210:637211
@@ -285,6 +285,7 @@
         case tr_MultiB:
             return m_role[B].m_pMulti;
         case tr_Name:
+        case tr_Coll_Message:
             return m_pName;
         case tr_RoleAName:
             return m_role[A].m_pRole;
@@ -1286,6 +1287,16 @@
     m_pObject->blockSignals(false);
 }
 
+void AssociationWidget::saveIdealTextPositions() {
+    m_oldNamePoint = calculateTextPosition(tr_Name);
+    m_oldMultiAPoint = calculateTextPosition(tr_MultiA);
+    m_oldMultiBPoint = calculateTextPosition(tr_MultiB);
+    m_oldChangeAPoint = calculateTextPosition(tr_ChangeA);
+    m_oldChangeBPoint = calculateTextPosition(tr_ChangeB);
+    m_oldRoleAPoint = calculateTextPosition(tr_RoleAName);
+    m_oldRoleBPoint = calculateTextPosition(tr_RoleBName);
+}
+
 /** Adjusts the ending point of the association that connects to Widget */
 void AssociationWidget::widgetMoved(UMLWidget* widget, int x, int y ) {
     // 2004-04-30: Achim Spangler
@@ -1302,13 +1313,6 @@
         << endl;
         return;
     }
-    QPoint oldNamePoint = calculateTextPosition(tr_Name);
-    QPoint oldMultiAPoint = calculateTextPosition(tr_MultiA);
-    QPoint oldMultiBPoint = calculateTextPosition(tr_MultiB);
-    QPoint oldChangeAPoint = calculateTextPosition(tr_ChangeA);
-    QPoint oldChangeBPoint = calculateTextPosition(tr_ChangeB);
-    QPoint oldRoleAPoint = calculateTextPosition(tr_RoleAName);
-    QPoint oldRoleBPoint = calculateTextPosition(tr_RoleBName);
 
     int dx = m_role[A].m_OldCorner.x() - x;
     int dy = m_role[A].m_OldCorner.y() - y;
@@ -1336,40 +1340,40 @@
         }
 
         if ( m_pName && !m_pName->getSelected() ) {
-            setTextPositionRelatively(tr_Name, oldNamePoint);
+            setTextPositionRelatively(tr_Name, m_oldNamePoint);
         }
 
     }//end if widgetA = widgetB
     else if (m_role[A].m_pWidget==widget) {
         if (m_pName && m_unNameLineSegment == 0 && !m_pName->getSelected() ) {
             //only calculate position and move text if the segment it is on is moving
-            setTextPositionRelatively(tr_Name, oldNamePoint);
+            setTextPositionRelatively(tr_Name, m_oldNamePoint);
         }
     }//end if widgetA moved
     else if (m_role[B].m_pWidget==widget) {
         if (m_pName && (m_unNameLineSegment == pos-1) && !m_pName->getSelected() ) {
             //only calculate position and move text if the segment it is on is moving
-            setTextPositionRelatively(tr_Name, oldNamePoint);
+            setTextPositionRelatively(tr_Name, m_oldNamePoint);
         }
     }//end if widgetB moved
 
     if ( m_role[A].m_pRole && !m_role[A].m_pRole->getSelected() ) {
-        setTextPositionRelatively(tr_RoleAName, oldRoleAPoint);
+        setTextPositionRelatively(tr_RoleAName, m_oldRoleAPoint);
     }
     if ( m_role[B].m_pRole && !m_role[B].m_pRole->getSelected() ) {
-        setTextPositionRelatively(tr_RoleBName, oldRoleBPoint);
+        setTextPositionRelatively(tr_RoleBName, m_oldRoleBPoint);
     }
     if ( m_role[A].m_pMulti && !m_role[A].m_pMulti->getSelected() ) {
-        setTextPositionRelatively(tr_MultiA, oldMultiAPoint);
+        setTextPositionRelatively(tr_MultiA, m_oldMultiAPoint);
     }
     if ( m_role[B].m_pMulti && !m_role[B].m_pMulti->getSelected() ) {
-        setTextPositionRelatively(tr_MultiB, oldMultiBPoint);
+        setTextPositionRelatively(tr_MultiB, m_oldMultiBPoint);
     }
     if ( m_role[A].m_pChangeWidget && !m_role[A].m_pChangeWidget->getSelected() ) {
-        setTextPositionRelatively(tr_ChangeA, oldChangeAPoint);
+        setTextPositionRelatively(tr_ChangeA, m_oldChangeAPoint);
     }
     if ( m_role[B].m_pChangeWidget && !m_role[B].m_pChangeWidget->getSelected() ) {
-        setTextPositionRelatively(tr_ChangeB, oldChangeBPoint);
+        setTextPositionRelatively(tr_ChangeB, m_oldChangeBPoint);
     }
 }//end method widgetMoved
 
@@ -2014,37 +2018,6 @@
     }
 }
 
-FloatingTextWidget* AssociationWidget::floatingText(Text_Role role) {
-    FloatingTextWidget *ft = NULL;
-    switch(role) {
-    case tr_MultiA:
-        ft = m_role[A].m_pMulti;
-        break;
-    case tr_MultiB:
-        ft = m_role[B].m_pMulti;
-        break;
-    case tr_Name:
-    case tr_Coll_Message:
-        ft = m_pName;
-        break;
-    case tr_RoleAName:
-        ft = m_role[A].m_pRole;
-        break;
-    case tr_RoleBName:
-        ft = m_role[B].m_pRole;
-        break;
-    case tr_ChangeA:
-        ft = m_role[A].m_pChangeWidget;
-        break;
-    case tr_ChangeB:
-        ft = m_role[B].m_pChangeWidget;
-        break;
-    default:
-        break;
-    }
-    return ft;
-}
-
 void AssociationWidget::setTextPosition(Text_Role role) {
     bool startMove = false;
     if( m_role[A].m_pMulti && m_role[A].m_pMulti->getStartMove() )
@@ -2065,7 +2038,7 @@
     if (startMove) {
         return;
     }
-    FloatingTextWidget *ft = floatingText(role);
+    FloatingTextWidget *ft = getTextWidgetByRole(role);
     if (ft == NULL)
         return;
     QPoint pos = calculateTextPosition(role);
@@ -2103,7 +2076,7 @@
     if (startMove) {
         return;
     }
-    FloatingTextWidget *ft = floatingText(role);
+    FloatingTextWidget *ft = getTextWidgetByRole(role);
     if (ft == NULL)
         return;
     int ftX = ft->getX();
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/associationwidget.h #637210:637211
@@ -316,6 +316,17 @@
     void widgetMoved(UMLWidget* widget, int x, int y);
 
     /**
+     * Auxiliary method for widgetMoved():
+     * Saves all ideally computed floatingtext positions before doing any
+     * kind of change.  This is necessary because a single invocation of
+     * calculateEndingPoints() modifies the LinePath ending points on ALL
+     * AssociationWidgets.  This means that if we don't save the old ideal
+     * positions then they are irretrievably lost as soon as
+     * calculateEndingPoints() is invoked.
+     */
+    void saveIdealTextPositions();
+
+    /**
      * Calculates the m_unNameLineSegment value according to the new
      * NameText topleft corner PT.
      * It iterates through all LinePath's segments and for each one
@@ -749,12 +760,6 @@
     QPoint calculateTextPosition(Uml::Text_Role role);
 
     /**
-     * Returns the FloatingTextWidget identified by the given text role.
-     * Returns NULL if there is no FloatingTextWidget active for the text role.
-     */
-    FloatingTextWidget* floatingText(Uml::Text_Role role);
-
-    /**
      * Puts the text widget with the given role at the given position.
      * This method calls @ref calculateTextPostion to get the needed position.
      * I.e. the line segment it is on has moved and it should move the same
@@ -930,6 +935,42 @@
     ListPopupMenu       *m_pMenu;
     bool                m_bSelected;
     int                 m_nMovingPoint;
+
+    /**
+     * Position of Name floatingtext saved by saveIdealTextPositions()
+     */
+    QPoint m_oldNamePoint;
+    /**
+     * Position of role A multiplicity floatingtext saved by
+     * saveIdealTextPositions()
+     */
+    QPoint m_oldMultiAPoint;
+    /**
+     * Position of role B multiplicity floatingtext saved by
+     * saveIdealTextPositions()
+     */
+    QPoint m_oldMultiBPoint;
+    /**
+     * Position of role A changeability floatingtext saved by
+     * saveIdealTextPositions()
+     */
+    QPoint m_oldChangeAPoint;
+    /**
+     * Position of role B changeability floatingtext saved by
+     * saveIdealTextPositions()
+     */
+    QPoint m_oldChangeBPoint;
+    /**
+     * Position of role A name floatingtext saved by
+     * saveIdealTextPositions()
+     */
+    QPoint m_oldRoleAPoint;
+    /**
+     * Position of role B name floatingtext saved by
+     * saveIdealTextPositions()
+     */
+    QPoint m_oldRoleBPoint;
+
     int         m_nLinePathSegmentIndex; ///< anchor for m_pAssocClassLine
     QCanvasLine *m_pAssocClassLine;  ///< used for connecting assoc. class
     /// selection adornment for the endpoints of the assoc. class connecting line
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/umlwidget.cpp #637210:637211
@@ -571,8 +571,13 @@
     }
     AssociationWidgetListIt assoc_it(m_Assocs);
     AssociationWidget* assocwidget = 0;
-    while((assocwidget=assoc_it.current())) {
+    while ((assocwidget = assoc_it.current())) {
         ++assoc_it;
+        assocwidget->saveIdealTextPositions();
+    }
+    assoc_it.toFirst();
+    while ((assocwidget = assoc_it.current())) {
+        ++assoc_it;
         assocwidget->widgetMoved(this, x, y);
     }
 }
@@ -581,9 +586,15 @@
 {
     AssociationWidgetListIt assoc_it(m_Assocs);
     AssociationWidget* assocwidget = 0;
-    while((assocwidget=assoc_it.current())) {
+    while ((assocwidget = assoc_it.current())) {
         ++assoc_it;
         if(!assocwidget->getSelected())
+            assocwidget->saveIdealTextPositions();
+    }
+    assoc_it.toFirst();
+    while ((assocwidget = assoc_it.current())) {
+        ++assoc_it;
+        if(!assocwidget->getSelected())
             assocwidget->widgetMoved(this, x, y);
     }
 }