Logo Search packages:      
Sourcecode: kdesdk version File versions

associationwidget.cpp

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include <cstdlib>
#include <cmath>

#include "activitywidget.h"
#include "uml.h"
#include "umlview.h"
#include "umldoc.h"
#include "umlwidget.h"
#include "messagewidget.h"
#include "umlrole.h"
#include "listpopupmenu.h"
#include "association.h"
#include "associationwidget.h"
#include "assocrules.h"
#include "floatingtext.h"
#include "objectwidget.h"
#include "dialogs/assocpropdlg.h"
#include "dialogs/selectopdlg.h"
#include "inputdialog.h"

#include <kdebug.h>
#include <klocale.h>

// this constructor really only for loading from XMI, otherwise it
// is bad..and shouldnt be allowed as it creates an incomplete
// associationwidget.
00035 AssociationWidget::AssociationWidget(UMLView *view)
      : LinkWidget(view)
{
      init(view);
}

// the preferred constructor
00042 AssociationWidget::AssociationWidget(UMLView *view, UMLWidget* pWidgetA,
                             Association_Type assocType, UMLWidget* pWidgetB )
      : LinkWidget(view)
{
      init(view);
      UMLDoc *umldoc = UMLApp::app()->getDocument();

      // set up UMLAssociation obj if assoc is represented and both roles are UML objects
      if (UMLAssociation::assocTypeHasUMLRepresentation(assocType)) {
            UMLObject* umlRoleA = pWidgetA->getUMLObject();
            UMLObject* umlRoleB = pWidgetB->getUMLObject();
            if (umlRoleA != NULL && umlRoleB != NULL) {
                  bool swap;

                  // THis isnt correct. We could very easily have more than one
                  // of the same type of association between the same two objects.
                  // Just create the association. This search should have been
                  // done BEFORE creation of the widget, if it mattered to the code.
                  // But lets leave check in here for the time being so that debugging
                  // output is shown, in case there is a collision with code elsewhere.
                  UMLAssociation * testAssoc = umldoc->findAssociation( assocType, umlRoleA, umlRoleB, &swap );
                  if (testAssoc != NULL)
                        kdDebug() << " constructing a similar or exact same assoc " << 
                              "as an already existing assoc (swap=" << swap << ")" << endl;

                  // now, just create a new association anyways
                  UMLAssociation * myAssoc = new UMLAssociation( assocType, umlRoleA, umlRoleB );
                  setUMLAssociation(myAssoc);
            }
      }

      setWidget(pWidgetA, A);
      setWidget(pWidgetB, B);

      setAssocType(assocType);

      calculateEndingPoints();

      //The AssociationWidget is set to Activated because it already has its side widgets
      setActivated(true);

      // sync UML meta-data to settings here
      mergeAssociationDataIntoUMLRepresentation();

      //collaboration messages need a name label because it's that
      //which handles the right click menu options
      if (getAssocType() == at_Coll_Message) {
            // Create a temporary name to bring on setName()
            ObjectWidget *ow = static_cast<ObjectWidget*>(m_role[B].m_pWidget);
            QString localIdStr;
            localIdStr.setNum(ow->getLocalID());
            setName("m" + localIdStr);
            if (m_pObject)
                  m_pName->setUMLObject( m_pObject );
            else
                  m_pName->setUMLObject( ow->getUMLObject() );
      }
}

00101 AssociationWidget::~AssociationWidget() { 
      cleanup();
}

00105 AssociationWidget& AssociationWidget::operator=(AssociationWidget & Other) {
      m_LinePath = Other.m_LinePath;

      m_pView = Other.m_pView;

      if (Other.m_pName) {
            m_pName = new FloatingText(m_pView);
            *m_pName = *(Other.m_pName);
      } else {
            m_pName = NULL;
      }

      for (unsigned r = (unsigned)A; r <= (unsigned)B; r++) {
            WidgetRole& lhs = m_role[r];
            const WidgetRole& rhs = Other.m_role[r];
            lhs.m_nIndex = rhs.m_nIndex;
            lhs.m_nTotalCount = rhs.m_nTotalCount;

            if (rhs.m_pMulti) {
                  lhs.m_pMulti = new FloatingText(m_pView);
                  *(lhs.m_pMulti) = *(rhs.m_pMulti);
            } else {
                  lhs.m_pMulti = NULL;
            }

            if (rhs.m_pRole) {
                  lhs.m_pRole = new FloatingText(m_pView);
                  *(lhs.m_pRole) = *(rhs.m_pRole);
            } else {
                  lhs.m_pRole = NULL;
            }

            if (rhs.m_pChangeWidget) {
                  lhs.m_pChangeWidget = new FloatingText(m_pView);
                  *(lhs.m_pChangeWidget) = *(rhs.m_pChangeWidget);
            } else {
                  lhs.m_pChangeWidget = NULL;
            }

            lhs.m_pWidget = rhs.m_pWidget;
            lhs.m_OldCorner = rhs.m_OldCorner;
            lhs.m_WidgetRegion = rhs.m_WidgetRegion;
      }

      m_bActivated = Other.m_bActivated;
      m_unNameLineSegment = Other.m_unNameLineSegment;
      m_bFocus = Other.m_bFocus;
      m_pMenu = Other.m_pMenu;
      setUMLAssociation(Other.getAssociation());
      m_bSelected = Other.m_bSelected;
      m_nMovingPoint = Other.m_nMovingPoint;

      return *this;
}

00160 bool AssociationWidget::operator==(AssociationWidget & Other) {
      if( this == &Other )
            return true;

      if( !m_pObject || !Other.m_pObject ) {
            if( !Other.m_pObject && m_pObject )
                  return false;
            if( Other.m_pObject && !m_pObject )
                  return false;
      } else if( m_pObject != Other.m_pObject )
            return false;

      if (getAssocType() != Other.getAssocType())
            return false;

      if (getWidgetID(A) != Other.getWidgetID(A))
            return false;

      if (getWidgetID(B) != Other.getWidgetID(B))
            return false;

      if (getWidget(A)->getBaseType() == Uml::wt_Object &&
          Other.getWidget(A)->getBaseType() == Uml::wt_Object) {
            ObjectWidget *ownA = static_cast<ObjectWidget*>(getWidget(A));
            ObjectWidget *otherA = static_cast<ObjectWidget*>(Other.getWidget(A));
            if (ownA->getLocalID() != otherA->getLocalID())
                  return false;
      }

      if (getWidget(B)->getBaseType() == Uml::wt_Object &&
          Other.getWidget(B)->getBaseType() == Uml::wt_Object) {
            ObjectWidget *ownB = static_cast<ObjectWidget*>(getWidget(B));
            ObjectWidget *otherB = static_cast<ObjectWidget*>(Other.getWidget(B));
            if (ownB->getLocalID() != otherB->getLocalID())
                  return false;
      }

      return true;
}

00200 bool AssociationWidget::operator!=(AssociationWidget & Other) {
      return !(*this == Other);
}

00204 UMLAssociation * AssociationWidget::getAssociation () {
      return static_cast<UMLAssociation*>(m_pObject);
}

00208 FloatingText* AssociationWidget::getMultiWidget(Role_Type role) {
      return m_role[role].m_pMulti;
}

00212 QString AssociationWidget::getMulti(Role_Type role) const
{
      if (m_role[role].m_pMulti == NULL)
            return "";
      return m_role[role].m_pMulti->getText();
}

00219 FloatingText* AssociationWidget::getNameWidget()
{
      return m_pName;
}

00224 QString AssociationWidget::getName() const {
      if (m_pName == NULL)
            return "";
      return m_pName->getText();
}

00230 QString AssociationWidget::getDoc() const {
      if (m_pObject)
            return m_pObject->getDoc();
      return m_Doc;
}

00236 FloatingText* AssociationWidget::getRoleWidget(Role_Type role) {
      return m_role[role].m_pRole;
}

00240 FloatingText* AssociationWidget::getChangeWidget(Role_Type role) {
      return m_role[role].m_pChangeWidget;
}

00244 QString AssociationWidget::getRoleName(Role_Type role) const {
      if (m_role[role].m_pRole == NULL)
            return "";
      return m_role[role].m_pRole->getText();
}

00250 QString AssociationWidget::getRoleDoc(Role_Type role) const {
      if (m_pObject == NULL)
            return "";
      UMLAssociation *umla = static_cast<UMLAssociation*>(m_pObject);
      return umla->getRoleDoc(role);
}

00257 void AssociationWidget::setName(QString strName) {
      bool newLabel = false;
      if(!m_pName) {
            // Don't construct the FloatingText if the string is empty.
            if (strName.isEmpty())
                  return;

            newLabel = true;
            m_pName = new FloatingText(m_pView, CalculateNameType(tr_Name), strName);
            m_pName->setLink(this);
            m_pName->setUMLObject(m_role[B].m_pWidget->getUMLObject());
      } else {
            m_pName->setText(strName);
      }

      // set attribute of UMLAssociation associated with this associationwidget
      if (m_pObject)
            m_pObject->setName(strName);

      setTextPosition( tr_Name, calculateTextPosition(tr_Name) );
      if (newLabel) {
            m_pName->setActivated();
            m_pView->addWidget(m_pName);
      }

      if(FloatingText::isTextValid(m_pName->getText()))
            m_pName -> show();
      else
            m_pName -> hide();

}

00289 void AssociationWidget::setMulti(QString strMulti, Role_Type role) {
      bool newLabel = false;
      Text_Role tr = (role == A ? tr_MultiA : tr_MultiB);

      if(!m_role[role].m_pMulti) {
            // Don't construct the FloatingText if the string is empty.
            if (strMulti.isEmpty())
                  return;

            newLabel = true;
            m_role[role].m_pMulti = new FloatingText(m_pView, tr, strMulti);
            m_role[role].m_pMulti->setLink(this);
            m_pView->addWidget(m_role[role].m_pMulti);
      } else {
            if (m_role[role].m_pMulti->getText() == "") {
                  newLabel = true;
            }
            m_role[role].m_pMulti->setText(strMulti);
      }

      m_role[role].m_pMulti->setActivated();

      if (newLabel) {
            setTextPosition( tr, calculateTextPosition(tr) );
      }

      if(FloatingText::isTextValid(m_role[role].m_pMulti->getText()))
            m_role[role].m_pMulti -> show();
      else
            m_role[role].m_pMulti -> hide();

      if (m_pObject)
            getAssociation()->setMulti(strMulti, role);
}

00324 bool AssociationWidget::setRoleName (QString strRole, Role_Type role) {
      bool newLabel = false;
      Association_Type type = getAssocType();
      //if the association is not supposed to have a Role FloatingText
      if( !AssocRules::allowRole( type ) )  {
            return false;
      }

      Text_Role tr = (role == A ? tr_RoleAName : tr_RoleBName);
      if(!m_role[role].m_pRole) {
            // Don't construct the FloatingText if the string is empty.
            if (strRole.isEmpty())
                  return true;

            newLabel = true;
            m_role[role].m_pRole = new FloatingText(m_pView, tr, strRole);
            m_role[role].m_pRole->setLink(this);
            m_pView->addWidget(m_role[role].m_pRole);
            Scope scope = getVisibility(role);
            m_role[role].m_pRole->setPreText(UMLAssociation::ScopeToString(scope));
      } else {
            if (m_role[role].m_pRole->getText() == "") {
                  newLabel = true;
            }
            m_role[role].m_pRole->setText(strRole);
      }

      // set attribute of UMLAssociation associated with this associationwidget
      if (m_pObject)
            getAssociation()->setRoleName(strRole, role);
      m_role[role].m_RoleName = strRole;

      m_role[role].m_pRole->setActivated();

      if (newLabel) {
            setTextPosition( tr, calculateTextPosition(tr) );
      }

      if(FloatingText::isTextValid(m_role[role].m_pRole->getText()))
            m_role[role].m_pRole -> show();
      else
            m_role[role].m_pRole -> hide();
      return true;
}

00369 void AssociationWidget::setDoc (QString doc) {
      if (m_pObject)
            m_pObject->setDoc(doc);
      else
            m_Doc = doc;
}

00376 void AssociationWidget::setRoleDoc (QString doc, Role_Type role) {
      if (m_pObject)
            getAssociation()->setRoleDoc(doc, role);
      else
            m_role[role].m_RoleDoc = doc;
}

00383 void AssociationWidget::setMessageText(FloatingText *ft) {
      ft->setText( getName() );
}

00387 Scope AssociationWidget::getVisibility(Role_Type role) const {
      if (m_pObject == NULL)
            return m_role[role].m_Visibility;
      UMLAssociation *umla = static_cast<UMLAssociation*>(m_pObject);
      return umla->getVisibility(role);
}

00394 void AssociationWidget::setVisibility (Scope value, Role_Type role)
{
      if (value == getVisibility(role))
            return;
      if (m_pObject)  // update our model object
            getAssociation()->setVisibility(value, role);
      m_role[role].m_Visibility = value;
      // update role pre-text attribute as appropriate
      if (m_role[role].m_pRole) {
            QString scopeString = UMLAssociation::ScopeToString(value);
            m_role[role].m_pRole->setPreText(scopeString);
      }
}

00408 Changeability_Type AssociationWidget::getChangeability(Role_Type role) const
{
      if (m_pObject == NULL)
            return m_role[role].m_Changeability;
      UMLAssociation *umla = static_cast<UMLAssociation*>(m_pObject);
      return umla->getChangeability(role);
}

00416 void AssociationWidget::setChangeability (Changeability_Type value, Role_Type role)
{
      if (value == getChangeability(role))
            return;
      QString changeString = UMLAssociation::ChangeabilityToString(value);
      if (m_pObject)  // update our model object
            getAssociation()->setChangeability(value, role);
      m_role[role].m_Changeability = value;
      // update our string representation
      setChangeWidget(changeString, role);
}

00428 void AssociationWidget::setChangeWidget(QString strChangeWidget, Role_Type role) {
      bool newLabel = false;
      Text_Role tr = (role == A ? tr_ChangeA : tr_ChangeB);

      if(!m_role[role].m_pChangeWidget) {
            // Don't construct the FloatingText if the string is empty.
            if (strChangeWidget.isEmpty())
                  return;

            newLabel = true;
            m_role[role].m_pChangeWidget = new FloatingText(m_pView, tr, strChangeWidget);
            m_role[role].m_pChangeWidget->setLink(this);
            m_pView->addWidget(m_role[role].m_pChangeWidget);
            m_role[role].m_pChangeWidget->setPreText("{"); // all types have this
            m_role[role].m_pChangeWidget->setPostText("}"); // all types have this
      } else {
            if (m_role[role].m_pChangeWidget->getText() == "") {
                  newLabel = true;
            }
            m_role[role].m_pChangeWidget->setText(strChangeWidget);
      }
      m_role[role].m_pChangeWidget->setActivated();

      if (newLabel) {
            setTextPosition( tr, calculateTextPosition(tr) );
      }

      if(FloatingText::isTextValid(m_role[role].m_pChangeWidget->getText()))
            m_role[role].m_pChangeWidget -> show();
      else
            m_role[role].m_pChangeWidget -> hide();
}

00461 bool AssociationWidget::linePathStartsAt(const UMLWidget* widget) {
      QPoint lpStart = m_LinePath.getPoint(0);
      int startX = lpStart.x();
      int startY = lpStart.y();
      int wX = widget->getX();
      int wY = widget->getY();
      int wWidth = widget->getWidth();
      int wHeight = widget->getHeight();
      bool result = (startX >= wX && startX <= wX + wWidth &&
                  startY >= wY && startY <= wY + wHeight);
      return result;
}

00474 void AssociationWidget::setText(FloatingText *ft, QString text) {
      Uml::Text_Role role = ft->getRole();
      switch (role) {
            case tr_Name:
                  setName(text);
                  break;
            case tr_RoleAName:
                  setRoleName(text, A);
                  break;
            case tr_RoleBName:
                  setRoleName(text, B);
                  break;
            case tr_MultiA:
                  setMulti(text, A);
                  break;
            case tr_MultiB:
                  setMulti(text, B);
                  break;
            default:
                  break;
      }
}

00497 bool AssociationWidget::activate() {
      if(isActivated())
            return true;

      bool status = true;
      Association_Type type = getAssocType();

      if (m_role[A].m_pWidget == NULL)
            setWidget(m_pView->findWidget(getWidgetID(A)), A);
      if (m_role[B].m_pWidget == NULL)
            setWidget(m_pView->findWidget(getWidgetID(B)), B);

      if(!m_role[A].m_pWidget || !m_role[B].m_pWidget) {
            kdDebug() << "Can't make association" << endl;
            return false;
      }

      calculateEndingPoints();
      m_LinePath.activate();

      if (AssocRules::allowRole(type)) {
            for (unsigned r = A; r <= B; r++) {
                  WidgetRole& robj = m_role[r];
                  if (robj.m_pRole == NULL)
                        continue;
                  robj.m_pRole->setLink(this);
                  Text_Role tr = (r == A ? tr_RoleAName : tr_RoleBName);
                  robj.m_pRole->setRole(tr);
                  Scope scope = getVisibility((Role_Type)r);
                  robj.m_pRole->setPreText(UMLAssociation::ScopeToString(scope));

                  if (FloatingText::isTextValid(robj.m_pRole->getText()))
                        robj.m_pRole -> show();
                  else
                        robj.m_pRole -> hide();
                  if (m_pView->getType() == dt_Collaboration)
                        robj.m_pRole->setUMLObject(robj.m_pWidget->getUMLObject());
                  robj.m_pRole->activate();
            }
      }

      if( m_pName != NULL ) {
            m_pName->setLink(this);
            m_pName->setRole( CalculateNameType(tr_Name) );

            if ( FloatingText::isTextValid(m_pName->getText()) ) {
                  m_pName-> show();
            } else {
                  m_pName-> hide();
            }
            if( m_pView->getType() == dt_Collaboration && m_pName) {
                  m_pName->setUMLObject(m_role[B].m_pWidget->getUMLObject());
            }
            m_pName->activate();
            calculateNameTextSegment();
      }

      for (unsigned r = A; r <= B; r++) {
            WidgetRole& robj = m_role[r];

            FloatingText* pMulti = robj.m_pMulti;
            if (pMulti != NULL &&
                AssocRules::allowMultiplicity(type, robj.m_pWidget->getBaseType())) {
                  pMulti->setLink(this);
                  Text_Role tr = (r == A ? tr_MultiA : tr_MultiB);
                  pMulti->setRole(tr);
                  if (FloatingText::isTextValid(pMulti->getText()))
                        pMulti -> show();
                  else
                        pMulti -> hide();
                  pMulti->activate();
            }

            FloatingText* pChangeWidget = robj.m_pChangeWidget;
            if (pChangeWidget != NULL ) {
                  pChangeWidget->setLink(this);
                  Text_Role tr = (r == A ? tr_ChangeA : tr_ChangeB);
                  pChangeWidget->setRole(tr);
                  if (FloatingText::isTextValid(pChangeWidget->getText()))
                        pChangeWidget -> show();
                  else
                        pChangeWidget -> hide ();
                  pChangeWidget->activate();
            }
      }

      // CHECK
      // m_LinePath.setAssocType( getAssocType() );
      if(status) {
            m_bActivated = true;
      }
      return status;
}

/** This function calculates which role should be set for the m_pName FloatingText */
00592 Uml::Text_Role AssociationWidget::CalculateNameType(Text_Role defaultRole) {

      Text_Role result = defaultRole;
      if( m_pView -> getType() == dt_Collaboration ) {
            if(m_role[A].m_pWidget == m_role[B].m_pWidget) {
                  result = tr_Coll_Message;//for now same as other Coll_Message
            } else {
                  result = tr_Coll_Message;
            }
      } else if( m_pView -> getType() == dt_Sequence ) {
            if(m_role[A].m_pWidget == m_role[B].m_pWidget) {
                  result = tr_Seq_Message_Self;
            } else {
                  result = tr_Seq_Message;
            }
      }

      return result;
}

00612 UMLWidget* AssociationWidget::getWidget(Role_Type role) {
      return m_role[role].m_pWidget;
}

00616 bool AssociationWidget::setWidgets( UMLWidget* widgetA,
                            Association_Type assocType,
                            UMLWidget* widgetB) {
      //if the association already has a WidgetB or WidgetA associated, then
      //it cannot be changed to other widget, that would require a  deletion
      //of the association and the creation of a new one
      if ((m_role[A].m_pWidget && (m_role[A].m_pWidget != widgetA)) ||
          (m_role[B].m_pWidget && (m_role[B].m_pWidget != widgetB))) {
            return false;
      }
      setWidget(widgetA, A);
      setAssocType(assocType);
      setWidget(widgetB, B);

      calculateEndingPoints();
      return true;
}

/** Returns true if this association associates WidgetA to WidgetB, otherwise it returns
    false */
00636 bool AssociationWidget::checkAssoc(UMLWidget * widgetA, UMLWidget *widgetB) {
      return (widgetA == m_role[A].m_pWidget && widgetB == m_role[B].m_pWidget);
}

/** CleansUp all the association's data in the related widgets  */
00641 void AssociationWidget::cleanup() {

      //let any other associations know we are going so they can tidy their positions up
      if(m_role[A].m_nTotalCount > 2)
            updateAssociations(m_role[A].m_nTotalCount - 1, m_role[A].m_WidgetRegion, A);
      if(m_role[B].m_nTotalCount > 2)
            updateAssociations(m_role[B].m_nTotalCount - 1, m_role[B].m_WidgetRegion, B);

      for (unsigned r = A; r <= B; r++) {
            WidgetRole& robj = m_role[r];

            if(robj.m_pWidget) {
                  robj.m_pWidget->removeAssoc(this);
                  robj.m_pWidget = 0;
            }
            if(robj.m_pRole) {
                  m_pView->removeWidget(robj.m_pRole);
                  robj.m_pRole = 0;
            }
            if(robj.m_pMulti) {
                  m_pView->removeWidget(robj.m_pMulti);
                  robj.m_pMulti = 0;
            }
            if(robj.m_pChangeWidget) {
                  m_pView->removeWidget(robj.m_pChangeWidget);
                  robj.m_pChangeWidget = 0;
            }
      }

      if(m_pName) {
            m_pView->removeWidget(m_pName);
            m_pName = 0;
      }

      if (m_pObject) {
            /*
               We do not remove the UMLAssociation from the document.
               Why? - Well, for example we might be in the middle of
               a cut/paste. If the UMLAssociation is removed by the cut
               then upon pasteing we have a problem.
               This is not quite clean yet - there should be a way to
               explicitly delete a UMLAssociation.  The Right Thing would
               be to have a ListView representation for UMLAssociation.
`
                  IF we are cut n pasting, why are we handling this association as a pointer?
                  We should be using the XMI representation for a cut and paste. This
                  allows us to be clean here, AND a choice of recreating the object 
                  w/ same id IF its a "cut", or a new object if its a "copy" operation
                  (in which case we wouldnt be here, in cleanup()).
             */
            setUMLAssociation(0);
      }

      m_LinePath.cleanup();
}

00697 void AssociationWidget::setUMLAssociation (UMLAssociation * assoc) 
{
      if (m_pObject) {
            UMLAssociation *umla = getAssociation();

            // safety check. Did some num-nuts try to set the existing
            // association again? If so, just bail here
            if (assoc && umla == assoc)
                  return;

            umla->disconnect(this);
            umla->nrof_parent_widgets--;

            // we are the last "owner" of this association, so delete it
            // from the parent UMLDoc, and as a stand-alone
            //DISCUSS: Should we really do this?
            //    It implies that an association's existence is ONLY
            //    governed by its existence on at least one diagram.
            //    OTOH, it might be argued that an association should
            //    further exist even when it's (temporarily) not present
            //    on any diagram. This is exactly what cut and paste
            //    relies on (at least the way it's implemented now)
            // ANSWER: yes, we *should* do this.
            //   This only implies that IF an association once 'belonged'
            //   to one or more parent associationwidgets, then it must 'die' when the
            //   last widget does. UMLAssociations which never had a parent
            //   in the first place wont be affected by this code, and can happily
            //   live on without a parent.
            //DISCUSS: Sorry Brian, but this breaks cut/paste.
            //    In particular, cut/paste means that the UMLAssociation _does_
            //    have the assocwidget parent - the only means of doing a cut/paste
            //    on the diagram is via the widgets. I.e. in practice there is no
            //    such thing as an "orphan" UMLAssociation.
            //    BTW, IMHO the concept of a widget being the parent of a UML object
            //    is fundamentally flawed. Widgets are pure presentation - they can
            //    come and go at a whim. If at all, the widgets could be considered
            //    children of the corresponding UML object.
            //   
            // ANSWER: This is the wrong treatment of cut and paste. Associations that
            // are being cut/n pasted should be serialized to XMI, then reconstituted
            // (IF a paste operation) rather than passing around object pointers. Its
            // just too hard otherwise to prevent problems in the code. Bottom line: we need to
            // delete orphaned associations or we well get code crashes and memory leaks.
            if (umla->nrof_parent_widgets == 0) {
                  //umla->deleteLater();
            }
      
            m_pObject = NULL;
      }

      if(assoc) {
            m_pObject = assoc;
      
            // move counter to "0" from "-1" (which means, no assocwidgets) 
            if(assoc->nrof_parent_widgets < 0)
                  assoc->nrof_parent_widgets = 0;

            assoc->nrof_parent_widgets++;
            connect(assoc, SIGNAL(modified()), this,
                              SLOT(mergeUMLRepresentationIntoAssociationData()));
      }

}


/** Returns true if the Widget is either at the starting or ending side of the association */
00763 bool AssociationWidget::contains(UMLWidget* widget) {
      return (widget == m_role[A].m_pWidget || widget == m_role[B].m_pWidget);
}

00767 Association_Type AssociationWidget::getAssocType() const {
      if (m_pObject == NULL)
            return m_AssocType;
      UMLAssociation *umla = static_cast<UMLAssociation*>(m_pObject);
      return umla->getAssocType();
}

/** Sets the association's type */
00775 void AssociationWidget::setAssocType(Association_Type type) {
      if (m_pObject)
            getAssociation()->setAssocType(type);
      m_AssocType = type;
      m_LinePath.setAssocType(type);
      // If the association new type is not supposed to have Multiplicity
      // FloatingTexts and a Role FloatingText then set the internal
      // floating text pointers to null.
      if( !AssocRules::allowMultiplicity(type, getWidget(A)->getBaseType()) ) {
            if (m_role[A].m_pMulti) {
                  delete m_role[A].m_pMulti;
                  m_role[A].m_pMulti = NULL;
            }
            if (m_role[B].m_pMulti) {
                  delete m_role[B].m_pMulti;
                  m_role[B].m_pMulti = NULL;
            }
      }
      if( !AssocRules::allowRole( type ) ) {
            if (m_role[A].m_pRole) {
                  delete m_role[A].m_pRole;
                  m_role[A].m_pRole = NULL;
            }
            if (m_role[B].m_pRole) {
                  delete m_role[B].m_pRole;
                  m_role[B].m_pRole = NULL;
            }
            setRoleDoc("", A);
            setRoleDoc("", B);
      }
}

00807 int AssociationWidget::getWidgetID(Role_Type role) const {
      if (m_role[role].m_pWidget == NULL) {
            if (m_pObject) {
                  UMLAssociation *umla = static_cast<UMLAssociation*>(m_pObject);
                  return umla->getRoleId(role);
            }
            kdError() << "AssociationWidget::getWidgetID(): m_pWidget is NULL" << endl;
            return -1;
      }
      if (m_role[role].m_pWidget->getBaseType() == Uml::wt_Object)
            return static_cast<ObjectWidget*>(m_role[role].m_pWidget)->getLocalID();
      int id = m_role[role].m_pWidget->getID();
      return id;
}

/** Returns a QString Object representing this AssociationWidget */
00823 QString AssociationWidget::toString() {
      QString string = "";

      if(m_role[A].m_pWidget) {
            string = m_role[A].m_pWidget -> getName();
      }
      string.append(":");

      if(m_role[A].m_pRole) {
            string += m_role[A].m_pRole -> getText();
      }
      string.append(":");
      string.append( UMLAssociation::typeAsString(getAssocType()) );
      string.append(":");
      if(m_role[B].m_pWidget) {
            string += m_role[B].m_pWidget -> getName();
      }

      if(m_role[B].m_pRole) {
            string += m_role[B].m_pRole -> getText();
      }

      return string;
}

00848 void AssociationWidget::mouseDoubleClickEvent(QMouseEvent * me) {
      if(me -> button() != RightButton && me->button() != LeftButton)
            return;
      int i = 0;
      if( ( i = m_LinePath.onLinePath( me -> pos() ) ) == -1 )
      {
            m_LinePath.setSelected(false);
            return;
      }
      if(me->button() == LeftButton) {
            /* if there is no point around the mouse pointer, we insert a new one */
            if (! m_LinePath.isPoint(i, me -> pos(), POINT_DELTA )) {
                  m_LinePath.insertPoint( i, me -> pos() );
            } else {
                  /* deselect the line path */
                  m_LinePath.setSelected( false );

                  /* there was a point so we remove the point */
                  m_LinePath.removePoint(i, me -> pos(), POINT_DELTA );

                  /* select the line path */
                  m_LinePath.setSelected( true );
            }

            m_LinePath.update();

            //calculateRoleTextSegment();
            calculateNameTextSegment();
      }
}

00879 void AssociationWidget::moveEvent(QMoveEvent* me) {
      // 2004-04-30: Achim Spangler
      // Simple Approach to block moveEvent during load of
      // XMI
      /// @todo avoid trigger of this event during load
      if ( UMLApp::app()->getDocument()->loading() ) {
            // hmmh - change of position during load of XMI
            // -> there is something wrong
            // -> avoid movement during opening
            // -> print warn and stay at old position
            kdWarning() << "AssociationWidget::moveEvent() called during load of XMI for ViewType: " << m_pView -> getType()
                  << ", and BaseType: " << getBaseType()
                  << endl;
            return;
      }
      /*to be here a line segment has moved.
        we need to see if the three text widgets needs to be moved.
        there are a few things to check first though:

        1) Do they exist
        2) does it need to move:
        2a) for the multi widgets only move if they changed region, otherwise they are close enough
        2b) for role name move if the segment it is on moves.
      */
      //first see if either the first or last segments moved, else no need to recalculate their point positions

      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);

      m_LinePath.setPoint( m_nMovingPoint, me->pos() );
      int pos = m_LinePath.count() - 1;//set to last point for widget b

      if ( m_nMovingPoint == 1 || (m_nMovingPoint == pos-1) ) {
            calculateEndingPoints();
      }
      if (m_role[A].m_pChangeWidget && (m_nMovingPoint == 1)) {
            setTextPositionRelatively(tr_ChangeA, calculateTextPosition(tr_ChangeA), oldChangeAPoint);
      }
      if (m_role[B].m_pChangeWidget && (m_nMovingPoint == 1)) {
            setTextPositionRelatively(tr_ChangeB, calculateTextPosition(tr_ChangeB), oldChangeBPoint);
      }
      if (m_role[A].m_pMulti && (m_nMovingPoint == 1)) {
            setTextPositionRelatively(tr_MultiA, calculateTextPosition(tr_MultiA), oldMultiAPoint);
      }
      if (m_role[B].m_pMulti && (m_nMovingPoint == pos-1)) {
            setTextPositionRelatively(tr_MultiB, calculateTextPosition(tr_MultiB), oldMultiBPoint);
      }

      if (m_pName) {
            if(m_nMovingPoint == (int)m_unNameLineSegment ||
               m_nMovingPoint - 1 == (int)m_unNameLineSegment) {
                  setTextPositionRelatively(tr_Name, calculateTextPosition(tr_Name), oldNamePoint);
            }
      }

      if (m_role[A].m_pRole) {
            setTextPositionRelatively(tr_RoleAName, calculateTextPosition(tr_RoleAName), oldRoleAPoint);
      }
      if (m_role[B].m_pRole) {
            setTextPositionRelatively(tr_RoleBName, calculateTextPosition(tr_RoleBName), oldRoleBPoint);
      }
}


/** Calculates and sets the first and last point in the Association's LinePath
    Each point is a middle point of its respecting UMLWidget's Bounding rectangle
    or a corner of it
    This method picks which sides to use for the association */
00952 void AssociationWidget::calculateEndingPoints() {
      /*
       * For each UMLWidget the diagram is divided in four regions by its diagonals
       * as indicated below
       *                             Region 2
       *                          \       /
       *                            \         /
       *                            +--------+
       *                            | \    / |
       *                   Region 1     |   ><   |    Region 3
       *                            | /    \ |
       *                            +--------+
       *                            /         \
       *                          /       \
       *                             Region 4
       *
       * Each diagonal is defined by two corners of the bounding rectangle
       *
       * To calculate the first point in the LinePath we have to find out in which
       * Region (defined by WidgetA's diagonals) is WidgetB's center
       * (let's call it Region M.) After that the first point will be the middle
       * point of the rectangle's side contained in Region M.
       *
       * To calculate the last point in the LinePath we repeat the above but
       * in the opposite direction (from widgetB to WidgetA)
       */

      UMLWidget *pWidgetA = m_role[A].m_pWidget;
      UMLWidget *pWidgetB = m_role[B].m_pWidget;
      if (!pWidgetA || !pWidgetB)
            return;
      m_role[A].m_OldCorner.setX( pWidgetA->getX() );
      m_role[A].m_OldCorner.setY( pWidgetA->getY() );
      m_role[B].m_OldCorner.setX( pWidgetB->getX() );
      m_role[B].m_OldCorner.setY( pWidgetB->getY() );
      uint size = m_LinePath.count();
      if(size < 2)
            m_LinePath.setStartEndPoints( m_role[A].m_OldCorner, m_role[B].m_OldCorner );

      // See if an association to self.
      // See if it needs to be set up before we continue:
      // If self association/message and doesn't have the minimum 4 points
      // then create it.  Make sure no points are out of bounds of viewing area.
      // This only happens on first time through that we are worried about.
      if (pWidgetA == pWidgetB && size < 4) {
            const int DISTANCE = 50;
            int x = pWidgetA -> getX();
            int y = pWidgetA -> getY();
            int h = pWidgetA -> getHeight();
            int w = pWidgetA -> getWidth();
            //see if above widget ok to start
            if( y - DISTANCE > 0 ) {
                  m_LinePath.setStartEndPoints( QPoint( x + w / 4, y ) , QPoint( x + w * 3 / 4, y ) );
                  m_LinePath.insertPoint( 1, QPoint( x + w / 4, y - DISTANCE ) );
                  m_LinePath.insertPoint( 2 ,QPoint( x + w * 3 / 4, y - DISTANCE ) );
                  m_role[A].m_WidgetRegion = m_role[B].m_WidgetRegion = North;
            } else {
                  m_LinePath.setStartEndPoints( QPoint( x + w / 4, y + h ), QPoint( x + w * 3 / 4, y + h ) );
                  m_LinePath.insertPoint( 1, QPoint( x + w / 4, y + h + DISTANCE ) );
                  m_LinePath.insertPoint( 2, QPoint( x + w * 3 / 4, y + h + DISTANCE ) );
                  m_role[A].m_WidgetRegion = m_role[B].m_WidgetRegion = South;
            }
            return;
      }//end a == b

      // If the line has more than one segment change the values to calculate
      // from widget to point 1.
      int xB = pWidgetB->getX() + pWidgetB->getWidth() / 2;
      int yB = pWidgetB->getY() + pWidgetB->getHeight() / 2;
      if( size > 2 ) {
            QPoint p = m_LinePath.getPoint( 1 );
            xB = p.x();
            yB = p.y();
      }
      doUpdates(xB, yB, A);

      // Now do the same for widgetB.
      // If the line has more than one segment change the values to calculate
      // from widgetB to the last point away from it.
      int xA = pWidgetA->getX() + pWidgetA->getWidth() / 2;
      int yA = pWidgetA->getY() + pWidgetA->getHeight() / 2;
      if (size > 2 ) {
            QPoint p = m_LinePath.getPoint( size - 2 );
            xA = p.x();
            yA = p.y();
      }
      doUpdates( xA, yA, B );
}

01041 void AssociationWidget::doUpdates(int otherX, int otherY, Role_Type role) {
      // Find widget region.
      Region oldRegion = m_role[role].m_WidgetRegion;
      UMLWidget *pWidget = m_role[role].m_pWidget;
      QRect rc(pWidget->getX(), pWidget->getY(),
             pWidget->getWidth(), pWidget->getHeight());
      Region& region = m_role[role].m_WidgetRegion;  // alias for brevity
      region = findPointRegion( rc, otherX, otherY);
      // Move some regions to the standard ones.
      switch( region ) {
      case NorthWest:
            region = North;
            break;
      case NorthEast:
            region = East;
            break;
      case SouthEast:
            region = South;
            break;
      case SouthWest:
      case Center:
            region = West;
            break;
      default:
            break;
      }
      int regionCount = getRegionCount(region, role) + 2;//+2 = (1 for this one and one to halve it)
      int totalCount = m_role[role].m_nTotalCount;
#ifdef DEBUG_ASSOCLINES
      kdDebug() << endl;
      kdDebug() << "doUpdates (role=" << role << "): widget=" << pWidget->getName()
              << " region=" << region << " regionCount=" << regionCount
              << " totalCount=" << totalCount << endl;
#endif
      if( oldRegion != region ) {
            updateRegionLineCount( regionCount - 1, regionCount, region, role );
            updateAssociations( totalCount - 1, oldRegion, role );
      } else if( totalCount != regionCount ) {
            updateRegionLineCount( regionCount - 1, regionCount, region, role );
      } else {
            updateRegionLineCount( m_role[role].m_nIndex, totalCount, region, role );
      }
      updateAssociations( regionCount, region, role );
}

/** Read property of bool m_bActivated. */
01087 const bool AssociationWidget::isActivated() {
      return m_bActivated;
}

/** Set the m_bActivated flag of a widget but does not perform the Activate method */
01092 void AssociationWidget::setActivated(bool active /*=true*/) {
      m_bActivated = active;
}

// ugly. but its what we are forced into by having the ugly association
// widget data NOT be stored in the UMLAssociation. *sigh*.
//   Yes, but there's a reason for this separation:
//   A single UMLAssociation can be represented in different
//   diagrams. Each graphical representation requires its own
//   AssociationWidget.
//   Besides, it's a good idea to separate the document objects
//   from their graphical representations. That's the way all
//   the rest of umbrello is structured.  --okellogg
01105 void AssociationWidget::mergeUMLRepresentationIntoAssociationData()
{
      UMLAssociation *uml = getAssociation();

      // block signals until finished
      uml->blockSignals(true);

      // floating text widgets
      FloatingText *text = getNameWidget();
      if (text)
            text->setText(uml->getName());

      text = getRoleWidget(A);
      if (text)
      {
            text->setText(uml->getRoleName(A));
            // it doesnt make sense to have visibility wi/o Rolename
            // so we only set it when its in here. Probably should have
            // error condition thrown when visb is set but rolename isnt.
            setVisibility(uml->getVisibility(A), A);
      }

      text = getRoleWidget(B);
      if (text)
      {
            text->setText(uml->getRoleName(B));
            setVisibility(uml->getVisibility(B), B);
      }

      text = getMultiWidget(A);
      if (text)
            text->setText(uml->getMulti(A));

      text = getMultiWidget(B);
      if (text)
            text->setText(uml->getMulti(B));

      uml->blockSignals(false);

}

// this will synchronize UMLAssociation w/ this new Widget
01147 void AssociationWidget::mergeAssociationDataIntoUMLRepresentation()
{

      UMLAssociation *uml = getAssociation();
      if (uml == NULL)
            return;

      // block emit modified signal, or we get a horrible loop
      uml->blockSignals(true);

      // would be desirable to do the following
      // so that we can be sure its back to initial state
      // in case we missed something here.
      //uml->init();

      // floating text widgets
      FloatingText *text = getNameWidget();
      if (text)
            uml->setName(text->getText());

      text = getRoleWidget(A);
      if (text)
            uml->setRoleName(text->getText(), A);

      text = getRoleWidget(B);
      if (text)
            uml->setRoleName(text->getText(), B);

      text = getMultiWidget(A);
      if (text)
            uml->setMulti(text->getText(), A);

      text = getMultiWidget(B);
      if (text)
            uml->setMulti(text->getText(), B);

      // unblock
      uml->blockSignals(false);
}

/** Adjusts the ending point of the association that connects to Widget */
01188 void AssociationWidget::widgetMoved(UMLWidget* widget, int x, int y ) {
      // 2004-04-30: Achim Spangler
      // Simple Approach to block moveEvent during load of
      // XMI
      /// @todo avoid trigger of this event during load
      if ( UMLApp::app()->getDocument()->loading() ) {
            // hmmh - change of position during load of XMI
            // -> there is something wrong
            // -> avoid movement during opening
            // -> print warn and stay at old position
            kdDebug() << "AssociationWidget::widgetMoved() called during load of XMI for ViewType: " << m_pView -> getType()
                  << ", and BaseType: " << getBaseType()
                  << 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;
      uint size = m_LinePath.count();
      uint pos = size - 1;
      calculateEndingPoints();

      // Assoc to self - move all points:
      if( m_role[A].m_pWidget == m_role[B].m_pWidget ) {
            for( int i=1 ; i < (int)pos ; i++ ) {
                  QPoint p = m_LinePath.getPoint( i );
                  int newX = p.x() - dx;
                  int newY = p.y() - dy;
                  // safety. We DONT want to go off the screen
                  if(newX < 0)
                        newX = 0;
                  // safety. We DONT want to go off the screen
                  if(newY < 0)
                        newY = 0;
                  newX = m_pView -> snappedX( newX );
                  newY = m_pView -> snappedY( newY );
                  p.setX( newX );
                  p.setY( newY );
                  m_LinePath.setPoint( i, p );
            }

            if ( m_pName && !m_pName->getSelected() ) {
                  setTextPositionRelatively(tr_Name, calculateTextPosition(tr_Name), 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, calculateTextPosition(tr_Name),
                                      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, calculateTextPosition(tr_Name),
                                      oldNamePoint);
            }
      }//end if widgetB moved

      if ( m_role[A].m_pRole && !m_role[A].m_pRole->getSelected() ) {
            setTextPositionRelatively(tr_RoleAName, calculateTextPosition(tr_RoleAName), oldRoleAPoint);
      }
      if ( m_role[B].m_pRole && !m_role[B].m_pRole->getSelected() ) {
            setTextPositionRelatively(tr_RoleBName, calculateTextPosition(tr_RoleBName), oldRoleBPoint);
      }
      if ( m_role[A].m_pMulti && !m_role[A].m_pMulti->getSelected() ) {
            setTextPositionRelatively(tr_MultiA, calculateTextPosition(tr_MultiA), oldMultiAPoint);
      }
      if ( m_role[B].m_pMulti && !m_role[B].m_pMulti->getSelected() ) {
            setTextPositionRelatively(tr_MultiB, calculateTextPosition(tr_MultiB), oldMultiBPoint);
      }
      if ( m_role[A].m_pChangeWidget && !m_role[A].m_pChangeWidget->getSelected() ) {
            setTextPositionRelatively(tr_ChangeA, calculateTextPosition(tr_ChangeA), oldChangeAPoint);
      }
      if ( m_role[B].m_pChangeWidget && !m_role[B].m_pChangeWidget->getSelected() ) {
            setTextPositionRelatively(tr_ChangeB, calculateTextPosition(tr_ChangeB), oldChangeBPoint);
      }
}//end method widgetMoved

/** Finds out in which region of rectangle Rect contains the Point (PosX, PosY) and returns the region
    number:
    1 = Region 1
    2 = Region 2
    3 = Region 3
    4 = Region 4
    5 = On diagonal 2 between Region 1 and 2
    6 = On diagonal 1 between Region 2 and 3
    7 = On diagonal 2 between Region 3 and 4
    8 = On diagonal 1 between Region 4 and 1
    9 = On diagonal 1 and On diagonal 2 (the center)
*/
01288 AssociationWidget::Region AssociationWidget::findPointRegion(QRect Rect, int PosX, int PosY) {
      float w = (float)Rect.width();
      float h = (float)Rect.height();
      float x =  (float)Rect.x();
      float y = (float)Rect.y();
      float Slope2 = w / h;
      float Slope1 = Slope2*(float)(-1);
      float b1 = x + w - ( Slope1* y );
      float b2 = x - ( Slope2* y );

      float eval1 = Slope1 * (float)PosY + b1;
      float eval2 = Slope2  *(float)PosY + b2;

      Region result = Error;
      //if inside region 1
      if(eval1 > PosX && eval2 > PosX) {
            result = West;
      }
      //if inside region 2
      else if (eval1 > PosX && eval2 < PosX) {
            result = North;
      }
      //if inside region 3
      else if (eval1 < PosX && eval2 < PosX) {
            result = East;
      }
      //if inside region 4
      else if (eval1 < PosX && eval2 > PosX) {
            result = South;
      }
      //if inside region 5
      else if (eval1 == PosX && eval2 < PosX) {
            result = NorthWest;
      }
      //if inside region 6
      else if (eval1 < PosX && eval2 == PosX) {
            result = NorthEast;
      }
      //if inside region 7
      else if (eval1 == PosX && eval2 > PosX) {
            result = SouthEast;
      }
      //if inside region 8
      else if (eval1 > PosX && eval2 == PosX) {
            result = SouthWest;
      }
      //if inside region 9
      else if (eval1 == PosX && eval2 == PosX) {
            result = Center;
      }
      return result;
}

01341 QPoint AssociationWidget::swapXY(QPoint p) {
      QPoint swapped( p.y(), p.x() );
      return swapped;
}

/* Returns the total length of the association's LinePath:
   result = segment_1_length + segment_2_length + ..... + segment_n_length
 */
01349 float AssociationWidget::totalLength() {
      uint size = m_LinePath.count();
      float total_length = 0;

      for(uint i = 0; i < size - 1; i++) {
            QPoint pi = m_LinePath.getPoint( i );
            QPoint pj = m_LinePath.getPoint( i+1 );
            int xi = pi.y();
            int xj = pj.y();
            int yi = pi.x();
            int yj = pj.x();
            total_length +=  sqrt( double(((xj - xi)*(xj - xi)) + ((yj - yi)*(yj - yi))) );
      }

      return total_length;
}


/** Calculates which point of segment P1P2 has a distance equal to Distance from P1,
    Lets say such point is P3,  the distance from P1 to P3 must be equal to Distance
    and if P3 is not a point of the segment P1P2 then the function returns (-1,-1)
*/
01371 QPoint AssociationWidget::calculatePointAtDistance(QPoint P1, QPoint P2, float Distance) {
      /*
        the distance D between points (x1, y1) and (x3, y3) has the following formula:
            ---     ------------------------------
        D =    \   /       2       2
              \ /   (x3 - x1)  +  (y3 - y1)

        D, x1 and y1 are known and the point (x3, y3) is inside line (x1,y1)(x2,y2), so if the
        that line has the formula y = mx + b
        then y3 = m*x3 + b

         2       2           2
        D   = (x3 - x1)  +  (y3 - y1)

         2       2             2      2          2
        D    = x3    - 2*x3*x1 + x1   + y3   - 2*y3*y1  + y1

         2       2       2       2          2
        D    - x1    - y1    = x3    - 2*x3*x1  + y3   - 2*y3*y1

         2       2       2       2                2
        D    - x1    - y1    = x3    - 2*x3*x1  + (m*x3 + b)  - 2*(m*x3 + b)*y1

         2       2       2          2       2   2
        D    - x1    - y1   + 2*b*y1 - b   =  (m  + 1)*x3   + (-2*x1 + 2*m*b -2*m*y1)*x3

         2      2       2       2
        C  = - D    + x1    + y1   - 2*b*y1 + b


         2
        A  = (m    + 1)

        B  = (-2*x1 + 2*m*b -2*m*y1)

        and we have
         2
        A * x3 + B * x3 - C = 0

                         ---------------
                 -B +  ---  /  2
                        \/  B   - 4*A*C
        sol_1  = --------------------------------
                         2*A


                         ---------------
                 -B -  ---  /  2
                        \/  B   - 4*A*C
        sol_2  = --------------------------------
                         2*A


        then in the distance formula we have only one variable x3 and that is easy
        to calculate
      */
      int x1 = P1.y();
      int y1 = P1.x();
      int x2 = P2.y();
      int y2 = P2.x();

      if(x2 == x1) {
            return QPoint(x1, y1 + (int)Distance);
      }
      float slope = ((float)y2 - (float)y1) / ((float)x2 - (float)x1);
      float b = (y1 - slope*x1);
      float A = (slope * slope) + 1;
      float B = (2*slope*b) - (2*x1)  - (2*slope*y1);
      float C = (b*b) - (Distance*Distance) + (x1*x1) + (y1*y1) - (2*b*y1);
      float t = B*B - 4*A*C;

      if(t < 0) {
            return QPoint(-1, -1);
      }
      float sol_1 = ((-1* B) + sqrt(t) ) / (2*A);
      float sol_2 = ((-1*B) - sqrt(t) ) / (2*A);

      if(sol_1 < 0.0 && sol_2 < 0.0) {
            return QPoint(-1, -1);
      }
      QPoint sol1Point((int)(slope*sol_1 + b), (int)(sol_1));
      QPoint sol2Point((int)(slope*sol_2 + b), (int)(sol_2));
      if(sol_1 < 0 && sol_2 >=0) {
            if(x2 > x1) {
                  if(x1 <= sol_2 && sol_2 <= x2)
                        return sol2Point;
            } else {
                  if(x2 <= sol_2 && sol_2 <= x1)
                        return sol2Point;
            }
      } else if(sol_1 >= 0 && sol_2 < 0) {
            if(x2 > x1) {
                  if(x1 <= sol_1 && sol_1 <= x2)
                        return sol1Point;
            } else {
                  if(x2 <= sol_1 && sol_1 <= x1)
                        return sol1Point;
            }
      } else {
            if(x2 > x1) {
                  if(x1 <= sol_1 && sol_1 <= x2)
                        return sol1Point;
                  if(x1 <= sol_2 && sol_2 <= x2)
                        return sol2Point;
            } else {
                  if(x2 <= sol_1 && sol_1 <= x1)
                        return sol1Point;
                  if(x2 <= sol_2 && sol_2 <= x1)
                        return sol2Point;
            }
      }
      return QPoint(-1, -1);
}

/** Calculates which point of a perpendicular line to segment P1P2 that contains P2
    has a distance equal to Distance from P2,
    Lets say such point is P3,  the distance from P2 to P3 must be equal to Distance
*/
01489 QPoint AssociationWidget::calculatePointAtDistanceOnPerpendicular(QPoint P1, QPoint P2, float Distance) {
      /*
        the distance D between points (x2, y2) and (x3, y3) has the following formula:

             ---     ------------------------------
        D =     \   /      2       2
               \ /   (x3 - x2)  +  (y3 - y2)

        D, x2 and y2 are known and line P2P3 is perpendicular to line (x1,y1)(x2,y2), so if the
        line P1P2 has the formula y = m*x + b,
        then        (x1 - x2)
              m =  -----------    , because it is perpendicular to line P1P2
                  (y2 - y1)

        also y2 = m*x2 + b
        => b = y2 - m*x2

        then P3 = (x3, m*x3 + b)

         2       2          2
        D  = (x3 - x2)  + (y3 - y2)

         2     2         2      2          2
        D  = x3    - 2*x3*x2 + x2   + y3   - 2*y3*y2  + y2

         2       2       2       2          2
        D    - x2    - y2    = x3    - 2*x3*x2  + y3   - 2*y3*y2



         2       2       2       2                2
        D    - x2    - y2    = x3    - 2*x3*x2  + (m*x3 + b)  - 2*(m*x3 + b)*y2

         2       2       2          2       2   2
        D    - x2    - y2     + 2*b*y2 - b   =  (m  + 1)*x3     + (-2*x2 + 2*m*b -2*m*y2)*x3

         2      2       2       2
        C  = - D    + x2    + y2   - 2*b*y2 + b

         2
        A  = (m    + 1)

        B  = (-2*x2 + 2*m*b -2*m*y2)

        and we have
         2
        A * x3 + B * x3 - C = 0


                         ---------------
                 -B +  ---  /  2
                        \/  B   - 4*A*C
        sol_1  = --------------------------------
                         2*A


                         ---------------
        sol_2  =   -B -  ---  /  2
                        \/  B   - 4*A*C
               --------------------------------
                         2*A

        then in the distance formula we have only one variable x3 and that is easy
        to calculate
      */
      int x1 = P1.y();
      int y1 = P1.x();
      int x2 = P2.y();
      int y2 = P2.x();

      if(x2 == x1) {
            return QPoint((int)(x2+ Distance), y2);
      }
      float slope = ((float)x1 - (float)x2) / ((float)y2 - (float)y1);
      float b = (y2 - slope*x2);
      float A = (slope * slope) + 1;
      float B = (2*slope*b) - (2*x2)  - (2*slope*y2);
      float C = (b*b) - (Distance*Distance) + (x2*x2) + (y2*y2) - (2*b*y2);
      float t = B*B - 4*A*C;
      if(t < 0) {
            return QPoint(-1, -1);
      }
      float sol_1 = ((-1* B) + sqrt(t) ) / (2*A);

      float sol_2 = ((-1*B) - sqrt(t) ) / (2*A);

      if(sol_1 < 0 && sol_2 < 0) {
            return QPoint(-1, -1);
      }
      QPoint sol1Point((int)(slope*sol_1 + b), (int)sol_1);
      QPoint sol2Point((int)(slope*sol_2 + b), (int)sol_2);
      if(sol_1 < 0 && sol_2 >=0) {
            return sol2Point;
      } else if(sol_1 >= 0 && sol_2 < 0) {
            return sol1Point;
      } else {    // Choose one solution , either will work fine
            if(slope >= 0) {
                  if(sol_1 <= sol_2)
                        return sol2Point;
                  else
                        return sol1Point;
            } else {
                  if(sol_1 <= sol_2)
                        return sol1Point;
                  else
                        return sol2Point;
            }

      }
      return QPoint(-1, -1);  // never reached, just keep compilers happy
}

/** Calculates the intersection (PS) between line P1P2 and a perpendicular line containing
    P3, the result is returned in ResultingPoint. and result value represents the distance
    between ResultingPoint and P3; if this value is negative an error ocurred. */
01604 float AssociationWidget::perpendicularProjection(QPoint P1, QPoint P2, QPoint P3,
                                     QPoint& ResultingPoint) {
      //line P1P2 is Line 1 = y=slope1*x + b1

      //line P3PS is Line 1 = y=slope2*x + b2

      float slope2 = 0;
      float slope1 = 0;
      float sx = 0, sy = 0;
      int y2 = P2.x();
      int y1 = P1.x();
      int x2 = P2.y();
      int x1 = P1.y();
      int y3 = P3.x();
      int x3 = P3.y();
      float distance = 0;
      float b1 = 0;

      float b2 = 0;

      if(x2 == x1) {
            sx = x2;
            sy = y3;
      } else if(y2 == y1) {
            sy = y2;
            sx = x3;
      } else {
            slope1 = (y2 - y1)/ (x2 - x1);
            slope2 = (x1 - x2)/ (y2 - y1);
            b1 = y2 - (slope1 * x2);
            b2 = y3 - (slope2 * x3);
            sx = (b2 - b1) / (slope1 - slope2);
            sy = slope1*sx + b1;
      }
      distance = (int)( sqrt( ((x3 - sx)*(x3 - sx)) + ((y3 - sy)*(y3 - sy)) ) );

      ResultingPoint.setX( (int)sy );
      ResultingPoint.setY( (int)sx );

      return distance;
}

01646 QPoint AssociationWidget::calculateTextPosition(Text_Role role) {
      QPoint p( -1, -1 ), q( -1, -1 );
      uint size = m_LinePath.count();
      uint pos = size - 1;
      int x = 0, y = 0;
      int textW = 0, textH = 0;
      int slope = 0, divisor = 1;
      const int SPACE = 2;
      FloatingText const * text = 0;

      if(role == tr_MultiA) {
            text = getMultiWidget(A);
            if( text ) {
                  textW = text -> width();
                  textH = text -> height();
            }
            p = m_LinePath.getPoint( 0 );
            q = m_LinePath.getPoint( 1 );
            divisor = (p.x()-q.x());
            if (divisor != 0)
                  slope = (p.y()-q.y())/divisor;
            else
                  slope = 10000;


            if( p.y() > q.y() )
                  if(slope == 0)
                        y = p.y() + SPACE;
                  else
                        y = p.y() - SPACE - textH;
            else
                  if(slope == 0)
                        y = p.y() - SPACE - textH;
                  else
                        y = p.y() + SPACE;

            if( p.x() < q.x() )
                  if(slope == 0)
                        x = p.x() + SPACE;
                  else
                        x = p.x() - SPACE - textW;
            else
                  if(slope == 0)
                        x = p.x() - SPACE - textW;
                  else
                        x = p.x() + SPACE;

      } else if(role == tr_MultiB) {
            text = getMultiWidget(B);
            if( text ) {
                  textW = text -> width();
                  textH = text -> height();
            }
            p = m_LinePath.getPoint( pos );
            q = m_LinePath.getPoint( pos - 1 );
            divisor = (p.x()-q.x());
            if (divisor != 0)
                  slope = (p.y()-q.y())/divisor;
            else
                  slope = 10000000;

            if( p.y() > q.y() )
            {
                  if(slope == 0)
                        y = p.y() - SPACE;
                  else
                        y = p.y() - textH - SPACE;
            } else
                  if(slope == 0)
                        y = p.y() - textH - SPACE;
                  else
                        y = p.y() + SPACE;


            if( p.x() < q.x() )
                  if(slope == 0)
                        x = p.x() + SPACE;
                  else
                        x = p.x() - textW + SPACE;
            else
                  if(slope == 0)
                        x = p.x() - textW + SPACE;
                  else
                        x = p.x() + SPACE;

      } else if(role == tr_Name) {

            x = (int)( ( m_LinePath.getPoint(m_unNameLineSegment).x() +
                       m_LinePath.getPoint(m_unNameLineSegment + 1).x() ) / 2 );

            y = (int)( ( m_LinePath.getPoint(m_unNameLineSegment).y() +
                       m_LinePath.getPoint(m_unNameLineSegment + 1).y() ) / 2 );

      } else if(role == tr_ChangeA) {

            text = getChangeWidget(A);

            if( text ) {
                  textW = text -> width();
                  textH = text -> height();
            }
            p = m_LinePath.getPoint( 0 );
            q = m_LinePath.getPoint( 1 );

            if( p.y() > q.y() )
                  y = p.y() - SPACE - (textH *2);
            else
                  y = p.y() + SPACE + textH;

            if( p.x() < q.x() )
                  x = p.x() + SPACE;
            else
                  x = p.x() - SPACE - textW;

      } else if(role == tr_ChangeB) {

            text = getChangeWidget(B);
            if( text ) {
                  textW = text -> width();
                  textH = text -> height();
            }

            p = m_LinePath.getPoint( pos );
            q = m_LinePath.getPoint( pos - 1 );

            if( p.y() > q.y() )
                  y = p.y() - (textH*2) - SPACE;
            else
                  y = p.y() + textH + SPACE;

            if( p.x() < q.x() )
                  x = p.x() + SPACE;
            else
                  x = p.x() - textW - SPACE;

      } else if(role == tr_RoleAName) {

            text = getRoleWidget(A);
            if( text ) {
                  textW = text -> width();
                  textH = text -> height();
            }
            p = m_LinePath.getPoint( 0 );
            q = m_LinePath.getPoint( 1 );

            if( p.y() > q.y() )
                  y = p.y() - SPACE - textH;
            else
                  y = p.y() + SPACE;

            if( p.x() < q.x() )
                  x = p.x() + SPACE;
            else
                  x = p.x() - SPACE - textW;
      }
      else if(role == tr_RoleBName)
      {
            text = getRoleWidget(B);
            if( text ) {
                  textW = text -> width();
                  textH = text -> height();
            }

            p = m_LinePath.getPoint( pos );
            q = m_LinePath.getPoint( pos - 1 );
            if( p.y() > q.y() )
                  y = p.y() - textH - SPACE;
            else
                  y = p.y() + SPACE;

            if( p.x() < q.x() )
                  x = p.x() + SPACE;
            else
                  x = p.x() - textW - SPACE;
      }
      p = QPoint( x, y );
      return p;
}

01825 void AssociationWidget::calculateNameTextSegment() {
      if(!m_pName) {
            return;
      }
      //changed to use the middle of the text
      //i think this will give a better result.
      //never know what sort of lines people come up with
      //and text could be long to give a false reading
      int xt = m_pName -> getX();
      int yt = m_pName -> getY();
      xt += m_pName -> getWidth() / 2;
      yt += m_pName -> getHeight() / 2;
      uint size = m_LinePath.count();
      //sum of length(PTP1) and length(PTP2)
      float total_length = 0;
      float smallest_length = 0;
      for(uint i = 0; i < size - 1; i++) {
            QPoint pi = m_LinePath.getPoint( i );
            QPoint pj = m_LinePath.getPoint( i+1 );
            int xtiDiff = xt - pi.x();
            int xtjDiff = xt - pj.x();
            int ytiDiff = yt - pi.y();
            int ytjDiff = yt - pj.y();
            total_length =  sqrt( double(xtiDiff * xtiDiff + ytiDiff * ytiDiff) )
                        + sqrt( double(xtjDiff * xtjDiff + ytjDiff * ytjDiff) );
            //this gives the closest point
            if( total_length < smallest_length || i == 0) {
                  smallest_length = total_length;
                  m_unNameLineSegment = i;
            }
      }
}

01858 FloatingText* AssociationWidget::floatingText(Text_Role role) {
      FloatingText *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;
}

01889 void AssociationWidget::setTextPosition(Text_Role role, QPoint pos) {
      bool startMove = false;
      if( m_role[A].m_pMulti && m_role[A].m_pMulti->getStartMove() )
            startMove = true;
      else if( m_role[B].m_pMulti && m_role[B].m_pMulti->getStartMove() )
            startMove = true;
      else if( m_role[A].m_pChangeWidget && m_role[A].m_pChangeWidget->getStartMove() )
            startMove = true;
      else if( m_role[B].m_pChangeWidget && m_role[B].m_pChangeWidget->getStartMove() )
            startMove = true;
      else if( m_role[A].m_pRole  && m_role[A].m_pRole->getStartMove() )
            startMove = true;
      else if( m_role[B].m_pRole  && m_role[B].m_pRole->getStartMove() )
            startMove = true;
      else if( m_pName && m_pName->getStartMove() )
            startMove = true;

      if (startMove) {
            return;
      }
      FloatingText *ft = floatingText(role);
      if (ft) {
            ft->setLinePos( pos.x(), pos.y() );
      }
}

01915 void AssociationWidget::setTextPositionRelatively(Text_Role role, QPoint pos, QPoint oldPosition) {
      bool startMove = false;
      if( m_role[A].m_pMulti && m_role[A].m_pMulti->getStartMove() )
            startMove = true;
      else if( m_role[B].m_pMulti && m_role[B].m_pMulti->getStartMove() )
            startMove = true;
      else if( m_role[A].m_pChangeWidget && m_role[A].m_pChangeWidget->getStartMove() )
            startMove = true;
      else if( m_role[B].m_pChangeWidget && m_role[B].m_pChangeWidget->getStartMove() )
            startMove = true;
      else if( m_role[A].m_pRole  && m_role[A].m_pRole->getStartMove() )
            startMove = true;
      else if( m_role[B].m_pRole  && m_role[B].m_pRole->getStartMove() )
            startMove = true;
      else if( m_pName && m_pName->getStartMove() )
            startMove = true;

      if (startMove) {
            return;
      }
      FloatingText *ft = floatingText(role);
      if (ft) {
            ft->setLinePositionRelatively( pos.x(), pos.y(),
                                     oldPosition.x(), oldPosition.y() );
      }
}

01942 void AssociationWidget::mousePressEvent(QMouseEvent * me) {
      m_nMovingPoint = -1;
      //make sure we should be here depending on the button
      if(me -> button() != RightButton && me->button() != LeftButton)
            return;
      QPoint mep = me->pos();
      // See if the user has clicked on a point to start moving the line segment
      // from that point
      checkPoints(mep);
      if( me -> state() != ShiftButton )
            m_pView -> clearSelected();
      setSelected( !m_bSelected );
}

01956 void AssociationWidget::mouseReleaseEvent(QMouseEvent * me) {
      if(me -> button() != RightButton && me->button() != LeftButton) {
            setSelected( false );
            return;
      }
      m_nMovingPoint = -1;
      if(me->button() != RightButton) {
            return;
      }
      // right button action:
      //work out the type of menu we want
      //work out if the association allows rolenames, multiplicity, etc
      //also must be within a certain distance to be a multiplicity menu
      QPoint p = me -> pos();
      ListPopupMenu::Menu_Type menuType = ListPopupMenu::mt_Undefined;
      int pos = m_LinePath.count() - 1;
      int DISTANCE = 40;//must be within this many pixels for it to be a multi menu
      QPoint lpStart = m_LinePath.getPoint(0);
      QPoint lpEnd = m_LinePath.getPoint(pos);
      int startXDiff = lpStart.x() - p.x();
      int startYDiff = lpStart.y() - p.y();
      int endXDiff = lpEnd.x() - p.x();
      int endYDiff = lpEnd.y() - p.y();
      float lengthMAP = sqrt( double(startXDiff * startXDiff + startYDiff * startYDiff) );
      float lengthMBP = sqrt( double(endXDiff * endXDiff + endYDiff * endYDiff) );
      Association_Type type = getAssocType();
      //allow multiplicity
      if( AssocRules::allowMultiplicity( type, getWidget(A) -> getBaseType() ) ) {
            if(lengthMAP < DISTANCE)
                  menuType =  ListPopupMenu::mt_MultiA;
            else if(lengthMBP < DISTANCE)
                  menuType = ListPopupMenu::mt_MultiB;
      }
      if( menuType == ListPopupMenu::mt_Undefined ) {
            if( type == at_Anchor )
                  menuType = ListPopupMenu::mt_Anchor;
            else if( AssocRules::allowRole( type ) )
                  menuType = ListPopupMenu::mt_RoleName;
            else
                  menuType = ListPopupMenu::mt_Association_Selected;
            if(type == at_Coll_Message) {
                  menuType = ListPopupMenu::mt_Collaboration_Message;
            }
      }
      m_pMenu = new ListPopupMenu(m_pView, menuType);
      m_pMenu->popup(me -> globalPos());
      connect(m_pMenu, SIGNAL(activated(int)), this, SLOT(slotMenuSelection(int)));
      setSelected();
}//end method mouseReleaseEvent

02006 bool AssociationWidget::showDialog() {
      AssocPropDlg dlg(static_cast<QWidget*>(m_pView), this );
      if (! dlg.exec())
            return false;
      QString name = getName();
      QString doc = getDoc();
      QString roleADoc = getRoleDoc(A), roleBDoc = getRoleDoc(B);
      QString rnA = getRoleName(A), rnB = getRoleName(B);
      QString ma = getMulti(A), mb = getMulti(B);
      Scope vA = getVisibility(A), vB = getVisibility(B);
      Changeability_Type cA = getChangeability(A), cB = getChangeability(B);
      //rules built into these functions to stop updating incorrect values
      setName(name);
      setRoleName(rnA, A);
      setRoleName(rnB, B);
      setDoc(doc);
      setRoleDoc(roleADoc, A);
      setRoleDoc(roleBDoc, B);
      setMulti(ma, A);
      setMulti(mb, B);
      setVisibility(vA, A);
      setVisibility(vB, B);
      setChangeability(cA, A);
      setChangeability(cB, B);
      m_pView -> showDocumentation( this, true );
      return true;
}

02034 void AssociationWidget::slotMenuSelection(int sel) {
      QString oldText, newText;
      QFont font;
      Uml::Association_Type atype = getAssocType();

      //if it's a collaboration message we now just use the code in floatingtextwidget
      //this means there's some redundant code below but that's better than duplicated code
      if (atype == at_Coll_Message && sel != ListPopupMenu::mt_Delete) {
            m_pName->slotMenuSelection(sel);
            return;
      }

      switch(sel) {
      case ListPopupMenu::mt_Properties:
            if(atype == at_Seq_Message || atype == at_Seq_Message_Self) {
                  // show op dlg for seq. diagram here
                  // don't worry about here, I don't think it can get here as
                  // line is widget on seq. diagram
                  // here just in case - remove later after testing
                  kdDebug() << "AssociationWidget::slotMenuSelection(mt_Properties): "
                          << "assoctype is " << atype << endl;
            } else {  //standard assoc dialog
                  m_pView -> updateDocumentation( false );
                  showDialog();
            }
            break;

      case ListPopupMenu::mt_Delete://for anchor
      case ListPopupMenu::mt_Delete_Association:
      case ListPopupMenu::mt_Delete_Message:
            m_pView->removeAssocInViewAndDoc(this);
            break;

      case ListPopupMenu::mt_Rename_MultiA:
            if(m_role[A].m_pMulti)
                  oldText = m_role[A].m_pMulti -> getText();
            else
                  oldText = "";
            newText = KInputDialog::getText(i18n("Multiplicity"), i18n("Enter multiplicity:") , oldText, 0, m_pView);
            if ( newText != oldText && FloatingText::isTextValid(newText) ) {
                  setMulti(newText, A);
            }
            break;


      case ListPopupMenu::mt_Rename_MultiB:
            if(m_role[B].m_pMulti)
                  oldText = m_role[B].m_pMulti -> getText();
            else
                  oldText = "";
            newText = KInputDialog::getText(i18n("Multiplicity"), i18n("Enter multiplicity:"), oldText, 0, m_pView);
            if ( newText != oldText && FloatingText::isTextValid(newText) ) {
                  setMulti(newText, B);
            }
            break;

      case ListPopupMenu::mt_Rename_Name:
            if(m_pName)
                  oldText = m_pName-> getText();
            else
                  oldText = "";

            newText = KInputDialog::getText(i18n("Association Name"), i18n("Enter association name:"), oldText, 0, m_pView);
            if ( newText != oldText && FloatingText::isTextValid(newText) )
                  setName(newText);

            break;

      case ListPopupMenu::mt_Rename_RoleAName:
            if(m_role[A].m_pRole)
                  oldText = m_role[A].m_pRole -> getText();
            else
                  oldText = "";
            newText = KInputDialog::getText(i18n("Role Name"), i18n("Enter role name:"), oldText, 0, m_pView);
            if ( newText != oldText && FloatingText::isTextValid(newText) ) {
                  setRoleName(newText, A);
            }
            break;

      case ListPopupMenu::mt_Rename_RoleBName:
            if(m_role[B].m_pRole)
                  oldText = m_role[B].m_pRole -> getText();
            else
                  oldText = "";
            newText = KInputDialog::getText(i18n("Role Name"), i18n("Enter role name:"), oldText, 0, m_pView);
            if ( newText != oldText && FloatingText::isTextValid(newText) ) {
                  setRoleName(newText, B);
            }
            break;

      case ListPopupMenu::mt_Change_Font:
            font = getFont();
            if( KFontDialog::getFont( font, false, m_pView ) )
                  setFont(font);
            break;

      case ListPopupMenu::mt_Change_Font_Selection:
            font = getFont();
            if( KFontDialog::getFont( font, false, m_pView ) )
                  m_pView -> selectionSetFont( font );
            break;

      case ListPopupMenu::mt_Cut:
            m_pView->setStartedCut();
            UMLApp::app()->slotEditCut();
            break;

      case ListPopupMenu::mt_Copy:
            UMLApp::app()->slotEditCopy();
            break;

      case ListPopupMenu::mt_Paste:
            UMLApp::app()->slotEditPaste();
            break;

      case ListPopupMenu::mt_Reset_Label_Positions:
            resetTextPositions();
            break;
      }//end switch
}


// utility method
02157 void AssociationWidget::setFont (QFont font) {
      if( m_pName) {
            m_pName->setFont( font );
      }
      if( m_role[A].m_pRole ) {
            m_role[A].m_pRole->setFont( font );
      }
      if( m_role[B].m_pRole ) {
            m_role[B].m_pRole->setFont( font );
      }
      if( m_role[A].m_pMulti ) {
            m_role[A].m_pMulti->setFont( font );
      }
      if( m_role[B].m_pMulti ) {
            m_role[B].m_pMulti->setFont( font );
      }
      if( m_role[A].m_pChangeWidget)
            m_role[A].m_pChangeWidget->setFont( font );
      if( m_role[B].m_pChangeWidget)
            m_role[B].m_pChangeWidget->setFont( font );
}

// find a general font for the association
02180 QFont AssociationWidget::getFont() const {
      QFont font;

      if( m_role[A].m_pRole )
            font = m_role[A].m_pRole -> getFont( );
      else    if( m_role[B].m_pRole)
            font = m_role[B].m_pRole -> getFont( );
      else    if( m_role[A].m_pMulti )
            font = m_role[A].m_pMulti -> getFont( );
      else    if( m_role[B].m_pMulti )
            font = m_role[B].m_pMulti -> getFont( );
      else    if( m_role[A].m_pChangeWidget)
            font = m_role[A].m_pChangeWidget-> getFont( );
      else    if( m_role[B].m_pChangeWidget)
            font = m_role[B].m_pChangeWidget-> getFont( );
      else    if( m_pName)
            font = m_pName-> getFont( );
      else
            font = m_role[A].m_pWidget -> getFont();

      return font;
}

02203 void AssociationWidget::checkPoints(QPoint p) {
      m_nMovingPoint = -1;
      //only check if more than the two endpoints
      int size = m_LinePath.count();
      if( size <= 2 )
            return;
      //check all points except the end points to se if we clicked on one of them
      QPoint tempPoint;
      int x, y;
      const int BOUNDRY = 4; //echeck for pixels around the point
      for(int i=1;i<size-1;i++) {
            tempPoint = m_LinePath.getPoint( i );
            x = tempPoint.x();
            y = tempPoint.y();
            if( x - BOUNDRY <= p.x() && x + BOUNDRY >= p.x() &&
                y - BOUNDRY <= p.y() && y + BOUNDRY >= p.y() ) {
                  m_nMovingPoint = i;
                  i = size; //no need to check the rest
            }//end if
      }//end for
}

02225 void AssociationWidget::mouseMoveEvent(QMouseEvent* me) {
      //make sure we have a moving point
      //I don't think there is another reason for being here
      if( m_nMovingPoint == -1 || me->state() != LeftButton) {
            return;
      }
      setSelected();
      //new position for point
      QPoint p = me->pos();
      QPoint oldp = m_LinePath.getPoint(m_nMovingPoint);

      if( m_pView -> getSnapToGrid() ) {
            int newX = m_pView->snappedX( p.x() );
            int newY = m_pView->snappedY( p.y() );
            p.setX(newX);
            p.setY(newY);
      }

      //move event called now
      QMoveEvent m(p, oldp);
      moveEvent(&m);
      m_pView->resizeCanvasToItems();
}

02249 AssociationWidget::Region AssociationWidget::getWidgetRegion(AssociationWidget * widget) const {
      if(widget -> getWidget(A) == m_role[A].m_pWidget)
            return m_role[A].m_WidgetRegion;
      if(widget -> getWidget(B) == m_role[B].m_pWidget)
            return m_role[B].m_WidgetRegion;
      return Error;
}

02257 int AssociationWidget::getRegionCount(AssociationWidget::Region region, Role_Type role) {
      if(region == Error)
            return 0;
      int widgetCount = 0;
      AssociationWidgetList list = m_pView -> getAssociationList();
      AssociationWidgetListIt assoc_it(list);
      AssociationWidget* assocwidget = 0;
      while((assocwidget = assoc_it.current())) {
            ++assoc_it;
            //don't count this association
            if (assocwidget == this)
                  continue;
            const WidgetRole& otherA = assocwidget->m_role[A];
            const WidgetRole& otherB = assocwidget->m_role[B];
            const UMLWidget *a = otherA.m_pWidget;
            const UMLWidget *b = otherB.m_pWidget;
            /*
            //don't count associations to self if both of their end points are on the same region
            //they are different and placement won't interfere with them
            if( a == b && otherA.m_WidgetRegion == otherB.m_WidgetRegion )
                  continue;
             */
            if (m_role[role].m_pWidget == a && region == otherA.m_WidgetRegion)
                  widgetCount++;
            else if (m_role[role].m_pWidget == b && region == otherB.m_WidgetRegion)
                  widgetCount++;
      }//end while
      return widgetCount;
}

02287 int AssociationWidget::findInterceptOnEdge(QRect rect,
                                 AssociationWidget::Region region,
                                 QPoint point)
{
      // The Qt coordinate system has (0,0) in the top left corner.
      // In order to go to the regular XY coordinate system with (0,0)
      // in the bottom left corner, we swap the X and Y axis.
      // That's why the following assignments look twisted.
      const int rectHalfWidth = rect.height() / 2;
      const int rectHalfHeight = rect.width() / 2;
      const int rectMidX = rect.y() + rectHalfWidth;
      const int rectMidY = rect.x() + rectHalfHeight;
      const int dX = rectMidX - point.y();
      const int dY = rectMidY - point.x();
      switch (region) {
            case West:
                  region = South;
                  break;
            case North:
                  region = West;
                  break;
            case East:
                  region = North;
                  break;
            case South:
                  region = East;
                  break;
            default:
                  break;
      }
      // Now we have regular coordinates with the point (0,0) in the
      // bottom left corner.
      if (region == North || region == South) {
            if (dX == 0)
                  return rectMidY;
                  // should be rectMidX, but we go back to Qt coord.sys.
            if (dY == 0) {
                  kdError() << "AssociationWidget::findInterceptOnEdge usage error: "
                          << "North/South (dY == 0)" << endl;
                  return -1;
            }
            const float m = (float)dY / (float)dX;
            float relativeX;
            if (region == North)
                  relativeX = (float)rectHalfHeight / m;
            else
                  relativeX = -(float)rectHalfHeight / m;
            return (rectMidY + (int)relativeX);
            // should be rectMidX, but we go back to Qt coord.sys.
      } else {
            if (dY == 0)
                  return rectMidX;
                  // should be rectMidY, but we go back to Qt coord.sys.
            if (dX == 0) {
                  kdError() << "AssociationWidget::findInterceptOnEdge usage error: "
                          << "East/West (dX == 0)" << endl;
                  return -1;
            }
            const float m = (float)dY / (float)dX;
            float relativeY = m * (float)rectHalfWidth;
            if (region == West)
                  relativeY = -relativeY;
            return (rectMidX + (int)relativeY);
            // should be rectMidY, but we go back to Qt coord.sys.
      }
}

02354 void AssociationWidget::insertIntoLists(int position, const AssociationWidget* assoc)
{
      bool did_insertion = false;
      for (int index = 0; index < m_positions_len; index++) {
            if (position < m_positions[index]) {
                  for (int moveback = m_positions_len; moveback > index; moveback--)
                        m_positions[moveback] = m_positions[moveback - 1];
                  m_positions[index] = position;
                  m_ordered.insert(index, assoc);
                  did_insertion = true;
                  break;
            }
      }
      if (! did_insertion) {
            m_positions[m_positions_len] = position;
            m_ordered.append(assoc);
      }
      m_positions_len++;
}

02374 void AssociationWidget::updateAssociations(int totalCount,
                                 AssociationWidget::Region region,
                                 Role_Type role)
{
      if( region == Error )
            return;
      AssociationWidgetList list = m_pView -> getAssociationList();
      AssociationWidgetListIt assoc_it(list);
      AssociationWidget* assocwidget = 0;
      UMLWidget *ownWidget = m_role[role].m_pWidget;
#ifdef DEBUG_ASSOCLINES
      Role_Type other = (role == A ? B : A);
      kdDebug() << "looking at assoc type " << getAssocType()
          << ";  own widget: " << ownWidget->getName() << ", other end: "
          << m_role[other].m_pWidget->getName() << endl;
#endif
      m_positions_len = 0;
      m_ordered.clear();
      // we order the AssociationWidget list by region and x/y value
      while ( (assocwidget = assoc_it.current()) ) {
            ++assoc_it;
            WidgetRole *roleA = &assocwidget->m_role[A];
            WidgetRole *roleB = &assocwidget->m_role[B];
            UMLWidget *wA = roleA->m_pWidget;
            UMLWidget *wB = roleB->m_pWidget;
            // Skip self associations.
            if (wA == wB)
                  continue;
            // Now we must find out with which end the assocwidget connects
            // to the input widget (ownWidget).
            bool inWidgetARegion = ( ownWidget == wA &&
                               region == roleA->m_WidgetRegion );
            bool inWidgetBRegion = ( ownWidget == wB &&
                               region == roleB->m_WidgetRegion);
            if ( !inWidgetARegion && !inWidgetBRegion )
                  continue;
            // Determine intercept position on the edge indicated by `region'.
            UMLWidget * otherWidget = (inWidgetARegion ? wB : wA);
            LinePath *linepath = assocwidget->getLinePath();
            QPoint refpoint;
            if (assocwidget->linePathStartsAt(otherWidget))
                  refpoint = linepath->getPoint(linepath->count() - 2);
            else
                  refpoint = linepath->getPoint(1);
            // The point is authoritative if we're called for the second time
            // (i.e. role==B) or it is a waypoint on the line path.
            bool pointIsAuthoritative = (role == B || linepath->count() > 2);
            if (! pointIsAuthoritative) {
                  // If the point is not authoritative then we use the other
                  // widget's center.
                  refpoint.setX(otherWidget->getX() + otherWidget->getWidth() / 2);
                  refpoint.setY(otherWidget->getY() + otherWidget->getHeight() / 2);
            }
            int intercept = findInterceptOnEdge(ownWidget->rect(), region, refpoint);
            if (intercept < 0) {
                  kdDebug() << "updateAssociations: error from findInterceptOnEdge for"
                          << " assocType=" << assocwidget->getAssocType()
                          << " ownWidget=" << ownWidget->getName()
                          << " otherWidget=" << otherWidget->getName() << endl;
                  continue;
            }
            insertIntoLists(intercept, assocwidget);
      } // while ( (assocwidget = assoc_it.current()) )

      // we now have an ordered list and we only have to call updateRegionLineCount
      int index = 1;
      for (assocwidget = m_ordered.first(); assocwidget; assocwidget = m_ordered.next()) {
            if (ownWidget == assocwidget->getWidget(A)) {
                  assocwidget->updateRegionLineCount(index++, totalCount, region, A);
            } else if (ownWidget == assocwidget->getWidget(B)) {
                  assocwidget->updateRegionLineCount(index++, totalCount, region, B);
            }
      } // for (assocwidget = ordered.first(); ...)
}

02449 void AssociationWidget::updateRegionLineCount(int index, int totalCount,
                                    AssociationWidget::Region region,
                                    Role_Type role) {
      if( region == Error )
            return;
      // If the association is to self and the line ends are on the same region then
      // use a different calculation.
      if (m_role[A].m_pWidget == m_role[B].m_pWidget &&
          m_role[A].m_WidgetRegion == m_role[B].m_WidgetRegion) {
            UMLWidget * pWidget = m_role[A].m_pWidget;
            int x = pWidget -> getX();
            int y = pWidget -> getY();
            int wh = pWidget -> height();
            int ww = pWidget -> width();
            int size = m_LinePath.count();
            // See if above widget ok to place assoc.
            switch( m_role[A].m_WidgetRegion ) {
            case North:
                  m_LinePath.setPoint( 0, QPoint( x + ( ww / 4 ), y ) );
                  m_LinePath.setPoint( size - 1, QPoint(x + ( ww * 3 / 4 ), y ) );
                  break;

            case South:
                  m_LinePath.setPoint( 0, QPoint( x + ( ww / 4 ), y + wh ) );
                  m_LinePath.setPoint( size - 1, QPoint( x + ( ww * 3 / 4 ), y + wh ) );
                  break;

            case East:
                  m_LinePath.setPoint( 0, QPoint( x + ww, y + ( wh / 4 ) ) );
                  m_LinePath.setPoint( size - 1, QPoint( x + ww, y + ( wh * 3 / 4 ) ) );
                  break;

            case West:
                  m_LinePath.setPoint( 0, QPoint( x, y + ( wh / 4 ) ) );
                  m_LinePath.setPoint( size - 1, QPoint( x, y + ( wh * 3 / 4 ) ) );
                  break;
            default:
                  break;
            }//end switch
            m_role[A].m_OldCorner.setX( x );
            m_role[A].m_OldCorner.setY( y );
            m_role[B].m_OldCorner.setX( x );
            m_role[B].m_OldCorner.setY( y );

            return;
      }

      WidgetRole& robj = m_role[role];
      UMLWidget * pWidget = robj.m_pWidget;

      // If a fork (ActivityWidget) for widget B then all associations should
      // meet in the middle.
      if (pWidget->getBaseType() == Uml::wt_Activity) {
            ActivityWidget *act = static_cast<ActivityWidget*>(pWidget);
            ActivityWidget::ActivityType atype = act->getActivityType();
            if (atype == ActivityWidget::Fork) {
                  totalCount = 2;
                  index = 1;
            } else if (atype == ActivityWidget::Initial ||
                     atype == ActivityWidget::End) {
                  region = Center;
                  totalCount = 2;
                  index = 1;
            }
      }
      robj.m_nIndex = index;
      robj.m_nTotalCount = totalCount;
      int x = pWidget->getX();
      int y = pWidget->getY();
      robj.m_OldCorner.setX(x);
      robj.m_OldCorner.setY(y);
      int ww = pWidget->getWidth();
      int wh = pWidget->getHeight();
      int ch = wh * index / totalCount;
      int cw = ww * index / totalCount;
      int snapX = m_pView->snappedX(x + cw);
      int snapY = m_pView->snappedY(y + ch);

      QPoint pt;

      switch(region) {
      case West:
            pt.setX(x);
            pt.setY(snapY);
            break;
      case North:
            pt.setX(snapX);
            pt.setY(y);
            break;
      case East:
            pt.setX(x + ww);
            pt.setY(snapY);
            break;
      case South:
            pt.setX(snapX);
            pt.setY(y + wh);
            break;
      case Center:
            pt.setX(x + ww / 2);
            pt.setY(y + wh / 2);
            break;
      default:
            break;
      }
      if (role == A)
            m_LinePath.setPoint( 0, pt );
      else
            m_LinePath.setPoint( m_LinePath.count() - 1, pt );
}

02559 void AssociationWidget::setSelected(bool _select /* = true */) {
      m_bSelected = _select;
      m_LinePath.setSelected( _select );
      if( m_pName)
            m_pName-> setSelected( _select );
      if( m_role[A].m_pRole )
            m_role[A].m_pRole -> setSelected( _select );
      if( m_role[B].m_pRole )
            m_role[B].m_pRole -> setSelected( _select );
      if( m_role[A].m_pMulti )
            m_role[A].m_pMulti -> setSelected( _select );
      if( m_role[B].m_pMulti )
            m_role[B].m_pMulti -> setSelected( _select );
      if( m_role[A].m_pChangeWidget)
            m_role[A].m_pChangeWidget-> setSelected( _select );
      if( m_role[B].m_pChangeWidget)
            m_role[B].m_pChangeWidget-> setSelected( _select );
      // Update the docwindow for this association.
      // This is done last because each of the above setSelected calls
      // overwrites the docwindow, but we want the main association doc
      // to win.
      if( _select ) {
            if( m_pView -> getSelectCount() == 0 )
                  m_pView -> showDocumentation( this, false );
      } else
            m_pView -> updateDocumentation( true );
}

02587 bool AssociationWidget::onAssociation(const QPoint & point) {
      return ( m_LinePath.onLinePath( point ) != -1 );
}

02591 void AssociationWidget::slotRemovePopupMenu()
{
      if(m_pMenu) {
            disconnect(m_pMenu, SIGNAL(activated(int)), this, SLOT(slotMenuSelection(int)));
            delete m_pMenu;
            m_pMenu = 0;
      }
}

02600 void AssociationWidget::slotClearAllSelected() {
      setSelected( false );
}

02604 void AssociationWidget::moveMidPointsBy( int x, int y ) {
      int pos = m_LinePath.count() - 1;
      for( int i=1 ; i < (int)pos ; i++ ) {
            QPoint p = m_LinePath.getPoint( i );
            int newX = p.x() + x;
            int newY = p.y() + y;
            newX = m_pView -> snappedX( newX );
            newY = m_pView -> snappedY( newY );
            p.setX( newX );
            p.setY( newY );
            m_LinePath.setPoint( i, p );
      }
}

02618 void AssociationWidget::moveEntireAssoc( int x, int y ) {
      //TODO: ADD SUPPORT FOR ASSOC. ON SEQ. DIAGRAMS WHEN NOTES BACK IN.
      moveMidPointsBy( x, y );
      calculateEndingPoints();
      calculateNameTextSegment();
      resetTextPositions();
}

02626 QRect AssociationWidget::getAssocLineRectangle()
{
      QRect rectangle;
      QPoint p;
      uint pen_width;

      /* we also want the end points connected to the other widget */
      int pos = m_LinePath.count();

      /* go through all points on the linepath */
      for( int i=0 ; i < (int) pos; i++ )
      {
            p = m_LinePath.getPoint( i );

            /* the first point is our starting point */
            if (i == 0) {
                  rectangle.setRect(p.x(), p.y(), 0, 0);
                  continue;
            }

            /* the lines have the width of the pen */
            pen_width = m_LinePath.getPen().width();
            if (pen_width == 0)
                  pen_width = 1; // width must be at least 1

            if (p.x() < rectangle.x())
                  rectangle.setX(p.x());
            if (p.y() < rectangle.y())
                  rectangle.setY(p.y());
            if (p.x() > rectangle.x() + rectangle.width()) {
                  int newX = p.x() - rectangle.x() + pen_width;
                  rectangle.setWidth(abs(newX));
            }
            if (p.y() > rectangle.y() + rectangle.height()) {
                  int newY = p.y() - rectangle.y() + pen_width;
                  rectangle.setHeight(abs(newY));
            }
      }
      return rectangle;
}


02668 void AssociationWidget::init (UMLView *view)
{
      m_pView = view;  // pointer to parent viewwidget object
      m_Type = wt_Association;

      // pointers to floating text widgets objects owned by this association
      m_pName = 0;
      m_role[A].m_pChangeWidget = 0;
      m_role[B].m_pChangeWidget = 0;
      m_role[A].m_pMulti = 0;
      m_role[B].m_pMulti = 0;
      m_role[A].m_pRole = 0;
      m_role[B].m_pRole = 0;
      m_role[A].m_pWidget = 0;
      m_role[B].m_pWidget = 0;

      // associationwidget attributes
      m_role[A].m_WidgetRegion = Error;
      m_role[B].m_WidgetRegion = Error;
      m_bActivated = false;
      m_unNameLineSegment = 0;
      m_pMenu = 0;
      m_bSelected = false;
      m_nMovingPoint = -1;

      // Initialize local members.
      // These are only used if we don't have a UMLAssociation attached.
      m_role[A].m_Visibility = Public;
      m_role[B].m_Visibility = Public;
      m_role[A].m_Changeability = chg_Changeable;
      m_role[B].m_Changeability = chg_Changeable;
      m_AssocType = Uml::at_Association;

      m_LinePath.setAssociation( this );

      connect(m_pView, SIGNAL(sigRemovePopupMenu()), this, SLOT(slotRemovePopupMenu()));
      connect(m_pView, SIGNAL( sigClearAllSelected() ), this, SLOT( slotClearAllSelected() ) );
}

02707 void AssociationWidget::resetTextPositions() {
      if (m_role[A].m_pMulti) {
            setTextPosition( tr_MultiA, calculateTextPosition(tr_MultiA) );
      }
      if (m_role[B].m_pMulti) {
            setTextPosition( tr_MultiB, calculateTextPosition(tr_MultiB) );
      }
      if (m_role[A].m_pChangeWidget) {
            setTextPosition( tr_ChangeA, calculateTextPosition(tr_ChangeA) );
      }
      if (m_role[B].m_pChangeWidget) {
            setTextPosition( tr_ChangeB, calculateTextPosition(tr_ChangeB) );
      }
      if (m_pName) {
            setTextPosition( tr_Name, calculateTextPosition(tr_Name) );
      }
      if (m_role[A].m_pRole) {
            setTextPosition( tr_RoleAName, calculateTextPosition(tr_RoleAName) );
      }
      if (m_role[B].m_pRole) {
            setTextPosition( tr_RoleBName, calculateTextPosition(tr_RoleBName) );
      }
}

02731 void AssociationWidget::setIndex(int index, Role_Type role) {
      m_role[role].m_nIndex = index;
}

02735 int AssociationWidget::getIndex(Role_Type role) const {
      return m_role[role].m_nIndex;
}

02739 void AssociationWidget::setTotalCount(int count, Role_Type role) {
      m_role[role].m_nTotalCount = count;
}

02743 int AssociationWidget::getTotalCount(Role_Type role) const {
      return  m_role[role].m_nTotalCount;
}

02747 UMLClassifier *AssociationWidget::getOperationOwner(FloatingText *) {
      Association_Type atype = getAssocType();
      Role_Type role = (atype == at_Coll_Message ? B : A);
      UMLObject *o = getWidget(role)->getUMLObject();
      if (o == NULL)
            return NULL;
      UMLClassifier *c = dynamic_cast<UMLClassifier*>(o);
      if (c == NULL)
            kdError() << "AssociationWidget::getOperationOwner: "
                    << "getWidget(" << role << ") is not a classifier"
                    << endl;
      return c;
}

02761 void AssociationWidget::setSeqNumAndOp(QString seqNum, QString op) {
      setName(op);
      setMulti(seqNum, A);
}

02766 UMLClassifier *AssociationWidget::getSeqNumAndOp(FloatingText *,
                                     QString& seqNum, QString& op) {
      seqNum = getMulti(A);
      op = getName();
      UMLObject *o = getWidget(B)->getUMLObject();
      UMLClassifier *c = dynamic_cast<UMLClassifier*>(o);
      return c;
}

02775 void AssociationWidget::setOperationText(FloatingText *, QString opText) {
      setName(opText);
}

02779 void AssociationWidget::cleanupBeforeFTsetLink(FloatingText *ft) {
      ft->removeAssoc(this);  // FIXME: This is horrible.
}

02783 void AssociationWidget::setupAfterFTsetLink(FloatingText *ft) {
      ft->addAssoc(this);    // FIXME: This is horrible.
}

02787 void AssociationWidget::setWidget( UMLWidget* widget, Role_Type role) {
      m_role[role].m_pWidget = widget;
      if (widget) {
            m_role[role].m_pWidget->addAssoc(this);
            if (m_pObject)
                  getAssociation()->setObject(widget->getUMLObject(), role);
      }
}

02796 UMLWidget* AssociationWidget::findWidget( int id, const UMLWidgetList& widgets,
                                      const MessageWidgetList* pMessages /* = NULL */)
{
      UMLWidgetListIt it( widgets );
      UMLWidget * obj = NULL;
      while ( (obj = it.current()) != NULL ) {
            ++it;
            if (obj->getBaseType() == wt_Object) {
                  if (static_cast<ObjectWidget *>(obj)->getLocalID() == id)
                        return obj;
            } else if (obj->getID() == id) {
                  return obj;
            }
      }

      if (pMessages == NULL)
            return NULL;

      MessageWidgetListIt mit( *pMessages );
      while ( (obj = (UMLWidget*)mit.current()) != NULL ) {
            ++mit;
            if( obj -> getID() == id )
                  return obj;
      }
      return NULL;
}

02823 void AssociationWidget::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) {
      QDomElement assocElement = qDoc.createElement( "assocwidget" );

      if (m_pObject) {
            assocElement.setAttribute( "xmi.id", getAssociation()->getID() );
      } else {
            assocElement.setAttribute( "type", m_AssocType );
            assocElement.setAttribute( "visibilityA", m_role[A].m_Visibility);
            assocElement.setAttribute( "visibilityB", m_role[B].m_Visibility);
            assocElement.setAttribute( "changeabilityA", m_role[A].m_Changeability);
            assocElement.setAttribute( "changeabilityB", m_role[B].m_Changeability);
            assocElement.setAttribute( "roleAdoc", m_role[A].m_RoleDoc);
            assocElement.setAttribute( "roleBdoc", m_role[B].m_RoleDoc);
            assocElement.setAttribute( "documentation", m_Doc );
      }
      assocElement.setAttribute( "widgetaid", getWidgetID(A) );
      assocElement.setAttribute( "widgetbid", getWidgetID(B) );
      assocElement.setAttribute( "indexa", m_role[A].m_nIndex );
      assocElement.setAttribute( "indexb", m_role[B].m_nIndex );
      assocElement.setAttribute( "totalcounta", m_role[A].m_nTotalCount );
      assocElement.setAttribute( "totalcountb", m_role[B].m_nTotalCount );
      m_LinePath.saveToXMI( qDoc, assocElement );

      if( m_pName )
            m_pName -> saveToXMI( qDoc, assocElement );

      if( m_role[A].m_pMulti )
            m_role[A].m_pMulti -> saveToXMI( qDoc, assocElement );

      if( m_role[B].m_pMulti )
            m_role[B].m_pMulti -> saveToXMI( qDoc, assocElement );

      if( m_role[A].m_pRole )
            m_role[A].m_pRole -> saveToXMI( qDoc, assocElement );

      if( m_role[B].m_pRole )
            m_role[B].m_pRole -> saveToXMI( qDoc, assocElement );

      if( m_role[A].m_pChangeWidget )
            m_role[A].m_pChangeWidget -> saveToXMI( qDoc, assocElement );

      if( m_role[B].m_pChangeWidget )
            m_role[B].m_pChangeWidget -> saveToXMI( qDoc, assocElement );

      qElement.appendChild( assocElement );
}

02870 bool AssociationWidget::loadFromXMI( QDomElement & qElement,
                             const UMLWidgetList& widgets,
                             const MessageWidgetList* pMessages )
{

      // load child widgets first
      QString widgetaid = qElement.attribute( "widgetaid", "-1" );
      QString widgetbid = qElement.attribute( "widgetbid", "-1" );
      int aId = widgetaid.toInt();
      int bId = widgetbid.toInt();
      UMLWidget *pWidgetA = findWidget( aId, widgets, pMessages );
      if (!pWidgetA) {
            kdError() << "AssociationWidget::loadFromXMI(): "
                    << "cannot find widget for roleA id " << aId << endl;
            return false;
      }
      UMLWidget *pWidgetB = findWidget( bId, widgets, pMessages );
      if (!pWidgetB) {
            kdError() << "AssociationWidget::loadFromXMI(): "
                    << "cannot find widget for roleB id " << bId << endl;
            return false;
      }
      setWidget(pWidgetA, A);
      setWidget(pWidgetB, B);

      QString id = qElement.attribute( "xmi.id", "-1" );
      bool oldStyleLoad = false;
      int nId = id.toInt();
      if (nId == -1) {

            // xmi.id not present, ergo either a pure widget association,
            // or old (pre-1.2) style:
            // Everything is loaded from the AssociationWidget.
            // UMLAssociation may or may not be saved - if it is, it's a dummy.
            // Create the UMLAssociation if both roles are UML objects;
            // else load the info locally.

            QString type = qElement.attribute( "type", "-1" );
            Uml::Association_Type aType = (Uml::Association_Type) type.toInt();
            if (UMLAssociation::assocTypeHasUMLRepresentation(aType)) {
                  // lack of an association in our widget AND presence of
                  // both uml objects for each role clearly identifies this
                  // as reading in an old-school file. Note it as such, and
                  // create, and add, the UMLAssociation for this widget.
                        // Remove this special code when backwards compatibility
                  // with older files isn't important anymore. -b.t.
                  UMLObject* umlRoleA = pWidgetA->getUMLObject();
                  UMLObject* umlRoleB = pWidgetB->getUMLObject();
                  if (!m_pObject && umlRoleA && umlRoleB)
                  {
                        oldStyleLoad = true; // flag for further special config below
                        if (aType == at_Aggregation || aType == at_Composition) {
                              kdWarning()<<" Old Style save file? swapping roles on association widget"<<this<<endl;
                              // We have to swap the A and B widgets to compensate
                              // for the long standing bug in LinePath of drawing
                              // the diamond at the wrong end which was fixed
                              // just before the 1.2 release.
                              // The logic here is that the user has understood
                              // that the diamond belongs at the SOURCE end of the
                              // the association (i.e. at the container, not at the
                              // contained), and has compensated for this anomaly
                              // by drawing the aggregations/compositions from
                              // target to source.
                              UMLWidget *tmpWidget = pWidgetA;
                              pWidgetA = pWidgetB;
                              pWidgetB = tmpWidget;
                              setWidget(pWidgetA, A);
                              setWidget(pWidgetB, B);
                              umlRoleA = pWidgetA->getUMLObject();
                              umlRoleB = pWidgetB->getUMLObject();
                        }

                        setUMLAssociation(UMLApp::app()->getDocument()->createUMLAssociation(umlRoleA, umlRoleB, aType));
                  }
            }

            setDoc( qElement.attribute("documentation", "") );
            setRoleDoc( qElement.attribute("roleAdoc", ""), A );
            setRoleDoc( qElement.attribute("roleBdoc", ""), B );

            setAssocType(aType);

            // visibilty defaults to Public if it cant set it here..
            QString visibilityA = qElement.attribute( "visibilityA", "0");
            if (visibilityA.toInt() > 0)
                  setVisibility( (Scope)visibilityA.toInt(), A);

            QString visibilityB = qElement.attribute( "visibilityB", "0");
            if (visibilityB.toInt() > 0)
                  setVisibility( (Scope)visibilityB.toInt(), B);

            // Changeability defaults to "Changeable" if it cant set it here..
            QString changeabilityA = qElement.attribute( "changeabilityA", "0");
            if (changeabilityA.toInt() > 0)
                  setChangeability( (Changeability_Type)changeabilityA.toInt(), A);

            QString changeabilityB = qElement.attribute( "changeabilityB", "0");
            if (changeabilityB.toInt() > 0)
                  setChangeability( (Changeability_Type)changeabilityB.toInt(), B);

      } else {

            // we should disconnect any prior association (can this happen??)
            if (m_pObject)
            {
                  UMLAssociation *umla = getAssociation();
                  umla->disconnect(this);
                  umla->nrof_parent_widgets--;
            }

            // New style: The xmi.id is a reference to the UMLAssociation.
            UMLDoc* umldoc = UMLApp::app()->getDocument();
            UMLAssociation * myAssoc = (UMLAssociation*)umldoc->findUMLObject(nId);
            if (myAssoc == NULL) {
                  kdError() << " AssociationWidget cannot find UML:Association " << nId << " for loadFromXMI"<< endl;
                  return false;
            } else
            {
                  setUMLAssociation(myAssoc);
            }

            m_LinePath.setAssocType( getAssociation()->getAssocType() );

      }

      QString indexa = qElement.attribute( "indexa", "0" );
      QString indexb = qElement.attribute( "indexb", "0" );
      QString totalcounta = qElement.attribute( "totalcounta", "0" );
      QString totalcountb = qElement.attribute( "totalcountb", "0" );
      m_role[A].m_nIndex = indexa.toInt();
      m_role[B].m_nIndex = indexb.toInt();
      m_role[A].m_nTotalCount = totalcounta.toInt();
      m_role[B].m_nTotalCount = totalcountb.toInt();

      //now load child elements
      QDomNode node = qElement.firstChild();
      QDomElement element = node.toElement();
      while( !element.isNull() ) {
            QString tag = element.tagName();
            if( tag == "linepath" ) {
                  if( !m_LinePath.loadFromXMI( element ) )
                        return false;
                  else {
                        // set up 'old' corner from first point in line
                        // as IF this ISNT done, then the subsequent call to
                        // widgetMoved will inadvertantly think we have made a
                        // big move in the position of the association when we haven't.
                        QPoint p = m_LinePath.getPoint(0);
                        m_role[A].m_OldCorner.setX(p.x());
                        m_role[A].m_OldCorner.setY(p.y());
                  }
            } else if (tag == "floatingtext" ||
                     tag == "UML:FloatingTextWidget") {  // for bkwd compatibility
                  QString r = element.attribute( "role", "-1");
                  if( r == "-1" )
                        return false;
                  Uml::Text_Role role = (Uml::Text_Role)r.toInt();
                  FloatingText *ft = new FloatingText(m_pView, role, "", 0);
                  if( ! ft->loadFromXMI(element) ) {
                        // Most likely cause: The FloatingText is empty.
                        delete ft;
                        node = element.nextSibling();
                        element = node.toElement();
                        continue;
                  }
                  // always need this
                  ft->setLink(this);

                  switch( role ) {
                        case Uml::tr_MultiA:
                              m_role[A].m_pMulti = ft;
                              if(oldStyleLoad)
                                    setMulti(m_role[A].m_pMulti->getText(), A);
                              break;

                        case Uml::tr_MultiB:
                              m_role[B].m_pMulti = ft;
                              if(oldStyleLoad)
                                    setMulti(m_role[B].m_pMulti->getText(), B);
                              break;

                        case Uml::tr_ChangeA:
                              m_role[A].m_pChangeWidget = ft;
                              break;

                        case Uml::tr_ChangeB:
                              m_role[B].m_pChangeWidget = ft;
                              break;

                        case Uml::tr_Name:
                              m_pName = ft;
                              if(oldStyleLoad)
                                    setName(m_pName->getText());
                              break;

                        case Uml::tr_Coll_Message:
                              m_pName = ft;
                              ft->setLink(this);
                              ft->setActivated();
                              if(FloatingText::isTextValid(ft->getText()))
                                    ft -> show();
                              else
                                    ft -> hide();
                              break;

                        case Uml::tr_RoleAName:
                              m_role[A].m_pRole = ft;
                              setRoleName( ft->getText(), A );
                              break;
                        case Uml::tr_RoleBName:
                              m_role[B].m_pRole = ft;
                              setRoleName( ft->getText(), B );
                              break;
                        default:
                              kdDebug() << "AssociationWidget::loadFromXMI(): "
                                    << "unexpected FloatingText (textrole "
                                    << role << ")" << endl;
                              delete ft;
                              break;
                  }
            }
            node = element.nextSibling();
            element = node.toElement();
      }

      return true;
}

03098 bool AssociationWidget::loadFromXMI( QDomElement & qElement ) {
      const MessageWidgetList& messages = m_pView->getMessageList();
      return loadFromXMI( qElement, m_pView->getWidgetList(), &messages );
}

#include "associationwidget.moc"

Generated by  Doxygen 1.6.0   Back to index