Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
FigEdgeModelElement |
|
| 2.7938144329896906;2.794 | ||||
FigEdgeModelElement$1 |
|
| 2.7938144329896906;2.794 | ||||
FigEdgeModelElement$2 |
|
| 2.7938144329896906;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 | } |