Coverage Report - org.argouml.uml.diagram.ui.FigEdgeModelElement
 
Classes in this File Line Coverage Branch Coverage Complexity
FigEdgeModelElement
0%
0/549
0%
0/286
2.794
FigEdgeModelElement$1
0%
0/6
N/A
2.794
FigEdgeModelElement$2
0%
0/7
0%
0/2
2.794
 
 1  
 /* $Id: FigEdgeModelElement.java 18859 2010-11-29 21:37:03Z mvw $
 2  
  *****************************************************************************
 3  
  * Copyright (c) 2009-2010 Contributors - see below
 4  
  * All rights reserved. This program and the accompanying materials
 5  
  * are made available under the terms of the Eclipse Public License v1.0
 6  
  * which accompanies this distribution, and is available at
 7  
  * http://www.eclipse.org/legal/epl-v10.html
 8  
  *
 9  
  * Contributors:
 10  
  *    Thomas Neustupny
 11  
  *    Bob Tarling
 12  
  *    Michiel van der Wulp
 13  
  *****************************************************************************
 14  
  *
 15  
  * Some portions of this file was previously release using the BSD License:
 16  
  */
 17  
 
 18  
 // Copyright (c) 1996-2009 The Regents of the University of California. All
 19  
 // Rights Reserved. Permission to use, copy, modify, and distribute this
 20  
 // software and its documentation without fee, and without a written
 21  
 // agreement is hereby granted, provided that the above copyright notice
 22  
 // and this paragraph appear in all copies.  This software program and
 23  
 // documentation are copyrighted by The Regents of the University of
 24  
 // California. The software program and documentation are supplied "AS
 25  
 // IS", without any accompanying services from The Regents. The Regents
 26  
 // does not warrant that the operation of the program will be
 27  
 // uninterrupted or error-free. The end-user understands that the program
 28  
 // was developed for research purposes and is advised not to rely
 29  
 // exclusively on the program for any reason.  IN NO EVENT SHALL THE
 30  
 // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
 31  
 // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
 32  
 // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 33  
 // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 34  
 // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
 35  
 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 36  
 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 37  
 // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 38  
 // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
 39  
 // UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 40  
 
 41  
 package org.argouml.uml.diagram.ui;
 42  
 
 43  
 import java.awt.Color;
 44  
 import java.awt.Font;
 45  
 import java.awt.Graphics;
 46  
 import java.awt.Point;
 47  
 import java.awt.Rectangle;
 48  
 import java.awt.event.KeyEvent;
 49  
 import java.awt.event.KeyListener;
 50  
 import java.awt.event.MouseEvent;
 51  
 import java.awt.event.MouseListener;
 52  
 import java.beans.PropertyChangeEvent;
 53  
 import java.beans.PropertyChangeListener;
 54  
 import java.beans.VetoableChangeListener;
 55  
 import java.util.ArrayList;
 56  
 import java.util.Collection;
 57  
 import java.util.HashSet;
 58  
 import java.util.Iterator;
 59  
 import java.util.List;
 60  
 import java.util.Set;
 61  
 import java.util.Vector;
 62  
 
 63  
 import javax.swing.Action;
 64  
 import javax.swing.Icon;
 65  
 import javax.swing.JMenu;
 66  
 import javax.swing.JSeparator;
 67  
 import javax.swing.SwingUtilities;
 68  
 
 69  
 import org.apache.log4j.Logger;
 70  
 import org.argouml.application.events.ArgoDiagramAppearanceEvent;
 71  
 import org.argouml.application.events.ArgoDiagramAppearanceEventListener;
 72  
 import org.argouml.application.events.ArgoEventPump;
 73  
 import org.argouml.application.events.ArgoEventTypes;
 74  
 import org.argouml.application.events.ArgoHelpEvent;
 75  
 import org.argouml.application.events.ArgoNotationEvent;
 76  
 import org.argouml.application.events.ArgoNotationEventListener;
 77  
 import org.argouml.cognitive.Designer;
 78  
 import org.argouml.cognitive.Highlightable;
 79  
 import org.argouml.cognitive.ToDoItem;
 80  
 import org.argouml.cognitive.ToDoList;
 81  
 import org.argouml.cognitive.ui.ActionGoToCritique;
 82  
 import org.argouml.i18n.Translator;
 83  
 import org.argouml.kernel.DelayedChangeNotify;
 84  
 import org.argouml.kernel.DelayedVChangeListener;
 85  
 import org.argouml.kernel.Owned;
 86  
 import org.argouml.kernel.Project;
 87  
 import org.argouml.model.AddAssociationEvent;
 88  
 import org.argouml.model.AssociationChangeEvent;
 89  
 import org.argouml.model.AttributeChangeEvent;
 90  
 import org.argouml.model.DeleteInstanceEvent;
 91  
 import org.argouml.model.DiElement;
 92  
 import org.argouml.model.InvalidElementException;
 93  
 import org.argouml.model.Model;
 94  
 import org.argouml.model.RemoveAssociationEvent;
 95  
 import org.argouml.model.UmlChangeEvent;
 96  
 import org.argouml.notation.Notation;
 97  
 import org.argouml.notation.NotationName;
 98  
 import org.argouml.notation.NotationProvider;
 99  
 import org.argouml.notation.NotationProviderFactory2;
 100  
 import org.argouml.notation.NotationRenderer;
 101  
 import org.argouml.notation.NotationSettings;
 102  
 import org.argouml.ui.ArgoJMenu;
 103  
 import org.argouml.ui.Clarifier;
 104  
 import org.argouml.ui.ContextActionFactoryManager;
 105  
 import org.argouml.ui.ProjectActions;
 106  
 import org.argouml.ui.targetmanager.TargetManager;
 107  
 import org.argouml.uml.StereotypeUtility;
 108  
 import org.argouml.uml.diagram.DiagramElement;
 109  
 import org.argouml.uml.diagram.DiagramSettings;
 110  
 import org.argouml.uml.ui.ActionDeleteModelElements;
 111  
 import org.argouml.util.IItemUID;
 112  
 import org.argouml.util.ItemUID;
 113  
 import org.tigris.gef.base.Geometry;
 114  
 import org.tigris.gef.base.Globals;
 115  
 import org.tigris.gef.base.Layer;
 116  
 import org.tigris.gef.base.Selection;
 117  
 import org.tigris.gef.persistence.pgml.PgmlUtility;
 118  
 import org.tigris.gef.presentation.Fig;
 119  
 import org.tigris.gef.presentation.FigCircle;
 120  
 import org.tigris.gef.presentation.FigEdge;
 121  
 import org.tigris.gef.presentation.FigEdgePoly;
 122  
 import org.tigris.gef.presentation.FigGroup;
 123  
 import org.tigris.gef.presentation.FigNode;
 124  
 import org.tigris.gef.presentation.FigPoly;
 125  
 import org.tigris.gef.presentation.FigText;
 126  
 
 127  
 /**
 128  
  * Abstract class to display diagram lines (edges) for UML ModelElements that
 129  
  * look like lines.
 130  
  * This Fig is prepared to show a (possibly editable) name,
 131  
  * and/or multiple stereotypes.
 132  
  * <p>
 133  
  * NOTE: This will drop the ArgoNotationEventListener and 
 134  
  * ArgoDiagramAppearanceEventListener
 135  
  * interfaces in the next release.  The corresponding methods have been marked
 136  
  * as deprecated.
 137  
  */
 138  0
 public abstract class FigEdgeModelElement
 139  
     extends FigEdgePoly
 140  
     implements
 141  
         VetoableChangeListener,
 142  
         DelayedVChangeListener,
 143  
         MouseListener,
 144  
         KeyListener,
 145  
         PropertyChangeListener,
 146  
         ArgoNotationEventListener,
 147  
         NotationRenderer,
 148  
         ArgoDiagramAppearanceEventListener,
 149  
         Highlightable,
 150  
         IItemUID,
 151  
         ArgoFig,
 152  
         Clarifiable,
 153  
         DiagramElement,
 154  
         Owned {
 155  
 
 156  0
     private static final Logger LOG =
 157  
         Logger.getLogger(FigEdgeModelElement.class);
 158  
 
 159  0
     private DiElement diElement = null;
 160  
 
 161  
     /**
 162  
      * Set the removeFromDiagram to false if this edge may not
 163  
      * be removed from the diagram.
 164  
      */
 165  0
     private boolean removeFromDiagram = true;
 166  
 
 167  
     /**
 168  
      * Offset from the end of the set of popup actions at which new items
 169  
      * should be inserted by concrete figures.
 170  
     **/
 171  
     private static int popupAddOffset;
 172  
 
 173  
     private NotationProvider notationProviderName;
 174  
 
 175  
     /**
 176  
      * The Fig that displays the name of this model element.
 177  
      * Use getNameFig(), no setter should be required.
 178  
      */
 179  
     private FigText nameFig;
 180  
 
 181  
     /**
 182  
      * Use getStereotypeFig(), no setter should be required.
 183  
      */
 184  
     private FigStereotypesGroup stereotypeFig;
 185  
 
 186  
     private FigEdgePort edgePort;
 187  
 
 188  
     private ItemUID itemUid;
 189  
 
 190  
     /*
 191  
      * List of model element listeners we've registered.
 192  
      */
 193  0
     private Set<Object[]> listeners = new HashSet<Object[]>();
 194  
 
 195  
     private DiagramSettings settings;
 196  
     
 197  
     /**
 198  
      * Construct a new FigEdge. This method creates the name element that holds
 199  
      * the name of the model element and adds itself as a listener. Also a
 200  
      * stereotype is constructed.
 201  
      * <p>
 202  
      * This constructor is only intended for use by concrete subclasses.
 203  
      * 
 204  
      * @param element owning uml element
 205  
      * @param renderSettings rendering settings
 206  
      */
 207  
     protected FigEdgeModelElement(Object element, 
 208  
             DiagramSettings renderSettings) {
 209  0
         super();
 210  
         // TODO: We don't have any settings that can change per-fig currently
 211  
         // so we can just use the default settings;
 212  
 //        settings = new DiagramSettings(renderSettings);
 213  0
         settings = renderSettings;
 214  
         
 215  
         // TODO: It doesn't matter what these get set to because GEF can't 
 216  
         // draw anything except 1 pixel wide lines
 217  0
         super.setLineColor(LINE_COLOR);
 218  0
         super.setLineWidth(LINE_WIDTH);
 219  0
         getFig().setLineColor(LINE_COLOR);
 220  0
         getFig().setLineWidth(LINE_WIDTH);
 221  
         
 222  0
         nameFig = new FigNameWithAbstract(element, 
 223  
                 new Rectangle(X0, Y0 + 20, 90, 20), 
 224  
                 renderSettings, false);
 225  0
         stereotypeFig = new FigStereotypesGroup(element, 
 226  
                 new Rectangle(X0, Y0, 90, 15),
 227  
                 settings);
 228  
       
 229  0
         initFigs();
 230  0
         initOwner(element);
 231  0
     }
 232  
 
 233  
 
 234  
 
 235  
     private void initFigs() {
 236  0
         nameFig.setTextFilled(false);
 237  0
         setBetweenNearestPoints(true);
 238  0
     }
 239  
     
 240  
     
 241  
     private void initOwner(Object element) {
 242  0
         if (element != null) {
 243  0
             if (!Model.getFacade().isAUMLElement(element)) {
 244  0
                 throw new IllegalArgumentException(
 245  
                         "The owner must be a model element - got a "
 246  
                         + element.getClass().getName());
 247  
             }
 248  0
             super.setOwner(element);  
 249  0
             if (edgePort != null) {
 250  0
                 edgePort.setOwner(getOwner());
 251  
             }
 252  0
             NotationName nn = Notation.findNotation(
 253  
                     settings.getNotationSettings().getNotationLanguage());
 254  0
             notationProviderName =
 255  
                 NotationProviderFactory2.getInstance().getNotationProvider(
 256  
                         getNotationProviderType(), element, this, nn);
 257  
 
 258  0
             addElementListener(element, "remove");
 259  
         }
 260  0
     }
 261  
 
 262  
     /**
 263  
      * Create a FigCommentPort if needed
 264  
      */
 265  
     public void makeEdgePort() {
 266  0
         if (edgePort == null) {
 267  0
             edgePort = new FigEdgePort(getOwner(), new Rectangle(), 
 268  
                     getSettings());
 269  0
             edgePort.setVisible(false);
 270  0
             addPathItem(edgePort,
 271  
                     new PathItemPlacement(this, edgePort, 50, 0));
 272  
         }
 273  0
     }
 274  
 
 275  
     /**
 276  
      * @return the FigCommentPort
 277  
      */
 278  
     public FigEdgePort getEdgePort() {
 279  0
         return edgePort;
 280  
     }
 281  
 
 282  
     ////////////////////////////////////////////////////////////////
 283  
     // accessors
 284  
 
 285  
     /**
 286  
      * Setter for the UID
 287  
      * @param newId the new UID
 288  
      */
 289  
     public void setItemUID(ItemUID newId) {
 290  0
         itemUid = newId;
 291  0
     }
 292  
 
 293  
     /**
 294  
      * Getter for the UID
 295  
      * @return the UID
 296  
      */
 297  
     public ItemUID getItemUID() {
 298  0
         return itemUid;
 299  
     }
 300  
 
 301  
     /*
 302  
      * @see org.tigris.gef.presentation.Fig#getTipString(java.awt.event.MouseEvent)
 303  
      */
 304  
     @Override
 305  
     public String getTipString(MouseEvent me) {
 306  0
         ToDoItem item = hitClarifier(me.getX(), me.getY());
 307  0
         String tip = "";
 308  0
         if (item != null
 309  
             && Globals.curEditor().getSelectionManager().containsFig(this)) {
 310  0
             tip = item.getHeadline();
 311  0
         } else if (getOwner() != null) {
 312  
             try {
 313  0
                 tip = Model.getFacade().getTipString(getOwner());
 314  0
             } catch (InvalidElementException e) {
 315  
                 // We moused over an object just as it was deleted
 316  
                 // transient condition - doesn't require I18N
 317  0
                 LOG.warn("A deleted element still exists on the diagram");
 318  0
                 return Translator.localize("misc.name.deleted");
 319  0
             }
 320  
         } else {
 321  0
             tip = toString();
 322  
         }
 323  
 
 324  0
         if (tip != null && tip.length() > 0 && !tip.endsWith(" ")) {
 325  0
             tip += " ";
 326  
         }
 327  0
         return tip;
 328  
     }
 329  
 
 330  
     /**
 331  
      * @param me the MouseEvent that triggered the popup menu request
 332  
      * @return a Vector containing a combination of these 4 types: Action,
 333  
      *         JMenu, JMenuItem, JSeparator.
 334  
      */
 335  
     @Override
 336  
     public Vector getPopUpActions(MouseEvent me) {
 337  0
         ActionList popUpActions =
 338  
             new ActionList(super.getPopUpActions(me), isReadOnly());
 339  
         
 340  
         // Added this part to load the extra menu content
 341  0
         final List<Action> modulesActions =
 342  
             ContextActionFactoryManager.getContextPopupActions();
 343  
         
 344  0
         for (Action a : modulesActions) {
 345  0
             if (a instanceof List) {
 346  0
                 JMenu m = new JMenu((Action) a);
 347  0
                 popUpActions.add(m);
 348  0
                 for (Action subAction : (List<Action>) a) {
 349  0
                     m.add(subAction);
 350  
                 }
 351  0
             } else {
 352  0
                 popUpActions.add(a);
 353  
             }
 354  
         }
 355  
         
 356  
         // popupAddOffset should be equal to the number of items added here:
 357  0
         popUpActions.add(new JSeparator());
 358  0
         popupAddOffset = 1;
 359  0
         if (removeFromDiagram) {
 360  0
             popUpActions.add(
 361  
                     ProjectActions.getInstance().getRemoveFromDiagramAction());
 362  0
             popupAddOffset++;
 363  
         }
 364  0
         popUpActions.add(new ActionDeleteModelElements());
 365  0
         popupAddOffset++;
 366  
 
 367  0
         if (TargetManager.getInstance().getTargets().size() == 1) {
 368  0
             ToDoList list = Designer.theDesigner().getToDoList();
 369  0
             List<ToDoItem> items = list.elementListForOffender(getOwner());
 370  0
             if (items != null && items.size() > 0) {
 371  
                 // TODO: This creates a dependency on the Critics subsystem.
 372  
                 // We need a generic way for modules (including our internal
 373  
                 // subsystems) to request addition of actions to the popup
 374  
                 // menu. - tfm 20080430
 375  0
                 ArgoJMenu critiques = new ArgoJMenu("menu.popup.critiques");
 376  0
                 ToDoItem itemUnderMouse = hitClarifier(me.getX(), me.getY());
 377  0
                 if (itemUnderMouse != null) {
 378  0
                     critiques.add(new ActionGoToCritique(itemUnderMouse));
 379  0
                     critiques.addSeparator();
 380  
                 }
 381  0
                 for (ToDoItem item : items) {
 382  0
                     if (item == itemUnderMouse) {
 383  0
                         continue;
 384  
                     }
 385  0
                     critiques.add(new ActionGoToCritique(item));
 386  
                 }
 387  0
                 popUpActions.add(0, new JSeparator());
 388  0
                 popUpActions.add(0, critiques);
 389  
             }
 390  
         }
 391  
 
 392  
         // Add stereotypes submenu
 393  0
         Action[] stereoActions = getApplyStereotypeActions();
 394  0
         if (stereoActions != null && stereoActions.length > 0) {
 395  0
             popUpActions.add(0, new JSeparator());
 396  0
             ArgoJMenu stereotypes = new ArgoJMenu(
 397  
                     "menu.popup.apply-stereotypes");
 398  0
             for (int i = 0; i < stereoActions.length; ++i) {
 399  0
                 stereotypes.addCheckItem(stereoActions[i]);
 400  
             }
 401  0
             popUpActions.add(0, stereotypes);
 402  
         }
 403  
 
 404  0
         return popUpActions;
 405  
     }
 406  
     
 407  
     /**
 408  
      * Get the set of Actions which valid for adding/removing
 409  
      * Stereotypes on the ModelElement of this Fig's owner.
 410  
      *  
 411  
      * @return array of Actions 
 412  
      */
 413  
     protected Action[] getApplyStereotypeActions() {
 414  0
         Collection<Object> elements = new ArrayList<Object>();
 415  0
         Object owner = getOwner();
 416  0
         if (owner != null) {
 417  0
             elements.add(owner);
 418  
         }
 419  0
         for (Object o : TargetManager.getInstance().getTargets()) {
 420  0
             Object element = null;
 421  0
             if (Model.getFacade().isAUMLElement(o)) {
 422  0
                 element = o;
 423  0
             } else if (o instanceof Fig) {
 424  0
                 element = ((Fig) o).getOwner();
 425  
             }
 426  0
             if (element != null && element != owner) {
 427  0
                 elements.add(element);
 428  
             }
 429  0
         }
 430  0
         return StereotypeUtility.getApplyStereotypeActions(elements);
 431  
     }
 432  
 
 433  
     /**
 434  
      * distance formula: (x-h)^2 + (y-k)^2 = distance^2
 435  
      *
 436  
      * @param p1 point
 437  
      * @param p2 point
 438  
      * @return the square of the distance
 439  
      */
 440  
     protected int getSquaredDistance(Point p1, Point p2) {
 441  0
         int xSquared = p2.x - p1.x;
 442  0
         xSquared *= xSquared;
 443  0
         int ySquared = p2.y - p1.y;
 444  0
         ySquared *= ySquared;
 445  0
         return xSquared + ySquared;
 446  
     }
 447  
 
 448  
     /**
 449  
      * @param g the <code>Graphics</code> object
 450  
      */
 451  
     public void paintClarifiers(Graphics g) {
 452  0
         int iconPos = 25, gap = 1, xOff = -4, yOff = -4;
 453  0
         Point p = new Point();
 454  0
         ToDoList tdList = Designer.theDesigner().getToDoList();
 455  
         /* Owner related todo items: */
 456  0
         List<ToDoItem> items = tdList.elementListForOffender(getOwner());
 457  0
         for (ToDoItem item : items) {
 458  0
             Icon icon = item.getClarifier();
 459  0
             if (icon instanceof Clarifier) {
 460  0
                 ((Clarifier) icon).setFig(this);
 461  0
                 ((Clarifier) icon).setToDoItem(item);
 462  
             }
 463  0
             if (icon != null) {
 464  0
                 stuffPointAlongPerimeter(iconPos, p);
 465  0
                 icon.paintIcon(null, g, p.x + xOff, p.y + yOff);
 466  0
                 iconPos += icon.getIconWidth() + gap;
 467  
             }
 468  0
         }
 469  
         /* Fig related todo items: */
 470  0
         items = tdList.elementListForOffender(this);
 471  0
         for (ToDoItem item : items) {
 472  0
             Icon icon = item.getClarifier();
 473  0
             if (icon instanceof Clarifier) {
 474  0
                 ((Clarifier) icon).setFig(this);
 475  0
                 ((Clarifier) icon).setToDoItem(item);
 476  
             }
 477  0
             if (icon != null) {
 478  0
                 stuffPointAlongPerimeter(iconPos, p);
 479  0
                 icon.paintIcon(null, g, p.x + xOff, p.y + yOff);
 480  0
                 iconPos += icon.getIconWidth() + gap;
 481  
             }
 482  0
         }
 483  0
     }
 484  
 
 485  
     /**
 486  
      * This is used to draw a box round the edge of any editable FigText
 487  
      * annotations of the edge when the edge is selected.
 488  
      * TODO: This logic probably belongs in our base selection class
 489  
      * SelectionEdgeClarifiers and could be written to discover what FigText
 490  
      * annotations exist rather than hard code in subclasses.
 491  
      * @param f the fig to indicate the bounds of
 492  
      * @param g the graphics
 493  
      */
 494  
     protected void indicateBounds(FigText f, Graphics g) {
 495  
         // No longer necessary, see issue 1048.
 496  0
     }
 497  
     
 498  
     /**
 499  
      * The user clicked on the clarifier.
 500  
      *
 501  
      * @param x the x of the point clicked
 502  
      * @param y the y of the point clicked
 503  
      * @return the todo item clicked
 504  
      */
 505  
     public ToDoItem hitClarifier(int x, int y) {
 506  0
         int iconPos = 25, xOff = -4, yOff = -4;
 507  0
         Point p = new Point();
 508  0
         ToDoList tdList = Designer.theDesigner().getToDoList();
 509  0
         List<ToDoItem> items = tdList.elementListForOffender(getOwner());
 510  0
         for (ToDoItem item : items) {
 511  0
             Icon icon = item.getClarifier();
 512  0
             stuffPointAlongPerimeter(iconPos, p);
 513  0
             int width = icon.getIconWidth();
 514  0
             int height = icon.getIconHeight();
 515  0
             if (y >= p.y + yOff
 516  
                 && y <= p.y + height + yOff
 517  
                 && x >= p.x + xOff
 518  
                 && x <= p.x + width + xOff) {
 519  0
                 return item;
 520  
             }
 521  0
             iconPos += width;
 522  0
         }
 523  0
         for (ToDoItem item : items) {
 524  0
             Icon icon = item.getClarifier();
 525  0
             if (icon instanceof Clarifier) {
 526  0
                 ((Clarifier) icon).setFig(this);
 527  0
                 ((Clarifier) icon).setToDoItem(item);
 528  0
                 if (((Clarifier) icon).hit(x, y)) {
 529  0
                     return item;
 530  
                 }
 531  
             }
 532  0
         }
 533  0
         items = tdList.elementListForOffender(this);
 534  0
         for (ToDoItem item : items) {
 535  0
             Icon icon = item.getClarifier();
 536  0
             stuffPointAlongPerimeter(iconPos, p);
 537  0
             int width = icon.getIconWidth();
 538  0
             int height = icon.getIconHeight();
 539  0
             if (y >= p.y + yOff
 540  
                 && y <= p.y + height + yOff
 541  
                 && x >= p.x + xOff
 542  
                 && x <= p.x + width + xOff) {
 543  0
                 return item;
 544  
             }
 545  0
             iconPos += width;
 546  0
         }
 547  0
         for (ToDoItem item : items) {
 548  0
             Icon icon = item.getClarifier();
 549  0
             if (icon instanceof Clarifier) {
 550  0
                 ((Clarifier) icon).setFig(this);
 551  0
                 ((Clarifier) icon).setToDoItem(item);
 552  0
                 if (((Clarifier) icon).hit(x, y)) {
 553  0
                     return item;
 554  
                 }
 555  
             }
 556  0
         }
 557  0
         return null;
 558  
     }
 559  
 
 560  
     /**
 561  
      * @return a {@link SelectionRerouteEdge} object that manages selection and
 562  
      *         rerouting of the edge.
 563  
      * 
 564  
      * @see org.tigris.gef.presentation.Fig#makeSelection()
 565  
      */
 566  
     @Override
 567  
     public Selection makeSelection() {
 568  
         // TODO: There is a cyclic dependency between SelectionRerouteEdge
 569  
         // and FigEdgeModelElement
 570  0
         return new SelectionRerouteEdge(this);
 571  
     }
 572  
 
 573  
     /**
 574  
      * Getter for name, the name Fig
 575  
      * @return the nameFig
 576  
      */
 577  
     protected FigText getNameFig() {
 578  0
         return nameFig;
 579  
     }
 580  
     
 581  
     /**
 582  
      * Get the Rectangle in which the model elements name is displayed
 583  
      *
 584  
      * @return the bounds of the namefig
 585  
      */
 586  
     public Rectangle getNameBounds() {
 587  0
         return nameFig.getBounds();
 588  
     }
 589  
     
 590  
     /**
 591  
      * @return the text of the namefig
 592  
      */
 593  
     public String getName() {
 594  0
         return nameFig.getText();
 595  
     }
 596  
 
 597  
     /**
 598  
      * Getter for stereo, the stereotype Fig
 599  
      * @return the stereo Fig
 600  
      */
 601  
     protected FigStereotypesGroup getStereotypeFig() {
 602  0
         return stereotypeFig;
 603  
     }
 604  
 
 605  
     /*
 606  
      * @see java.beans.VetoableChangeListener#vetoableChange(java.beans.PropertyChangeEvent)
 607  
      */
 608  
     public void vetoableChange(PropertyChangeEvent pce) {
 609  0
         Object src = pce.getSource();
 610  0
         if (src == getOwner()) {
 611  0
             DelayedChangeNotify delayedNotify =
 612  
                 new DelayedChangeNotify(this, pce);
 613  0
             SwingUtilities.invokeLater(delayedNotify);
 614  
         }
 615  0
     }
 616  
 
 617  
     /*
 618  
      * @see org.argouml.kernel.DelayedVChangeListener#delayedVetoableChange(java.beans.PropertyChangeEvent)
 619  
      */
 620  
     public void delayedVetoableChange(PropertyChangeEvent pce) {
 621  
         // update any text, colors, fonts, etc.
 622  0
         renderingChanged();
 623  
         // update the relative sizes and positions of internel Figs
 624  0
         Rectangle bbox = getBounds();
 625  0
         setBounds(bbox.x, bbox.y, bbox.width, bbox.height);
 626  0
         endTrans();
 627  0
     }
 628  
 
 629  
     /**
 630  
      * This method gets called when a bound property gets changed. This may
 631  
      * represent a UML element value from the Model subsystem, a GEF property,
 632  
      * or something which ArgoUML itself implements.
 633  
      * 
 634  
      * @param pve the event containing the property change information
 635  
      * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
 636  
      */
 637  
     @Override
 638  
     public void propertyChange(final PropertyChangeEvent pve) {
 639  0
         Object src = pve.getSource();
 640  0
         String pName = pve.getPropertyName();
 641  0
         if (pve instanceof DeleteInstanceEvent && src == getOwner()) {
 642  0
             Runnable doWorkRunnable = new Runnable() {
 643  
                 public void run() {
 644  
                     try {
 645  0
                         removeFromDiagram();
 646  0
                     } catch (InvalidElementException e) {
 647  0
                         LOG.error("updateLayout method accessed "
 648  
                                     + "deleted element", e);
 649  0
                     }
 650  0
                 }  
 651  
             };
 652  0
             SwingUtilities.invokeLater(doWorkRunnable);
 653  0
             return;
 654  
         }
 655  
         // We handle and consume editing events
 656  0
         if (pName.equals("editing")
 657  
                 && Boolean.FALSE.equals(pve.getNewValue())) {
 658  0
             LOG.debug("finished editing");
 659  
             // parse the text that was edited
 660  0
             textEdited((FigText) src);
 661  0
             calcBounds();
 662  0
             endTrans();
 663  0
         } else if (pName.equals("editing")
 664  
                 && Boolean.TRUE.equals(pve.getNewValue())) {
 665  0
             textEditStarted((FigText) src);
 666  
         } else {
 667  
             // Pass everything except editing events to superclass
 668  0
             super.propertyChange(pve);
 669  
         }
 670  
 
 671  0
         if (Model.getFacade().isAUMLElement(src) 
 672  
                 && getOwner() != null
 673  
                 && !Model.getUmlFactory().isRemoved(getOwner())) {
 674  
             /* If the source of the event is an UML object,
 675  
              * then the UML model has been changed.*/
 676  0
             modelChanged(pve);
 677  
             
 678  0
             final UmlChangeEvent event = (UmlChangeEvent) pve;
 679  
             
 680  0
             Runnable doWorkRunnable = new Runnable() {
 681  
                 public void run() {
 682  
                     try {
 683  0
                         updateLayout(event);
 684  0
                     } catch (InvalidElementException e) {
 685  0
                         if (LOG.isDebugEnabled()) {
 686  0
                             LOG.debug("updateLayout method accessed "
 687  
                                     + "deleted element ", e);
 688  
                         }
 689  0
                     }
 690  0
                 }  
 691  
             };
 692  0
             SwingUtilities.invokeLater(doWorkRunnable);
 693  
             
 694  
         }
 695  
         /* The following is a possible future improvement 
 696  
          * of the modelChanged() function.
 697  
          * Michiel: Propose not to do this to keep architecture stable. */
 698  
 //        if (pve instanceof AttributeChangeEvent) {
 699  
 //            modelAttributeChanged((AttributeChangeEvent) pve);
 700  
 //        } else if (pve instanceof AddAssociationEvent) {
 701  
 //            modelAssociationAdded((AddAssociationEvent) pve);
 702  
 //        } else if (pve instanceof RemoveAssociationEvent) {
 703  
 //            modelAssociationRemoved((RemoveAssociationEvent) pve);
 704  
 //        }
 705  0
     }
 706  
     
 707  
     /**
 708  
      * Called whenever we receive an AttributeChangeEvent.
 709  
      * 
 710  
      * @param ace the event
 711  
      */
 712  
     protected void modelAttributeChanged(AttributeChangeEvent ace) {
 713  
         // Default implementation is to do nothing
 714  0
     }
 715  
 
 716  
     /**
 717  
      * Called whenever we receive an AddAssociationEvent.
 718  
      * 
 719  
      * @param aae the event
 720  
      */
 721  
     protected void modelAssociationAdded(AddAssociationEvent aae) {
 722  
         // Default implementation is to do nothing        
 723  0
     }
 724  
 
 725  
     /**
 726  
      * Called whenever we receive an RemoveAssociationEvent.
 727  
      * 
 728  
      * @param rae the event
 729  
      */
 730  
     protected void modelAssociationRemoved(RemoveAssociationEvent rae) {
 731  
         // Default implementation is to do nothing
 732  0
     }
 733  
     
 734  
     /**
 735  
      * This is a template method called by the ArgoUML framework as the result
 736  
      * of a change to a model element. Do not call this method directly
 737  
      * yourself.
 738  
      * <p>Override this in any subclasses in order to restructure the FigNode
 739  
      * due to change of any model element that this FigNode is listening to.</p>
 740  
      * <p>This method is guaranteed by the framework to be running on the 
 741  
      * Swing/AWT thread.</p>
 742  
      *
 743  
      * @param event the UmlChangeEvent that caused the change
 744  
      */
 745  
     protected void updateLayout(UmlChangeEvent event) {
 746  0
     }
 747  
 
 748  
     /**
 749  
      * This method is called when the user doubleclicked on the text field,
 750  
      * and starts editing. Subclasses should override this method to e.g.
 751  
      * supply help to the user about the used format. <p>
 752  
      *
 753  
      * It is also possible to alter the text to be edited
 754  
      * already here, e.g. by adding the stereotype in front of the name,
 755  
      * but that seems not user-friendly.
 756  
      *
 757  
      * @param ft the FigText that will be edited and contains the start-text
 758  
      */
 759  
     protected void textEditStarted(FigText ft) {
 760  0
         if (ft == getNameFig()) {
 761  0
             showHelp(notationProviderName.getParsingHelp());
 762  0
             ft.setText(notationProviderName.toString(getOwner(), 
 763  
                     getNotationSettings()));
 764  
         }
 765  0
     }
 766  
     
 767  
 
 768  
     /**
 769  
      * Utility function to localize the given string with help text,
 770  
      * and show it in the status bar of the ArgoUML window.
 771  
      * This function is used in favour of the inline call
 772  
      * to enable later improvements; e.g. it would be possible to
 773  
      * show a help-balloon. TODO: Work this out.
 774  
      * One matter to possibly improve: show multiple lines.
 775  
      *
 776  
      * @param s the given string to be localized and shown
 777  
      */
 778  
     protected void showHelp(String s) {
 779  0
         ArgoEventPump.fireEvent(new ArgoHelpEvent(
 780  
                 ArgoEventTypes.HELP_CHANGED, this,
 781  
                 Translator.localize(s)));
 782  0
     }
 783  
 
 784  
     /**
 785  
      * This method is called after the user finishes editing a text
 786  
      * field that is in the FigEdgeModelElement.  Determine which field
 787  
      * and update the model.  This class handles the name, subclasses
 788  
      * should override to handle other text elements.
 789  
      *
 790  
      * @param ft the text Fig that has been edited
 791  
      */
 792  
     protected void textEdited(FigText ft) {
 793  0
         if (ft == nameFig) {
 794  0
             if (getOwner() == null) {
 795  0
                 return;
 796  
             }
 797  0
             notationProviderName.parse(getOwner(), ft.getText());
 798  0
             ft.setText(notationProviderName.toString(getOwner(), 
 799  
                     getNotationSettings()));
 800  
         }
 801  0
     }
 802  
 
 803  
     /**
 804  
      * @param f the Fig
 805  
      * @return true if editable
 806  
      */
 807  
     protected boolean canEdit(Fig f) {
 808  0
         return true;
 809  
     }
 810  
 
 811  
     ////////////////////////////////////////////////////////////////
 812  
     // event handlers - MouseListener implementation
 813  
 
 814  
     /*
 815  
      * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
 816  
      */
 817  
     public void mousePressed(MouseEvent me) {
 818  
         // Required for MouseListener interface, but we only care about clicks
 819  0
     }
 820  
 
 821  
     /*
 822  
      * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
 823  
      */
 824  
     public void mouseReleased(MouseEvent me) {
 825  
         // Required for MouseListener interface, but we only care about clicks
 826  0
     }
 827  
 
 828  
     /*
 829  
      * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
 830  
      */
 831  
     public void mouseEntered(MouseEvent me) {
 832  
         // Required for MouseListener interface, but we only care about clicks
 833  0
     }
 834  
 
 835  
     /*
 836  
      * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
 837  
      */
 838  
     public void mouseExited(MouseEvent me) {
 839  
         // Required for MouseListener interface, but we only care about clicks
 840  0
     }
 841  
 
 842  
     /*
 843  
      * If the user double clicks on any part of this FigNode, pass it
 844  
      * down to one of the internal Figs.  This allows the user to
 845  
      * initiate direct text editing.
 846  
      *
 847  
      * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
 848  
      */
 849  
     public void mouseClicked(MouseEvent me) {
 850  0
         if (!me.isConsumed() && !isReadOnly() && me.getClickCount() >= 2) {
 851  0
             Fig f = hitFig(new Rectangle(me.getX() - 2, me.getY() - 2, 4, 4));
 852  0
             if (f instanceof MouseListener && canEdit(f)) {
 853  0
                 ((MouseListener) f).mouseClicked(me);
 854  
             }
 855  
         }
 856  0
         me.consume();
 857  0
     }
 858  
     
 859  
     /**
 860  
      * Return true if the model element that this Fig represents is read only
 861  
      * @return The model element is read only.
 862  
      */
 863  
     private boolean isReadOnly() {
 864  0
         Object owner = getOwner();
 865  0
         if (Model.getFacade().isAUMLElement(owner)) {
 866  0
             return Model.getModelManagementHelper().isReadOnly(owner);
 867  
         }
 868  0
         return false;
 869  
     }
 870  
 
 871  
     
 872  
 
 873  
     /*
 874  
      * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
 875  
      */
 876  
     public void keyPressed(KeyEvent ke) {
 877  
         // Required for KeyListener interface, but not used
 878  0
     }
 879  
 
 880  
     /*
 881  
      * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
 882  
      */
 883  
     public void keyReleased(KeyEvent ke) {
 884  
         // Required for KeyListener interface, but not used
 885  0
     }
 886  
 
 887  
     /*
 888  
      * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
 889  
      */
 890  
     public void keyTyped(KeyEvent ke) {
 891  0
         if (!ke.isConsumed()
 892  
                 && !isReadOnly()
 893  
                 && nameFig != null
 894  
                 && canEdit(nameFig)) {
 895  0
             nameFig.keyTyped(ke);
 896  
         }
 897  0
     }
 898  
 
 899  
 
 900  
     /**
 901  
      * Rerenders the attached elements of the fig. <p>
 902  
      * 
 903  
      * Warning: The purpose of this function is NOT 
 904  
      * to redraw the whole Fig every time
 905  
      * something changes. That would be inefficient.<p>
 906  
      * 
 907  
      * Instead, this function should only be called
 908  
      * for major changes that require a complete redraw, 
 909  
      * such as change of owner, 
 910  
      * and change of notation language. <p>
 911  
      * 
 912  
      * Overrule this function for subclasses that add extra
 913  
      * or remove graphical parts.
 914  
      */
 915  
     public void renderingChanged() {
 916  
         // TODO: This needs to use a different method than that used by the
 917  
         // constructor if it wants to allow the method to be overridden
 918  0
         initNotationProviders(getOwner());
 919  0
         updateNameText();
 920  0
         updateStereotypeText();
 921  0
         damage();
 922  0
     }
 923  
     
 924  
     ////////////////////////////////////////////////////////////////
 925  
     // internal methods
 926  
 
 927  
     /**
 928  
      * This is called after any part of the UML ModelElement has
 929  
      * changed. This method automatically updates the name FigText.
 930  
      * Subclasses should override and update other parts.
 931  
      *
 932  
      * @param e the event
 933  
      */
 934  
     protected void modelChanged(PropertyChangeEvent e) {
 935  0
         if (e instanceof DeleteInstanceEvent) {
 936  
             // No need to update if model element went away
 937  0
             return;
 938  
         }
 939  
         
 940  0
         if (e instanceof AssociationChangeEvent 
 941  
                 || e instanceof AttributeChangeEvent) {
 942  0
             updateListeners(getOwner(), getOwner());
 943  
         }
 944  
 
 945  
         // Update attached node figures
 946  
         // TODO: Presumably this should only happen on a add or remove event
 947  0
         determineFigNodes();
 948  0
     }
 949  
     
 950  
     /**
 951  
      * generate the notation for the modelelement and stuff it into the text Fig
 952  
      */
 953  
     protected void updateNameText() {
 954  
 
 955  0
         if (getOwner() == null) {
 956  0
             return;
 957  
         }
 958  0
         if (notationProviderName != null) {
 959  0
             String nameStr = notationProviderName.toString(
 960  
                     getOwner(), getNotationSettings());
 961  0
             nameFig.setText(nameStr);
 962  0
             updateFont();
 963  0
             calcBounds();
 964  0
             setBounds(getBounds());
 965  
         }
 966  0
     }
 967  
 
 968  
     /**
 969  
      * generate the notation for the stereotype and stuff it into the text Fig
 970  
      */
 971  
     protected void updateStereotypeText() {
 972  0
         if (getOwner() == null) {
 973  0
             return;
 974  
         }
 975  0
         Object modelElement = getOwner();
 976  0
         stereotypeFig.populate();
 977  0
     }
 978  
 
 979  
     /**
 980  
      * Replace the NotationProvider(s). <p>
 981  
      *
 982  
      * This method shall not be used for the initial creation of
 983  
      * notation providers, but only for replacing them when required.
 984  
      * Initialization must be done in the
 985  
      * constructor using methods which
 986  
      * can't be overridden. <p>
 987  
      * NotationProviders can not be updated - they
 988  
      * are lightweight throw-away objects.
 989  
      * Hence this method creates a (new) NotationProvider whenever
 990  
      * needed. E.g. when the notation language is
 991  
      * changed by the user, then the NPs are to be re-created.
 992  
      * So, this method shall not be
 993  
      * called from a Fig constructor.<p>
 994  
      *
 995  
      * After the removal of the deprecated method setOwner(),
 996  
      * this method shall contain the following statement:
 997  
      *     assert notationProviderName != null
 998  
      * 
 999  
      * @param own the current owner
 1000  
      */ 
 1001  
     protected void initNotationProviders(Object own) {
 1002  0
         if (notationProviderName != null) {
 1003  0
             notationProviderName.cleanListener();
 1004  
         }
 1005  
         /* This should NOT be looking for a NamedElement, 
 1006  
          * since this is not always about the name of this 
 1007  
          * modelelement alone.*/
 1008  0
         if (Model.getFacade().isAModelElement(own)) {
 1009  0
             NotationName notation = Notation.findNotation(
 1010  
                     getNotationSettings().getNotationLanguage());
 1011  0
             notationProviderName =
 1012  
                 NotationProviderFactory2.getInstance().getNotationProvider(
 1013  
                         getNotationProviderType(), own, this, 
 1014  
                         notation);
 1015  
         }
 1016  0
     }
 1017  
 
 1018  
 
 1019  
     /**
 1020  
      * Overrule this for subclasses 
 1021  
      * that need a different NotationProvider.
 1022  
      * 
 1023  
      * @return the type of the notation provider
 1024  
      */
 1025  
     protected int getNotationProviderType() {
 1026  0
         return NotationProviderFactory2.TYPE_NAME;
 1027  
     }
 1028  
 
 1029  
     /**
 1030  
      * Implementations of this method should register/unregister the fig for all
 1031  
      * (model)events that may cause a repaint to be necessary.
 1032  
      * In the simplest case, the fig should register itself
 1033  
      * as listening to (all) events fired by (only) the owner. <p>
 1034  
      *  
 1035  
      * But for, for example, for a
 1036  
      * FigLink the fig must also register for events fired by the
 1037  
      * association of the owner - because the name of 
 1038  
      * the association is shown, not the name of the Link.<p>
 1039  
      * 
 1040  
      * In other cases, there is no need to register for any event, 
 1041  
      * e.g. when a notationProvider is used. <p>
 1042  
      * 
 1043  
      * This function is called in 2 places: at creation (load) time of this Fig,
 1044  
      * i.e. when the owner changes, 
 1045  
      * and in some cases by the modelChanged() function, 
 1046  
      * i.e. when the model changes. <p>
 1047  
      * 
 1048  
      * This function shall always register for the "remove" event of the owner!
 1049  
      * Otherwise the Fig will not be deleted when the owner gets deleted.<p>
 1050  
      * 
 1051  
      *  IF this method is called with both the oldOwner and the 
 1052  
      *  newOwner equal and not null, 
 1053  
      *  AND we listen only to the owner itself,
 1054  
      *  THEN we can safely ignore the call, but 
 1055  
      *  ELSE we need to update the listeners of the related elements, 
 1056  
      *  since the related elements may have been replaced.
 1057  
      * 
 1058  
      * @param newOwner the new owner for the listeners, 
 1059  
      *          or null if all listeners have to be removed
 1060  
      * @param oldOwner the previous owner, 
 1061  
      *          or null if there was none, and all listeners have to be set
 1062  
      */
 1063  
     protected void updateListeners(Object oldOwner, Object newOwner) {
 1064  0
         Set<Object[]> l = new HashSet<Object[]>();
 1065  0
         if (newOwner != null) {
 1066  0
             l.add(new Object[] {newOwner, "remove"});
 1067  
         }
 1068  0
         updateElementListeners(l);
 1069  0
     }
 1070  
 
 1071  
     /*
 1072  
      * @see org.tigris.gef.presentation.Fig#setLayer(org.tigris.gef.base.Layer)
 1073  
      */
 1074  
     @Override
 1075  
     public void setLayer(Layer lay) {
 1076  0
         super.setLayer(lay);
 1077  0
         getFig().setLayer(lay);
 1078  
         
 1079  
         // TODO: Workaround for GEF redraw problem
 1080  
         // Force all child figs into the same layer
 1081  0
         for (Fig f : (List<Fig>) getPathItemFigs()) {
 1082  0
             f.setLayer(lay);
 1083  
         }
 1084  0
     }
 1085  
 
 1086  
     /*
 1087  
      * @see org.tigris.gef.presentation.Fig#deleteFromModel()
 1088  
      */
 1089  
     @Override
 1090  
     public void deleteFromModel() {
 1091  0
         Object own = getOwner();
 1092  0
         if (own != null) {
 1093  0
             getProject().moveToTrash(own);
 1094  
         }
 1095  
 
 1096  
         /* TODO: MVW: Why is this not done in GEF? */
 1097  0
         Iterator it = getPathItemFigs().iterator();
 1098  0
         while (it.hasNext()) {
 1099  0
             ((Fig) it.next()).deleteFromModel();
 1100  
         }
 1101  0
         super.deleteFromModel();
 1102  0
     }
 1103  
 
 1104  
     /**
 1105  
      * @see org.argouml.application.events.ArgoNotationEventListener#notationChanged(org.argouml.application.events.ArgoNotationEvent)
 1106  
      * @deprecated for 0.27.2 by tfmorris. Changes to notation provider are
 1107  
      *             now handled by the owning diagram.
 1108  
      */
 1109  
     @Deprecated
 1110  
     public void notationChanged(ArgoNotationEvent event) {
 1111  0
         if (getOwner() == null) {
 1112  0
             return;
 1113  
         }
 1114  0
         renderingChanged();
 1115  0
     }
 1116  
 
 1117  
     /**
 1118  
      * @see org.argouml.application.events.ArgoNotationEventListener#notationAdded(org.argouml.application.events.ArgoNotationEvent)
 1119  
      * @deprecated for 0.27.2 by tfmorris.
 1120  
      */
 1121  
     @Deprecated
 1122  
     public void notationAdded(ArgoNotationEvent event) {
 1123  
         // Default implementation is to do nothing
 1124  0
     }
 1125  
 
 1126  
     /**
 1127  
      * @see org.argouml.application.events.ArgoNotationEventListener#notationRemoved(org.argouml.application.events.ArgoNotationEvent)
 1128  
      * @deprecated for 0.27.2 by tfmorris.
 1129  
      */
 1130  
     @Deprecated
 1131  
     public void notationRemoved(ArgoNotationEvent event) {
 1132  
         // Default implementation is to do nothing
 1133  0
     }
 1134  
 
 1135  
     /**
 1136  
      * @see org.argouml.application.events.ArgoNotationEventListener#notationProviderAdded(org.argouml.application.events.ArgoNotationEvent)
 1137  
      * @deprecated for 0.27.2 by tfmorris.
 1138  
      */
 1139  
     @Deprecated
 1140  
     public void notationProviderAdded(ArgoNotationEvent event) {
 1141  
         // Default implementation is to do nothing
 1142  0
     }
 1143  
 
 1144  
     /**
 1145  
      * @see org.argouml.application.events.ArgoNotationEventListener#notationProviderRemoved(org.argouml.application.events.ArgoNotationEvent)
 1146  
      * @deprecated for 0.27.2 by tfmorris.
 1147  
      */
 1148  
     @Deprecated
 1149  
     public void notationProviderRemoved(ArgoNotationEvent event) {
 1150  
         // Default implementation is to do nothing
 1151  0
     }
 1152  
 
 1153  
     /*
 1154  
      * @see org.tigris.gef.presentation.Fig#hit(java.awt.Rectangle)
 1155  
      */
 1156  
     @Override
 1157  
     public boolean hit(Rectangle r) {
 1158  
         // Check if labels etc have been hit
 1159  
         // Apparently GEF does require PathItems to be "annotations"
 1160  
         // which ours aren't, so until that is resolved...
 1161  0
         Iterator it = getPathItemFigs().iterator();
 1162  0
         while (it.hasNext()) {
 1163  0
             Fig f = (Fig) it.next();
 1164  0
             if (f.hit(r)) {
 1165  0
                 return true;
 1166  
             }
 1167  0
         }
 1168  0
         return super.hit(r);
 1169  
     }
 1170  
 
 1171  
     /*
 1172  
      * @see org.tigris.gef.presentation.Fig#removeFromDiagram()
 1173  
      */
 1174  
     @Override
 1175  
     public final void removeFromDiagram() {
 1176  0
         Fig delegate = getRemoveDelegate();
 1177  
         // TODO: Dependency cycle between FigNodeModelElement and FigEdgeME
 1178  
         // Is this needed?  If so, introduce a Removable interface to decouple
 1179  0
         if (delegate instanceof FigNodeModelElement) {
 1180  0
             ((FigNodeModelElement) delegate).removeFromDiagramImpl();
 1181  0
         } else if (delegate instanceof FigEdgeModelElement) {
 1182  0
             ((FigEdgeModelElement) delegate).removeFromDiagramImpl();
 1183  0
         } else if (delegate != null) {
 1184  0
             removeFromDiagramImpl();
 1185  
         }
 1186  0
     }
 1187  
     
 1188  
     /**
 1189  
      * Subclasses should override this to redirect a remove request from
 1190  
      * one Fig to another.
 1191  
      * e.g. FigEdgeAssociationClass uses this to delegate the remove to
 1192  
      * its attached FigAssociationClass.
 1193  
      * @return the fig handling the remove
 1194  
      */
 1195  
     protected Fig getRemoveDelegate() {
 1196  0
         return this;
 1197  
     }
 1198  
     
 1199  
     protected void removeFromDiagramImpl() {
 1200  0
         Object o = getOwner();
 1201  0
         if (o != null) {
 1202  0
             removeElementListener(o);
 1203  
         }
 1204  0
         if (notationProviderName != null) {
 1205  0
             notationProviderName.cleanListener();
 1206  
         }
 1207  
 
 1208  
         /* TODO: MVW: Why is this not done in GEF? */
 1209  0
         Iterator it = getPathItemFigs().iterator();
 1210  0
         while (it.hasNext()) {
 1211  0
             Fig fig = (Fig) it.next();
 1212  0
             fig.removeFromDiagram();
 1213  0
         }
 1214  
 
 1215  0
         super.removeFromDiagram();
 1216  0
         damage();
 1217  0
     }
 1218  
     
 1219  
     protected void superRemoveFromDiagram() {
 1220  0
         super.removeFromDiagram();
 1221  0
     }
 1222  
 
 1223  
     /*
 1224  
      * @see org.tigris.gef.presentation.Fig#damage()
 1225  
      */
 1226  
     @Override
 1227  
     public void damage() {
 1228  0
         super.damage();
 1229  0
         getFig().damage();
 1230  0
     }
 1231  
 
 1232  
     /**
 1233  
      * <p>Determines if the FigEdge is currently connected to the correct
 1234  
      * FigNodes, if not the edges is the correct FigNodes set and the edge
 1235  
      * rerouted.
 1236  
      * <p>Typically this is used when a user has amended from the property
 1237  
      * panel a relationship from one model element to another and the graph
 1238  
      * needs to react to that change.
 1239  
      * <p>e.g. if the participant of an association end is changed.
 1240  
      * <p>Calls a helper method (layoutThisToSelf) to avoid this edge
 1241  
      * disappearing if the new source and dest are the same node.
 1242  
      * 
 1243  
      * TODO: This method is called far too frequently. It should only be called
 1244  
      * when a specific event is received. It seems to be currently called whenever
 1245  
      * any event is received from the owner.
 1246  
      *
 1247  
      * @return boolean whether or not the update was sucessful
 1248  
      */
 1249  
     protected boolean determineFigNodes() {
 1250  0
         Object owner = getOwner();
 1251  0
         if (owner == null) {
 1252  0
             LOG.error("The FigEdge has no owner");
 1253  0
             return false;
 1254  
         }
 1255  0
         if (getLayer() == null) {
 1256  0
             LOG.error("The FigEdge has no layer");
 1257  0
             return false;
 1258  
         }
 1259  
 
 1260  0
         Object newSource = getSource();
 1261  0
         Object newDest = getDestination();
 1262  
 
 1263  0
         Fig currentSourceFig = getSourceFigNode();
 1264  0
         Fig currentDestFig = getDestFigNode();
 1265  0
         Object currentSource = null;
 1266  0
         Object currentDestination = null;
 1267  0
         if (currentSourceFig != null && currentDestFig != null) {
 1268  0
             currentSource = currentSourceFig.getOwner();
 1269  0
             currentDestination = currentDestFig.getOwner();
 1270  
         }
 1271  0
         if (newSource != currentSource || newDest != currentDestination) {
 1272  0
             Fig newSourceFig = getNoEdgePresentationFor(newSource);
 1273  0
             Fig newDestFig = getNoEdgePresentationFor(newDest);
 1274  0
             if (newSourceFig != currentSourceFig) {
 1275  0
                 setSourceFigNode((FigNode) newSourceFig);
 1276  0
                 setSourcePortFig(newSourceFig);
 1277  
 
 1278  
             }
 1279  0
             if (newDestFig != currentDestFig) {
 1280  0
                 setDestFigNode((FigNode) newDestFig);
 1281  0
                 setDestPortFig(newDestFig);
 1282  
             }
 1283  0
             ((FigNode) newSourceFig).updateEdges();
 1284  0
             ((FigNode) newDestFig).updateEdges();
 1285  0
             calcBounds();
 1286  
 
 1287  
             // adapted from SelectionWButtons from line 280
 1288  
             // calls a helper method to avoid this edge disappearing
 1289  
             // if the new source and dest are the same node.
 1290  0
             if (newSourceFig == newDestFig) {
 1291  
 
 1292  0
                 layoutThisToSelf();
 1293  
             }
 1294  
 
 1295  
         }
 1296  
 
 1297  0
         return true;
 1298  
     }
 1299  
 
 1300  
     /**
 1301  
      * A version of GEF's presentationFor() method which 
 1302  
      * @param element ModelElement to return presentation for
 1303  
      * @return the Fig representing the presentation
 1304  
      */
 1305  
     private Fig getNoEdgePresentationFor(Object element) {
 1306  0
         if (element == null) {
 1307  0
             throw new IllegalArgumentException("Can't search for a null owner");
 1308  
         }
 1309  
         
 1310  0
         List contents = PgmlUtility.getContentsNoEdges(getLayer());
 1311  0
         int figCount = contents.size();
 1312  0
         for (int figIndex = 0; figIndex < figCount; ++figIndex) {
 1313  0
             Fig fig = (Fig) contents.get(figIndex);
 1314  0
             if (fig.getOwner() == element) {
 1315  0
                 return fig;
 1316  
             }
 1317  
         }
 1318  0
         throw new IllegalStateException("Can't find a FigNode representing "
 1319  
                 + Model.getFacade().getName(element));
 1320  
     }
 1321  
 
 1322  
 
 1323  
     /**
 1324  
      * helper method for updateClassifiers() in order to automatically
 1325  
      * layout an edge that is now from and to the same node type.
 1326  
      * <p>adapted from SelectionWButtons from line 280
 1327  
      */
 1328  
     private void layoutThisToSelf() {
 1329  
 
 1330  0
         FigPoly edgeShape = new FigPoly();
 1331  
         //newFC = _content;
 1332  0
         Point fcCenter =
 1333  
             new Point(getSourceFigNode().getX() / 2,
 1334  
                     getSourceFigNode().getY() / 2);
 1335  0
         Point centerRight =
 1336  
             new Point(
 1337  
                       (int) (fcCenter.x
 1338  
                              + getSourceFigNode().getSize().getWidth() / 2),
 1339  
                       fcCenter.y);
 1340  
 
 1341  0
         int yoffset = (int) ((getSourceFigNode().getSize().getHeight() / 2));
 1342  0
         edgeShape.addPoint(fcCenter.x, fcCenter.y);
 1343  0
         edgeShape.addPoint(centerRight.x, centerRight.y);
 1344  0
         edgeShape.addPoint(centerRight.x + 30, centerRight.y);
 1345  0
         edgeShape.addPoint(centerRight.x + 30, centerRight.y + yoffset);
 1346  0
         edgeShape.addPoint(centerRight.x, centerRight.y + yoffset);
 1347  
 
 1348  
         // place the edge on the layer and update the diagram
 1349  0
         this.setBetweenNearestPoints(true);
 1350  0
         edgeShape.setLineColor(LINE_COLOR);
 1351  0
         edgeShape.setFilled(false);
 1352  0
         edgeShape.setComplete(true);
 1353  0
         this.setFig(edgeShape);
 1354  0
     }
 1355  
 
 1356  
     /**
 1357  
      * Returns the source of the edge. The source is the owner of the
 1358  
      * node the edge travels from in a binary relationship. For
 1359  
      * instance: for a classifierrole, this is the sender.
 1360  
      * @return a model element
 1361  
      */
 1362  
     protected Object getSource() {
 1363  0
         Object owner = getOwner();
 1364  0
         if (owner != null) {
 1365  0
             return Model.getCoreHelper().getSource(owner);
 1366  
         }
 1367  0
         return null;
 1368  
     }
 1369  
     
 1370  
     /**
 1371  
      * Get the model element at the source end of the edge. This is not the
 1372  
      * same as the owner of the node at the source end, rather it is the
 1373  
      * element that connects the element of the edge to the element of the
 1374  
      * node.
 1375  
      * Mostly this returns null as the edge connects directly to the node but
 1376  
      * implementations such as the Fig for association will return an
 1377  
      * association end that connects the association to the classifier.
 1378  
      * @return the model element that connects the edge to the node (or null
 1379  
      * if the edge requires no such connector.
 1380  
      */
 1381  
     public Object getSourceConnector() {
 1382  0
         return null;
 1383  
     }
 1384  
     
 1385  
     /**
 1386  
      * Get the model element at the destination end of the edge. This is not
 1387  
      * the same as the owner of the node at the source end, rather it is the
 1388  
      * element that connects the element of the edge to the element of the
 1389  
      * node.
 1390  
      * Mostly this returns null as the edge connects directly to the node but
 1391  
      * implementations such as the Fig for association will return an
 1392  
      * association end that connects the association to the classifier.
 1393  
      * @return the model element that connects the edge to the node (or null
 1394  
      * if the edge requires no such connector.
 1395  
      */
 1396  
     public Object getDestinationConnector() {
 1397  0
         return null;
 1398  
     }
 1399  
     
 1400  
     /**
 1401  
      * Returns the destination of the edge. The destination is the
 1402  
      * owner of the node the edge travels to in a binary
 1403  
      * relationship. For instance: for a classifierrole, this is the
 1404  
      * receiver.
 1405  
      * @return a model element
 1406  
      */
 1407  
     protected Object getDestination() {
 1408  0
         Object owner = getOwner();
 1409  0
         if (owner != null) {
 1410  0
             return Model.getCoreHelper().getDestination(owner);
 1411  
         }
 1412  0
         return null;
 1413  
     }
 1414  
 
 1415  
 
 1416  
     /**
 1417  
      * @param allowed true if the function RemoveFromDiagram is allowed
 1418  
      */
 1419  
     protected void allowRemoveFromDiagram(boolean allowed) {
 1420  0
         this.removeFromDiagram = allowed;
 1421  0
     }
 1422  
 
 1423  
     /**
 1424  
      * Set the associated Diagram Interchange element.
 1425  
      * 
 1426  
      * @param element the element to be associated with this Fig
 1427  
      */
 1428  
     public void setDiElement(DiElement element) {
 1429  0
         this.diElement = element;
 1430  0
     }
 1431  
 
 1432  
     /**
 1433  
      * @return the Diagram Interchange element associated with this Fig
 1434  
      */
 1435  
     public DiElement getDiElement() {
 1436  0
         return diElement;
 1437  
     }
 1438  
 
 1439  
     /**
 1440  
      * @return Returns the popupAddOffset.
 1441  
      */
 1442  
     protected static int getPopupAddOffset() {
 1443  0
         return popupAddOffset;
 1444  
     }
 1445  
     
 1446  
 
 1447  
     /**
 1448  
      * Add an element listener and remember the registration.
 1449  
      * 
 1450  
      * @param element
 1451  
      *            element to listen for changes on
 1452  
      * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, Object, String)
 1453  
      */
 1454  
     protected void addElementListener(Object element) {
 1455  0
         listeners.add(new Object[] {element, null});
 1456  0
         Model.getPump().addModelEventListener(this, element);
 1457  0
     }
 1458  
 
 1459  
     /**
 1460  
      * Add a listener and remember the registration.
 1461  
      * 
 1462  
      * @param element
 1463  
      *            element to listen for changes on
 1464  
      * @param property
 1465  
      *            name of property to listen for changes of
 1466  
      * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, Object, String)
 1467  
      */
 1468  
     protected void addElementListener(Object element, String property) {
 1469  0
         listeners.add(new Object[] {element, property});
 1470  0
         Model.getPump().addModelEventListener(this, element, property);
 1471  0
     }
 1472  
 
 1473  
     /**
 1474  
      * Add a listener and remember the registration.
 1475  
      * 
 1476  
      * @param element
 1477  
      *            element to listen for changes on
 1478  
      * @param property
 1479  
      *            array of property names (Strings) to listen for changes of
 1480  
      * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, Object, String)
 1481  
      */
 1482  
     protected void addElementListener(Object element, String[] property) {
 1483  0
         listeners.add(new Object[] {element, property});
 1484  0
         Model.getPump().addModelEventListener(this, element, property);
 1485  0
     }
 1486  
     
 1487  
     /**
 1488  
      * Add an element listener and remember the registration.
 1489  
      * 
 1490  
      * @param element
 1491  
      *            element to listen for changes on
 1492  
      * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, Object, String)
 1493  
      */
 1494  
     protected void removeElementListener(Object element) {
 1495  0
         listeners.remove(new Object[] {element, null});
 1496  0
         Model.getPump().removeModelEventListener(this, element);
 1497  0
     }
 1498  
    
 1499  
     
 1500  
     /**
 1501  
      * Unregister all listeners registered through addElementListener
 1502  
      * @see #addElementListener(Object, String)
 1503  
      */
 1504  
     protected void removeAllElementListeners() {
 1505  0
         removeElementListeners(listeners);
 1506  0
     }
 1507  
 
 1508  
     private void removeElementListeners(Set<Object[]> listenerSet) {
 1509  0
         for (Object[] listener : listenerSet) {
 1510  0
             Object property = listener[1];
 1511  0
             if (property == null) {
 1512  0
                 Model.getPump().removeModelEventListener(this, listener[0]);
 1513  0
             } else if (property instanceof String[]) {
 1514  0
                 Model.getPump().removeModelEventListener(this, listener[0],
 1515  
                         (String[]) property);
 1516  0
             } else if (property instanceof String) {
 1517  0
                 Model.getPump().removeModelEventListener(this, listener[0],
 1518  
                         (String) property);
 1519  
             } else {
 1520  0
                 throw new RuntimeException(
 1521  
                         "Internal error in removeAllElementListeners");
 1522  
             }
 1523  0
         }
 1524  0
         listeners.removeAll(listenerSet);
 1525  0
     }
 1526  
 
 1527  
     private void addElementListeners(Set<Object[]> listenerSet) {
 1528  0
         for (Object[] listener : listenerSet) {
 1529  0
             Object property = listener[1];
 1530  0
             if (property == null) {
 1531  0
                 addElementListener(listener[0]);
 1532  0
             } else if (property instanceof String[]) {
 1533  0
                 addElementListener(listener[0], (String[]) property);
 1534  0
             } else if (property instanceof String) {
 1535  0
                 addElementListener(listener[0], (String) property);
 1536  
             } else {
 1537  0
                 throw new RuntimeException(
 1538  
                         "Internal error in addElementListeners");
 1539  
             }
 1540  0
         }
 1541  0
     }
 1542  
 
 1543  
     /**
 1544  
      * Update the set of registered listeners to match the given set using 
 1545  
      * a minimal update strategy to remove unneeded listeners and add new 
 1546  
      * listeners.
 1547  
      * 
 1548  
      * @param listenerSet a set of arrays containing a tuple of a UML element
 1549  
      * to be listened to and a set of property to be listened for.  
 1550  
      */
 1551  
     protected void updateElementListeners(Set<Object[]> listenerSet) {
 1552  0
         Set<Object[]> removes = new HashSet<Object[]>(listeners);
 1553  0
         removes.removeAll(listenerSet);
 1554  0
         removeElementListeners(removes);
 1555  
         
 1556  0
         Set<Object[]> adds = new HashSet<Object[]>(listenerSet);
 1557  0
         adds.removeAll(listeners);
 1558  0
         addElementListeners(adds);
 1559  0
     }
 1560  
 
 1561  
     /**
 1562  
      * This optional method is not implemented.  It will throw an
 1563  
      * {@link UnsupportedOperationException} if used. Figs are 
 1564  
      * added to a GraphModel which is, in turn, owned by a project.
 1565  
      * @param project the project
 1566  
      * @deprecated
 1567  
      */
 1568  
     @SuppressWarnings("deprecation")
 1569  
     @Deprecated
 1570  
     public void setProject(Project project) {
 1571  0
         throw new UnsupportedOperationException();
 1572  
     }
 1573  
     
 1574  
     /**
 1575  
      * @deprecated for 0.27.2 by tfmorris.  Implementations should have all
 1576  
      * the information that they require in the DiagramSettings object.
 1577  
      * 
 1578  
      * @return the owning project
 1579  
      * @see org.argouml.uml.diagram.ui.ArgoFig#getProject()
 1580  
      */
 1581  
     @Deprecated
 1582  
     public Project getProject() {
 1583  0
         return ArgoFigUtil.getProject(this);
 1584  
     }
 1585  
     
 1586  
     /**
 1587  
      * Handles diagram font changing.
 1588  
      * 
 1589  
      * @param e the event
 1590  
      * @see org.argouml.application.events.ArgoDiagramAppearanceEventListener#diagramFontChanged(org.argouml.application.events.ArgoDiagramAppearanceEvent)
 1591  
      * @deprecated for 0.27.2 by tfmorris. Global rendering changes are now
 1592  
      *             managed at the diagram level.
 1593  
      */
 1594  
     @Deprecated
 1595  
     public void diagramFontChanged(ArgoDiagramAppearanceEvent e) {
 1596  0
         updateFont();
 1597  0
         calcBounds(); //TODO: Does this help?
 1598  0
         redraw();
 1599  0
     }
 1600  
     
 1601  
     /**
 1602  
      * This function should, for all FigTexts, 
 1603  
      * recalculate the font-style (plain, bold, italic, bold/italic),
 1604  
      * and apply it by calling FigText.setFont().
 1605  
      */
 1606  
     protected void updateFont() {
 1607  0
         int style = getNameFigFontStyle();
 1608  0
         Font f = getSettings().getFont(style);
 1609  0
         nameFig.setFont(f);
 1610  0
         deepUpdateFont(this);
 1611  0
     }
 1612  
 
 1613  
     /**
 1614  
      * Determines the font style based on the UML model. 
 1615  
      * Overrule this in Figs that have to show bold or italic based on the 
 1616  
      * UML model they represent. 
 1617  
      * E.g. abstract classes show their name in italic.
 1618  
      * 
 1619  
      * @return the font style for the nameFig.
 1620  
      */
 1621  
     protected int getNameFigFontStyle() {
 1622  0
         return Font.PLAIN;
 1623  
     }
 1624  
 
 1625  
     private void deepUpdateFont(FigEdge fe) {
 1626  0
         Font f = getSettings().getFont(Font.PLAIN);
 1627  0
         for (Object pathFig : fe.getPathItemFigs()) {
 1628  0
             deepUpdateFontRecursive(f, pathFig);
 1629  
         }
 1630  0
         fe.calcBounds();
 1631  0
     }
 1632  
 
 1633  
     /**
 1634  
      * Changes the font for all Figs contained in the given FigGroup. <p>
 1635  
      * 
 1636  
      *  TODO: In fact, there is a design error in this method:
 1637  
      *  E.g. for a class, if the name is Italic since the class is abstract,
 1638  
      *  then the classes features should be in Plain font.
 1639  
      *  This problem can be fixed by implementing 
 1640  
      *  the updateFont() method in all subclasses.
 1641  
      *  
 1642  
      * @param fg the FigGroup to change the font of.
 1643  
      */
 1644  
     private void deepUpdateFontRecursive(Font f, Object pathFig) {
 1645  0
         if (pathFig instanceof ArgoFigText) {
 1646  0
             ((ArgoFigText) pathFig).updateFont();
 1647  0
         } else if (pathFig instanceof FigText) {
 1648  0
             ((FigText) pathFig).setFont(f);
 1649  0
         } else if (pathFig instanceof FigGroup) {
 1650  0
             for (Object fge : ((FigGroup) pathFig).getFigs()) {
 1651  0
                 deepUpdateFontRecursive(f, fge);
 1652  
             }
 1653  
         }
 1654  0
     }
 1655  
     
 1656  
 
 1657  
     public DiagramSettings getSettings() {
 1658  
         // TODO: This is a temporary crutch to use until all Figs are updated
 1659  
         // to use the constructor that accepts a DiagramSettings object
 1660  0
         if (settings == null) {
 1661  0
             LOG.debug("Falling back to project-wide settings");
 1662  0
             Project p = getProject();
 1663  0
             if (p != null) {
 1664  0
                 return p.getProjectSettings().getDefaultDiagramSettings();
 1665  
             }
 1666  
         }
 1667  0
         return settings;
 1668  
     }
 1669  
     
 1670  
     public void setSettings(DiagramSettings renderSettings) {
 1671  0
         settings = renderSettings;
 1672  0
         renderingChanged();
 1673  0
     }
 1674  
     
 1675  
     /**
 1676  
      * @return the current notation settings
 1677  
      */
 1678  
     protected NotationSettings getNotationSettings() {
 1679  0
         return getSettings().getNotationSettings();
 1680  
     }
 1681  
     
 1682  
 //    public void setLineWidth(int w) {
 1683  
 //        super.setLineWidth(w);
 1684  
 //    }
 1685  
     
 1686  
     public void setLineColor(Color c) {
 1687  0
         super.setLineColor(c);
 1688  0
     }
 1689  
     
 1690  
     public void setFig(Fig f) {
 1691  0
         super.setFig(f);
 1692  
         // GEF sets a different Fig than the one that we had at construction
 1693  
         // time, so we need to set its color and width
 1694  0
         f.setLineColor(getLineColor());
 1695  0
         f.setLineWidth(getLineWidth());
 1696  0
     }
 1697  
     
 1698  
 
 1699  
     /**
 1700  
      * Setting the owner of the Fig must be done in the constructor and not
 1701  
      * changed afterwards for all ArgoUML figs.
 1702  
      * 
 1703  
      * @param owner owning UML element
 1704  
      * @see org.tigris.gef.presentation.Fig#setOwner(java.lang.Object)
 1705  
      * @throws UnsupportedOperationException
 1706  
      * @deprecated for 0.27.3 by tfmorris. Set owner in constructor. This method
 1707  
      *             is implemented in GEF, so we'll leave this implementation
 1708  
      *             here to block any attempts to use it within ArgoUML.
 1709  
      */
 1710  
     @SuppressWarnings("deprecation")
 1711  
     @Deprecated
 1712  
     public void setOwner(Object owner) {
 1713  0
         if (owner != getOwner()) {
 1714  0
             throw new UnsupportedOperationException(
 1715  
                     "Owner must be set in constructor and left unchanged");
 1716  
         }
 1717  0
     }
 1718  
     
 1719  
     /**
 1720  
      * We override GEF completely here (no call to super method).
 1721  
      * Code is unfortunately copied from GEF to avoid multiple calls
 1722  
      * to calcBounds()
 1723  
      * 
 1724  
      * @see org.tigris.gef.presentation.FigEdgePoly#computeRouteImpl()
 1725  
      */
 1726  
     public void computeRouteImpl() {
 1727  
         
 1728  0
         Fig sourcePortFig = getSourcePortFig();
 1729  0
         Fig destPortFig = getDestPortFig();
 1730  
 
 1731  0
         if (sourcePortFig instanceof FigNodeModelElement) {
 1732  0
             sourcePortFig = ((FigNodeModelElement) sourcePortFig).getBigPort();
 1733  
         }
 1734  
         
 1735  0
         if (destPortFig instanceof FigNodeModelElement) {
 1736  0
             destPortFig = ((FigNodeModelElement) destPortFig).getBigPort();
 1737  
         }
 1738  
         
 1739  0
         if (!(sourcePortFig instanceof FigCircle)
 1740  
                 || !(destPortFig instanceof FigCircle)) {
 1741  
             // If this is not a circle to circle edge we default to let GEF
 1742  
             // calculate edge route.
 1743  0
             super.computeRouteImpl();
 1744  
         } else {
 1745  
             // If the edge is from a circle to a circle (e.g. between use
 1746  
             // cases) we have our own implementation overriding GEF. Which
 1747  
             // attempts to keep the edges perpendicular if the edge is already
 1748  
             // close to perpendicular.
 1749  
         
 1750  0
             if (!_initiallyLaidOut) {
 1751  0
                 layoutEdge();
 1752  0
                 _initiallyLaidOut = true;
 1753  
             }
 1754  0
             FigPoly p = ((FigPoly) getFig());
 1755  
     
 1756  
             
 1757  
             
 1758  0
             Point srcPt = sourcePortFig.getCenter();
 1759  0
             Point dstPt = destPortFig.getCenter();
 1760  
     
 1761  0
             if (_useNearest) {
 1762  0
                 if (p.getNumPoints() == 2) {
 1763  
                     // ? two iterations of refinement, maybe should be a for-loop
 1764  0
                     srcPt = sourcePortFig.connectionPoint(p.getPoint(1));
 1765  0
                     dstPt = destPortFig.connectionPoint(p
 1766  
                             .getPoint(p.getNumPoints() - 2));
 1767  0
                     srcPt = sourcePortFig.connectionPoint(dstPt);
 1768  0
                     dstPt = destPortFig.connectionPoint(srcPt);
 1769  
                     
 1770  
                     // If the line angle is less than 3 degrees then snap the line
 1771  
                     // straight
 1772  0
                     final int delta = 3;
 1773  0
                     double angle = Geometry.segmentAngle(srcPt, dstPt);
 1774  0
                     double mod = angle % 90;
 1775  0
                     final boolean snapStraight = (mod != 0 && (mod < delta || mod > 90 - delta));
 1776  
     
 1777  0
                     if (snapStraight) {
 1778  0
                         int newX = (srcPt.x + dstPt.x) / 2;
 1779  0
                         int newY = (srcPt.y + dstPt.y) / 2;
 1780  0
                         if (newX < getSourcePortFig().getX() + getSourcePortFig().getWidth()
 1781  
                                 && newX >= getSourcePortFig().getX()) {
 1782  0
                             srcPt.x = newX;
 1783  0
                             dstPt.x = newX;
 1784  0
                         } else if (newY >= getSourcePortFig().getY()
 1785  
                                 && newY < getSourcePortFig().getY() + getSourcePortFig().getHeight()) {
 1786  0
                             srcPt.y = newY;
 1787  0
                             dstPt.y = newY;
 1788  
                         }
 1789  
                     }
 1790  0
                 } else {
 1791  0
                     srcPt = sourcePortFig.connectionPoint(p.getPoint(1));
 1792  0
                     dstPt = destPortFig.connectionPoint(p
 1793  
                             .getPoint(p.getNumPoints() - 2));
 1794  
                 }
 1795  
             }
 1796  
     
 1797  0
             setEndPoints(srcPt, dstPt);
 1798  0
             calcBounds();
 1799  
         }
 1800  0
     } /* end computeRoute */
 1801  
     
 1802  
     public void notationRenderingChanged(NotationProvider np, String rendering) {
 1803  0
         if (notationProviderName == np) {
 1804  0
             nameFig.setText(rendering);
 1805  0
             damage();
 1806  
         }
 1807  0
     }
 1808  
 
 1809  
     public NotationSettings getNotationSettings(NotationProvider np) {
 1810  0
         if (notationProviderName == np) {
 1811  0
             return getNotationSettings();
 1812  
         }
 1813  0
         return null;
 1814  
     }
 1815  
 
 1816  
     public Object getOwner(NotationProvider np) {
 1817  0
         if (notationProviderName == np) {
 1818  0
             return getOwner();
 1819  
         }
 1820  0
         return null;
 1821  
     }
 1822  
 }