Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
FigNodeModelElement |
|
| 3.124031007751938;3.124 | ||||
FigNodeModelElement$1 |
|
| 3.124031007751938;3.124 | ||||
FigNodeModelElement$SelectionDefaultClarifiers |
|
| 3.124031007751938;3.124 |
1 | /* $Id: FigNodeModelElement.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.Dimension; | |
44 | import java.awt.Font; | |
45 | import java.awt.Graphics; | |
46 | import java.awt.Image; | |
47 | import java.awt.Rectangle; | |
48 | import java.awt.event.InputEvent; | |
49 | import java.awt.event.KeyEvent; | |
50 | import java.awt.event.KeyListener; | |
51 | import java.awt.event.MouseEvent; | |
52 | import java.awt.event.MouseListener; | |
53 | import java.beans.PropertyChangeEvent; | |
54 | import java.beans.PropertyChangeListener; | |
55 | import java.beans.PropertyVetoException; | |
56 | import java.beans.VetoableChangeListener; | |
57 | import java.util.ArrayList; | |
58 | import java.util.Collection; | |
59 | import java.util.HashSet; | |
60 | import java.util.Iterator; | |
61 | import java.util.List; | |
62 | import java.util.Set; | |
63 | import java.util.Vector; | |
64 | ||
65 | import javax.swing.Action; | |
66 | import javax.swing.Icon; | |
67 | import javax.swing.JMenu; | |
68 | import javax.swing.JSeparator; | |
69 | import javax.swing.SwingUtilities; | |
70 | ||
71 | import org.apache.log4j.Logger; | |
72 | import org.argouml.application.events.ArgoDiagramAppearanceEvent; | |
73 | import org.argouml.application.events.ArgoDiagramAppearanceEventListener; | |
74 | import org.argouml.application.events.ArgoEventPump; | |
75 | import org.argouml.application.events.ArgoEventTypes; | |
76 | import org.argouml.application.events.ArgoHelpEvent; | |
77 | import org.argouml.application.events.ArgoNotationEvent; | |
78 | import org.argouml.application.events.ArgoNotationEventListener; | |
79 | import org.argouml.cognitive.Designer; | |
80 | import org.argouml.cognitive.Highlightable; | |
81 | import org.argouml.cognitive.ToDoItem; | |
82 | import org.argouml.cognitive.ToDoList; | |
83 | import org.argouml.cognitive.ui.ActionGoToCritique; | |
84 | import org.argouml.i18n.Translator; | |
85 | import org.argouml.kernel.DelayedChangeNotify; | |
86 | import org.argouml.kernel.DelayedVChangeListener; | |
87 | import org.argouml.kernel.Owned; | |
88 | import org.argouml.kernel.Project; | |
89 | import org.argouml.model.AssociationChangeEvent; | |
90 | import org.argouml.model.AttributeChangeEvent; | |
91 | import org.argouml.model.DeleteInstanceEvent; | |
92 | import org.argouml.model.DiElement; | |
93 | import org.argouml.model.InvalidElementException; | |
94 | import org.argouml.model.Model; | |
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.profile.FigNodeStrategy; | |
103 | import org.argouml.ui.ArgoJMenu; | |
104 | import org.argouml.ui.Clarifier; | |
105 | import org.argouml.ui.ContextActionFactoryManager; | |
106 | import org.argouml.ui.ProjectActions; | |
107 | import org.argouml.ui.UndoableAction; | |
108 | import org.argouml.ui.targetmanager.TargetManager; | |
109 | import org.argouml.uml.StereotypeUtility; | |
110 | import org.argouml.uml.diagram.ArgoDiagram; | |
111 | import org.argouml.uml.diagram.DiagramAppearance; | |
112 | import org.argouml.uml.diagram.DiagramElement; | |
113 | import org.argouml.uml.diagram.DiagramSettings; | |
114 | import org.argouml.uml.diagram.DiagramSettings.StereotypeStyle; | |
115 | import org.argouml.uml.diagram.PathContainer; | |
116 | import org.argouml.uml.ui.ActionDeleteModelElements; | |
117 | import org.argouml.util.IItemUID; | |
118 | import org.argouml.util.ItemUID; | |
119 | import org.tigris.gef.base.Diagram; | |
120 | import org.tigris.gef.base.Globals; | |
121 | import org.tigris.gef.base.Layer; | |
122 | import org.tigris.gef.base.LayerPerspective; | |
123 | import org.tigris.gef.base.Selection; | |
124 | import org.tigris.gef.graph.MutableGraphSupport; | |
125 | import org.tigris.gef.presentation.Fig; | |
126 | import org.tigris.gef.presentation.FigGroup; | |
127 | import org.tigris.gef.presentation.FigImage; | |
128 | import org.tigris.gef.presentation.FigNode; | |
129 | import org.tigris.gef.presentation.FigRect; | |
130 | import org.tigris.gef.presentation.FigText; | |
131 | ||
132 | /** | |
133 | * Abstract class to display diagram icons for UML ModelElements that | |
134 | * look like nodes and that have editable names and can be | |
135 | * resized. | |
136 | * <p> | |
137 | * NOTE: This will drop the ArgoNotationEventListener and | |
138 | * ArgoDiagramAppearanceEventListener interfaces in the next release. | |
139 | * The corresponding methods have been marked as deprecated. | |
140 | * | |
141 | * @author abonner | |
142 | */ | |
143 | 119 | public abstract class FigNodeModelElement |
144 | extends FigNode | |
145 | implements | |
146 | VetoableChangeListener, | |
147 | DelayedVChangeListener, | |
148 | MouseListener, | |
149 | KeyListener, | |
150 | PropertyChangeListener, | |
151 | PathContainer, | |
152 | ArgoDiagramAppearanceEventListener, | |
153 | ArgoNotationEventListener, | |
154 | NotationRenderer, | |
155 | Highlightable, | |
156 | IItemUID, | |
157 | Clarifiable, | |
158 | ArgoFig, | |
159 | StereotypeStyled, | |
160 | DiagramElement, | |
161 | Owned { | |
162 | ||
163 | ||
164 | 119 | private static final Logger LOG = |
165 | Logger.getLogger(FigNodeModelElement.class); | |
166 | ||
167 | // TODO: There are lots and LOTS of magic numbers used in calculating | |
168 | // positions and sizes. Any time you see Figs being placed at 10,10 use | |
169 | // these constants instead. If you can reliably interpret calculations, | |
170 | // you can factor them out of there as well. Add additional constants | |
171 | // as needed to express other common factors - tfm 20081201 | |
172 | ||
173 | /** | |
174 | * Default width for a node fig. | |
175 | * Used to be 60 (up to V0.20), later (from V0.22) it was 90. | |
176 | * Now 64 to align to grid better. | |
177 | */ | |
178 | protected static final int WIDTH = 64; | |
179 | ||
180 | /** | |
181 | * The default minimum height of the name fig, computed to allow room for | |
182 | * the Critics "clarifiers" (red squiggly line) with the default font. This | |
183 | * should really go away and be managed internally to the name figs and | |
184 | * fetched through getMinimumSize(). The final height can change based on | |
185 | * the font selected. | |
186 | */ | |
187 | protected static final int NAME_FIG_HEIGHT = 21; | |
188 | ||
189 | /** | |
190 | * Padding to be used above and below the name. | |
191 | */ | |
192 | protected static final int NAME_V_PADDING = 2; | |
193 | ||
194 | private DiElement diElement; | |
195 | ||
196 | private NotationProvider notationProviderName; | |
197 | ||
198 | /** | |
199 | * True if an instance is allowed to be | |
200 | * invisible. This is currently only set true by FigEdgePort. | |
201 | * TODO: FigEdgePort should be removed from the FigNodeModelElement | |
202 | * hierarchy and so the need for this removed. | |
203 | */ | |
204 | 1071 | protected boolean invisibleAllowed = false; |
205 | ||
206 | ||
207 | /** | |
208 | * Needed for loading. Warning: if false, a too small size might look bad! | |
209 | */ | |
210 | 1071 | private boolean checkSize = true; |
211 | ||
212 | /** | |
213 | * Offset from the end of the set of popup actions at which new items | |
214 | * should be inserted by concrete figures. | |
215 | * @See {@link #getPopUpActions(MouseEvent)} | |
216 | */ | |
217 | private static int popupAddOffset; | |
218 | ||
219 | /** | |
220 | * Used for #buildModifierPopUp(). | |
221 | */ | |
222 | protected static final int ROOT = 1; | |
223 | ||
224 | /** | |
225 | * Used for #buildModifierPopUp(). | |
226 | */ | |
227 | protected static final int ABSTRACT = 2; | |
228 | ||
229 | /** | |
230 | * Used for #buildModifierPopUp(). | |
231 | */ | |
232 | protected static final int LEAF = 4; | |
233 | ||
234 | /** | |
235 | * Used for #buildModifierPopUp(). | |
236 | */ | |
237 | protected static final int ACTIVE = 8; | |
238 | ||
239 | private Fig bigPort; | |
240 | ||
241 | /** | |
242 | * use getNameFig() and setNameFig() to access the Figs. | |
243 | * Use getName() and setName() to just change the text. | |
244 | */ | |
245 | private FigText nameFig; | |
246 | ||
247 | /** | |
248 | * use getter/setter | |
249 | * getStereotypeFig() and setStereoTypeFig() to access the Figs. | |
250 | * Use getStereotype() and setStereotype() to change stereotype | |
251 | * text. | |
252 | */ | |
253 | private FigStereotypesGroup stereotypeFig; | |
254 | ||
255 | /** | |
256 | * The <code>FigProfileIcon</code> being currently displayed | |
257 | */ | |
258 | private FigProfileIcon stereotypeFigProfileIcon; | |
259 | ||
260 | /** | |
261 | * Contains the figs of the floating stereotypes when viewed in | |
262 | * <code>SmallIcon</code> mode. | |
263 | */ | |
264 | 1071 | private List<Fig> floatingStereotypes = new ArrayList<Fig>(); |
265 | ||
266 | /** | |
267 | * The current stereotype view, defaults to "textual". | |
268 | * | |
269 | * @see DiagramAppearance#STEREOTYPE_VIEW_TEXTUAL | |
270 | * @see DiagramAppearance#STEREOTYPE_VIEW_SMALL_ICON | |
271 | * @see DiagramAppearance#STEREOTYPE_VIEW_BIG_ICON | |
272 | */ | |
273 | 1071 | private DiagramSettings.StereotypeStyle stereotypeStyle = |
274 | DiagramSettings.StereotypeStyle.TEXTUAL; | |
275 | ||
276 | /** | |
277 | * The width of the profile icons when viewed at the small icon mode. | |
278 | * The icon width is resized to <code>ICON_WIDTH</code> and the height is | |
279 | * set to a value that attempts to keep the original width/height | |
280 | * proportion. | |
281 | */ | |
282 | private static final int ICON_WIDTH = 16; | |
283 | ||
284 | /** | |
285 | * When stereotypes are shown in <code>BigIcon</code> mode the | |
286 | * <code>nameFig</code> is replaced by the one provided by the | |
287 | * <code>FigProfileIcon</code> | |
288 | * | |
289 | * @see FigProfileIcon | |
290 | */ | |
291 | private FigText originalNameFig; | |
292 | ||
293 | /** | |
294 | * EnclosedFigs are the Figs that are enclosed by this figure. Say that | |
295 | * it is a Package then these are the Classes, Interfaces, Packages etc | |
296 | * that are on this figure. This is not the same as the figures in the | |
297 | * FigGroup that this FigNodeModelElement "is", since these are the | |
298 | * figures that make up this high-level primitive figure. | |
299 | */ | |
300 | 1071 | private Vector<Fig> enclosedFigs = new Vector<Fig>(); |
301 | ||
302 | /** | |
303 | * The figure enclosing this figure such as a package surrounding a class. | |
304 | */ | |
305 | private Fig encloser; | |
306 | ||
307 | // TODO: Bobs says - what is the purpose of this flag? Please document. | |
308 | 1071 | private boolean readyToEdit = true; |
309 | ||
310 | private boolean suppressCalcBounds; | |
311 | private static boolean showBoldName; | |
312 | ||
313 | private ItemUID itemUid; | |
314 | ||
315 | /** | |
316 | * Set the removeFromDiagram to false if this node may not | |
317 | * be removed from the diagram. | |
318 | */ | |
319 | 1071 | private boolean removeFromDiagram = true; |
320 | ||
321 | ||
322 | /** | |
323 | * If the contains text to be edited by the user. | |
324 | */ | |
325 | 1071 | private boolean editable = true; |
326 | ||
327 | // TODO: A more strongly typed data structure could be used here. | |
328 | 1071 | private Set<Object[]> listeners = new HashSet<Object[]>(); |
329 | ||
330 | /** | |
331 | * Settings which affect rendering (color, font, line width, etc); | |
332 | */ | |
333 | private DiagramSettings settings; | |
334 | ||
335 | /** | |
336 | * The notation settings for this specific fig. We manage it separately | |
337 | * from DiagramSettings because it is more likely to change. | |
338 | */ | |
339 | private NotationSettings notationSettings; | |
340 | ||
341 | /** | |
342 | * Construct an unplaced Fig with no owner using the given | |
343 | * rendering settings. | |
344 | */ | |
345 | private void constructFigs() { | |
346 | // TODO: Why isn't this stuff managed by the nameFig itself? | |
347 | 1071 | nameFig.setFilled(true); |
348 | 1071 | nameFig.setText(placeString()); |
349 | 1071 | nameFig.setBotMargin(7); // make space for the clarifier |
350 | 1071 | nameFig.setRightMargin(4); // margin between text and border |
351 | 1071 | nameFig.setLeftMargin(4); |
352 | ||
353 | 1071 | readyToEdit = false; |
354 | ||
355 | 1071 | setShadowSize(getSettings().getDefaultShadowWidth()); |
356 | /* TODO: how to handle changes in shadowsize | |
357 | * from the project properties? */ | |
358 | ||
359 | 1071 | stereotypeStyle = getSettings().getDefaultStereotypeView(); |
360 | 1071 | } |
361 | ||
362 | /** | |
363 | * Construct a figure at a specific position for a given model element | |
364 | * with the given settings. This is the constructor used by the PGML | |
365 | * parser when loading a diagram from a file.<p> | |
366 | * | |
367 | * Beware: the width and height in the given Rectangle are currently ignored. | |
368 | * According issue 5604 this is a bug. | |
369 | * | |
370 | * @param element ModelElement associated with figure | |
371 | * @param bounds x & y are used to set position, width & height are ignored | |
372 | * @param renderSettings the rendering settings to use for the Fig | |
373 | */ | |
374 | protected FigNodeModelElement(Object element, Rectangle bounds, | |
375 | DiagramSettings renderSettings) { | |
376 | 1071 | super(); |
377 | 1071 | super.setOwner(element); |
378 | ||
379 | // TODO: We currently don't support per-fig settings for most stuff, so | |
380 | // we can just use the defaults that we were given. | |
381 | // settings = new DiagramSettings(renderSettings); | |
382 | 1071 | settings = renderSettings; |
383 | ||
384 | // Be careful here since subclasses could have overridden this with | |
385 | // the assumption that it wouldn't be called before the constructors | |
386 | // finished | |
387 | 1071 | super.setFillColor(FILL_COLOR); |
388 | 1071 | super.setLineColor(LINE_COLOR); |
389 | 1071 | super.setLineWidth(LINE_WIDTH); |
390 | 1071 | super.setTextColor(TEXT_COLOR); // Some subclasses will try to use this |
391 | ||
392 | /* | |
393 | * Notation settings are different since, we know that, at a minimum, | |
394 | * the isShowPath() setting can change because with implement | |
395 | * PathContainer, so we make sure that we have a private copy of the | |
396 | * notation settings. | |
397 | */ | |
398 | 1071 | notationSettings = new NotationSettings(settings.getNotationSettings()); |
399 | ||
400 | // this rectangle marks the whole modelelement figure; everything | |
401 | // is inside it: | |
402 | 1071 | bigPort = createBigPortFig(); |
403 | 1071 | nameFig = new FigNameWithAbstractAndBold(element, |
404 | new Rectangle(X0, Y0, WIDTH, NAME_FIG_HEIGHT), getSettings(), true); | |
405 | 1071 | stereotypeFig = createStereotypeFig(); |
406 | 1071 | constructFigs(); |
407 | ||
408 | // TODO: For a FigPool the element will be null. | |
409 | // When issue 5031 is resolved this constraint can be reinstated | |
410 | // if (element == null) { | |
411 | // throw new IllegalArgumentException("An owner must be supplied"); | |
412 | // } | |
413 | 1071 | if (element != null && !Model.getFacade().isAUMLElement(element)) { |
414 | 0 | throw new IllegalArgumentException( |
415 | "The owner must be a model element - got a " | |
416 | + element.getClass().getName()); | |
417 | } | |
418 | ||
419 | 1071 | nameFig.setText(placeString()); |
420 | ||
421 | 1071 | if (element != null) { |
422 | 0 | NotationName nn = Notation.findNotation(notationSettings.getNotationLanguage()); |
423 | 0 | notationProviderName = |
424 | NotationProviderFactory2.getInstance().getNotationProvider( | |
425 | getNotationProviderType(), element, this, nn); | |
426 | ||
427 | /* This next line presumes that the 1st fig with this owner | |
428 | * is the previous port - and consequently nullifies the owner | |
429 | * of this 1st fig. */ | |
430 | 0 | bindPort(element, bigPort); |
431 | ||
432 | // Add a listener for changes to any property | |
433 | 0 | addElementListener(element); |
434 | } | |
435 | ||
436 | 1071 | if (bounds != null) { |
437 | 0 | setLocation(bounds.x, bounds.y); |
438 | } | |
439 | ||
440 | // TODO: The following is carried over from setOwner, but probably | |
441 | // isn't needed | |
442 | // renderingChanged(); | |
443 | // It does the following (add as needed): | |
444 | // updateNameText(); | |
445 | // updateStereotypeText(); | |
446 | // updateStereotypeIcon(); | |
447 | // updateBounds(); | |
448 | // damage(); | |
449 | ||
450 | 1071 | readyToEdit = true; |
451 | 1071 | } |
452 | ||
453 | /** | |
454 | * Overrule this if a rectangle is not usable. | |
455 | * | |
456 | * @return the Fig to be used as bigPort | |
457 | */ | |
458 | protected Fig createBigPortFig() { | |
459 | 1071 | return new FigRect(X0, Y0, 0, 0, DEBUG_COLOR, DEBUG_COLOR); |
460 | } | |
461 | ||
462 | protected FigStereotypesGroup createStereotypeFig() { | |
463 | 0 | return new FigStereotypesGroup(getOwner(), |
464 | new Rectangle(X0, Y0, WIDTH, STEREOHEIGHT), settings); | |
465 | } | |
466 | ||
467 | ||
468 | ||
469 | /** | |
470 | * This is the final call at creation time of the Fig, i.e. here | |
471 | * it is put on a Diagram. | |
472 | * | |
473 | * @param lay the Layer (which has a 1..1 relation to the Diagram) | |
474 | * @see org.tigris.gef.presentation.Fig#setLayer(org.tigris.gef.base.Layer) | |
475 | */ | |
476 | @Override | |
477 | public void setLayer(Layer lay) { | |
478 | 0 | super.setLayer(lay); |
479 | 0 | determineDefaultPathVisible(); |
480 | 0 | } |
481 | ||
482 | ||
483 | /** | |
484 | * Clone this figure. After the base clone method has been called determine | |
485 | * which child figs of the clone represent the name, stereotype and port. | |
486 | * <p> | |
487 | * TODO: enclosedFigs, encloser and eventSenders may also need to be cloned. | |
488 | * | |
489 | * @see java.lang.Object#clone() | |
490 | * @return the cloned figure | |
491 | */ | |
492 | @Override | |
493 | public Object clone() { | |
494 | 0 | final FigNodeModelElement clone = (FigNodeModelElement) super.clone(); |
495 | ||
496 | 0 | final Iterator cloneIter = clone.getFigs().iterator(); |
497 | 0 | for (Object thisFig : getFigs()) { |
498 | 0 | final Fig cloneFig = (Fig) cloneIter.next(); |
499 | 0 | if (thisFig == getBigPort()) { |
500 | 0 | clone.setBigPort(cloneFig); |
501 | } | |
502 | 0 | if (thisFig == nameFig) { |
503 | 0 | clone.nameFig = (FigSingleLineText) thisFig; |
504 | /* TODO: MVW: I think this has to be: | |
505 | * clone.nameFig = (FigSingleLineText) cloneFig; | |
506 | * but have not the means to investigate, | |
507 | * since this code is not yet used. | |
508 | * Enable the menu-items for Copy/Paste to test... | |
509 | * BTW: In some other FigNodeModelElement | |
510 | * classes I see the same mistake. */ | |
511 | } | |
512 | 0 | if (thisFig == getStereotypeFig()) { |
513 | 0 | clone.stereotypeFig = (FigStereotypesGroup) thisFig; |
514 | /* Idem here: | |
515 | * clone.stereotypeFig = (FigStereotypesGroup) cloneFig; */ | |
516 | } | |
517 | 0 | } |
518 | 0 | return clone; |
519 | } | |
520 | ||
521 | /** | |
522 | * Default Reply text to be shown while placing node in diagram. | |
523 | * Overrule this when the text is not "new [UMLClassName]". | |
524 | * | |
525 | * @return the text to be shown while placing node in diagram | |
526 | */ | |
527 | public String placeString() { | |
528 | 2142 | if (Model.getFacade().isAModelElement(getOwner())) { |
529 | 0 | String placeString = Model.getFacade().getName(getOwner()); |
530 | 0 | if (placeString == null) { |
531 | 0 | placeString = |
532 | // TODO: I18N | |
533 | "new " + Model.getFacade().getUMLClassName(getOwner()); | |
534 | } | |
535 | 0 | return placeString; |
536 | } | |
537 | 2142 | return ""; |
538 | } | |
539 | ||
540 | /** | |
541 | * @param id UID | |
542 | */ | |
543 | public void setItemUID(ItemUID id) { | |
544 | 0 | itemUid = id; |
545 | 0 | } |
546 | ||
547 | /** | |
548 | * @return UID | |
549 | */ | |
550 | public ItemUID getItemUID() { | |
551 | 0 | return itemUid; |
552 | } | |
553 | ||
554 | /** | |
555 | * Get the Fig that displays the model element name. | |
556 | * | |
557 | * @return the name Fig | |
558 | */ | |
559 | protected FigText getNameFig() { | |
560 | 2142 | return nameFig; |
561 | } | |
562 | ||
563 | /** | |
564 | * Get the Rectangle in which the model elements name is displayed | |
565 | * @return bounding box for name | |
566 | */ | |
567 | public Rectangle getNameBounds() { | |
568 | 0 | return nameFig.getBounds(); |
569 | } | |
570 | ||
571 | /** | |
572 | * Set the Fig that displays the model element name. | |
573 | * | |
574 | * @param fig the name Fig | |
575 | */ | |
576 | protected void setNameFig(FigText fig) { | |
577 | 0 | nameFig = fig; |
578 | 0 | if (nameFig != null) { |
579 | 0 | updateFont(); |
580 | } | |
581 | 0 | } |
582 | ||
583 | /** | |
584 | * Get the name of the model element this Fig represents. | |
585 | * | |
586 | * @return the name of the model element | |
587 | */ | |
588 | public String getName() { | |
589 | 0 | return nameFig.getText(); |
590 | } | |
591 | ||
592 | /** | |
593 | * Change the name of the model element this Fig represents. | |
594 | * | |
595 | * @param n the name of the model element | |
596 | */ | |
597 | public void setName(String n) { | |
598 | 0 | nameFig.setText(n); |
599 | 0 | } |
600 | ||
601 | /** | |
602 | * This method returns a Vector of one of these 4 types: | |
603 | * AbstractAction, JMenu, JMenuItem, JSeparator. | |
604 | * {@inheritDoc} | |
605 | */ | |
606 | @Override | |
607 | public Vector getPopUpActions(MouseEvent me) { | |
608 | 0 | ActionList popUpActions = |
609 | new ActionList(super.getPopUpActions(me), isReadOnly()); | |
610 | ||
611 | 0 | final List<Action> modulesActions = |
612 | ContextActionFactoryManager.getContextPopupActions(); | |
613 | ||
614 | 0 | for (Action a : modulesActions) { |
615 | 0 | if (a instanceof List) { |
616 | 0 | JMenu m = new JMenu((Action) a); |
617 | 0 | popUpActions.add(m); |
618 | 0 | for (Action subAction : (List<Action>) a) { |
619 | 0 | m.add(subAction); |
620 | } | |
621 | 0 | } else { |
622 | 0 | popUpActions.add(a); |
623 | } | |
624 | } | |
625 | ||
626 | // Show ... | |
627 | 0 | ArgoJMenu show = buildShowPopUp(); |
628 | 0 | if (show.getMenuComponentCount() > 0) { |
629 | 0 | popUpActions.add(show); |
630 | } | |
631 | ||
632 | // popupAddOffset should be equal to the number of items added here: | |
633 | 0 | popUpActions.add(new JSeparator()); |
634 | 0 | popupAddOffset = 1; |
635 | 0 | if (removeFromDiagram) { |
636 | 0 | popUpActions.add( |
637 | ProjectActions.getInstance().getRemoveFromDiagramAction()); | |
638 | 0 | popupAddOffset++; |
639 | } | |
640 | ||
641 | 0 | if (!isReadOnly()) { |
642 | 0 | popUpActions.add(new ActionDeleteModelElements()); |
643 | 0 | popupAddOffset++; |
644 | } | |
645 | ||
646 | /* Check if multiple items are selected: */ | |
647 | 0 | if (TargetManager.getInstance().getTargets().size() == 1) { |
648 | ||
649 | // TODO: Having Critics actions here introduces an unnecessary | |
650 | // dependency on the Critics subsystem. Have it register its | |
651 | // desired actions using an extension mechanism - tfm | |
652 | 0 | ToDoList tdList = Designer.theDesigner().getToDoList(); |
653 | 0 | List<ToDoItem> items = tdList.elementListForOffender(getOwner()); |
654 | 0 | if (items != null && items.size() > 0) { |
655 | // TODO: This creates a dependency on the Critics subsystem. | |
656 | // We need a generic way for modules (including our internal | |
657 | // subsystems) to request addition of actions to the popup | |
658 | // menu. - tfm 20080430 | |
659 | 0 | ArgoJMenu critiques = new ArgoJMenu("menu.popup.critiques"); |
660 | 0 | ToDoItem itemUnderMouse = hitClarifier(me.getX(), me.getY()); |
661 | 0 | if (itemUnderMouse != null) { |
662 | 0 | critiques.add(new ActionGoToCritique(itemUnderMouse)); |
663 | 0 | critiques.addSeparator(); |
664 | } | |
665 | 0 | for (ToDoItem item : items) { |
666 | 0 | if (item != itemUnderMouse) { |
667 | 0 | critiques.add(new ActionGoToCritique(item)); |
668 | } | |
669 | } | |
670 | 0 | popUpActions.add(0, new JSeparator()); |
671 | 0 | popUpActions.add(0, critiques); |
672 | } | |
673 | } | |
674 | ||
675 | // Add stereotypes submenu | |
676 | 0 | Collection<Object> elements = new ArrayList<Object>(); |
677 | 0 | Object owner = getOwner(); |
678 | 0 | if (owner != null) { |
679 | 0 | elements.add(owner); |
680 | } | |
681 | 0 | for (Object o : TargetManager.getInstance().getTargets()) { |
682 | 0 | Object element = null; |
683 | 0 | if (Model.getFacade().isAUMLElement(o)) { |
684 | 0 | element = o; |
685 | 0 | } else if (o instanceof Fig) { |
686 | 0 | element = ((Fig) o).getOwner(); |
687 | } | |
688 | 0 | if (element != null && element != owner) { |
689 | 0 | elements.add(element); |
690 | } | |
691 | 0 | } |
692 | 0 | final Action[] stereoActions = |
693 | StereotypeUtility.getApplyStereotypeActions(elements); | |
694 | 0 | if (stereoActions != null) { |
695 | 0 | popUpActions.add(0, new JSeparator()); |
696 | 0 | final ArgoJMenu stereotypes = |
697 | new ArgoJMenu("menu.popup.apply-stereotypes"); | |
698 | 0 | for (Action action : stereoActions) { |
699 | 0 | stereotypes.addCheckItem(action); |
700 | } | |
701 | 0 | popUpActions.add(0, stereotypes); |
702 | } | |
703 | ||
704 | 0 | if (TargetManager.getInstance().getTargets().size() == 1) { |
705 | // add stereotype view submenu | |
706 | 0 | ArgoJMenu stereotypesView = |
707 | new ArgoJMenu("menu.popup.stereotype-view"); | |
708 | ||
709 | // TODO: There are cyclic dependencies between ActionStereotypeView* | |
710 | // and FigNodeModelElement. Register these actions opaquely since | |
711 | // we don't what they are. - tfm | |
712 | 0 | stereotypesView.addRadioItem(new ActionStereotypeViewTextual(this)); |
713 | 0 | stereotypesView.addRadioItem(new ActionStereotypeViewBigIcon(this)); |
714 | 0 | stereotypesView.addRadioItem( |
715 | new ActionStereotypeViewSmallIcon(this)); | |
716 | ||
717 | 0 | popUpActions.add(0, stereotypesView); |
718 | } | |
719 | ||
720 | 0 | return popUpActions; |
721 | } | |
722 | ||
723 | protected ArgoJMenu buildShowPopUp() { | |
724 | 0 | ArgoJMenu showMenu = new ArgoJMenu("menu.popup.show"); |
725 | ||
726 | 0 | for (UndoableAction ua : ActionSetPath.getActions()) { |
727 | 0 | showMenu.add(ua); |
728 | } | |
729 | 0 | return showMenu; |
730 | } | |
731 | ||
732 | /** | |
733 | * @return the pop-up menu item for Visibility | |
734 | */ | |
735 | protected Object buildVisibilityPopUp() { | |
736 | 0 | ArgoJMenu visibilityMenu = new ArgoJMenu("menu.popup.visibility"); |
737 | ||
738 | 0 | visibilityMenu.addRadioItem(new ActionVisibilityPublic(getOwner())); |
739 | 0 | visibilityMenu.addRadioItem(new ActionVisibilityPrivate(getOwner())); |
740 | 0 | visibilityMenu.addRadioItem(new ActionVisibilityProtected(getOwner())); |
741 | 0 | visibilityMenu.addRadioItem(new ActionVisibilityPackage(getOwner())); |
742 | ||
743 | 0 | return visibilityMenu; |
744 | } | |
745 | ||
746 | /** | |
747 | * Build a pop-up menu item for the various modifiers.<p> | |
748 | * | |
749 | * This function is designed to be easily extendable with new items. | |
750 | * | |
751 | * @param items bitwise OR of the items: ROOT, ABSTRACT, LEAF, ACTIVE. | |
752 | * @return the menu item | |
753 | */ | |
754 | protected Object buildModifierPopUp(int items) { | |
755 | 0 | ArgoJMenu modifierMenu = new ArgoJMenu("menu.popup.modifiers"); |
756 | ||
757 | 0 | if ((items & ABSTRACT) > 0) { |
758 | 0 | modifierMenu.addCheckItem(new ActionModifierAbstract(getOwner())); |
759 | } | |
760 | 0 | if ((items & LEAF) > 0) { |
761 | 0 | modifierMenu.addCheckItem(new ActionModifierLeaf(getOwner())); |
762 | } | |
763 | 0 | if ((items & ROOT) > 0) { |
764 | 0 | modifierMenu.addCheckItem(new ActionModifierRoot(getOwner())); |
765 | } | |
766 | 0 | if ((items & ACTIVE) > 0) { |
767 | 0 | modifierMenu.addCheckItem(new ActionModifierActive(getOwner())); |
768 | } | |
769 | ||
770 | 0 | return modifierMenu; |
771 | } | |
772 | ||
773 | /* | |
774 | * @see org.tigris.gef.presentation.Fig#getEnclosingFig() | |
775 | */ | |
776 | @Override | |
777 | public Fig getEnclosingFig() { | |
778 | 0 | return encloser; |
779 | } | |
780 | ||
781 | /* | |
782 | * Updates the modelelement container if the fig is moved in or | |
783 | * out another fig. If this fig doesn't have an enclosing fig | |
784 | * anymore, the namespace of the diagram will be the owning | |
785 | * modelelement. If this fig is moved inside another | |
786 | * FigNodeModelElement the owner of that fignodemodelelement will | |
787 | * be the owning modelelement. | |
788 | * | |
789 | * @see org.tigris.gef.presentation.FigNode#setEnclosingFig(org.tigris.gef.presentation.Fig) | |
790 | */ | |
791 | @Override | |
792 | public void setEnclosingFig(Fig newEncloser) { | |
793 | 0 | Fig oldEncloser = encloser; |
794 | ||
795 | 0 | LayerPerspective layer = (LayerPerspective) getLayer(); |
796 | 0 | if (layer != null) { |
797 | 0 | ArgoDiagram diagram = (ArgoDiagram) layer.getDiagram(); |
798 | 0 | diagram.encloserChanged( |
799 | this, | |
800 | (FigNode) oldEncloser, | |
801 | (FigNode) newEncloser); | |
802 | } | |
803 | ||
804 | 0 | super.setEnclosingFig(newEncloser); |
805 | ||
806 | 0 | if (layer != null && newEncloser != oldEncloser) { |
807 | 0 | Diagram diagram = layer.getDiagram(); |
808 | 0 | if (diagram instanceof ArgoDiagram) { |
809 | 0 | ArgoDiagram umlDiagram = (ArgoDiagram) diagram; |
810 | // Set the namespace of the enclosed model element to the | |
811 | // namespace of the encloser. | |
812 | 0 | Object namespace = null; |
813 | 0 | if (newEncloser == null) { |
814 | // The node's been placed on the diagram | |
815 | 0 | umlDiagram.setModelElementNamespace(getOwner(), null); |
816 | } else { | |
817 | // The node's been placed within some Fig | |
818 | 0 | namespace = newEncloser.getOwner(); |
819 | 0 | if (Model.getFacade().isANamespace(namespace)) { |
820 | 0 | umlDiagram.setModelElementNamespace( |
821 | getOwner(), namespace); | |
822 | } | |
823 | } | |
824 | } | |
825 | ||
826 | 0 | if (encloser instanceof FigNodeModelElement) { |
827 | 0 | ((FigNodeModelElement) encloser).removeEnclosedFig(this); |
828 | } | |
829 | 0 | if (newEncloser instanceof FigNodeModelElement) { |
830 | 0 | ((FigNodeModelElement) newEncloser).addEnclosedFig(this); |
831 | } | |
832 | } | |
833 | 0 | encloser = newEncloser; |
834 | 0 | } |
835 | ||
836 | /** | |
837 | * Handle the case where this fig is moved into a Component. | |
838 | * | |
839 | * @param newEncloser the new encloser for this Fig | |
840 | */ | |
841 | protected void moveIntoComponent(Fig newEncloser) { | |
842 | 0 | final Object component = newEncloser.getOwner(); |
843 | 0 | final Object owner = getOwner(); |
844 | ||
845 | 0 | assert Model.getFacade().isAComponent(component); |
846 | 0 | assert Model.getFacade().isAUMLElement(owner); |
847 | ||
848 | 0 | final Collection er1 = Model.getFacade().getElementResidences(owner); |
849 | 0 | final Collection er2 = Model.getFacade().getResidentElements(component); |
850 | 0 | boolean found = false; |
851 | // Find all ElementResidences between the class and the component: | |
852 | 0 | final Collection<Object> common = new ArrayList<Object>(er1); |
853 | 0 | common.retainAll(er2); |
854 | 0 | for (Object elementResidence : common) { |
855 | 0 | if (!found) { |
856 | 0 | found = true; |
857 | // There is already a correct ElementResidence | |
858 | } else { | |
859 | // There were 2 ElementResidences .. strange case. | |
860 | 0 | Model.getUmlFactory().delete(elementResidence); |
861 | } | |
862 | } | |
863 | 0 | if (!found) { |
864 | // There was no ElementResidence yet, so let's create one: | |
865 | 0 | Model.getCoreFactory().buildElementResidence( |
866 | owner, component); | |
867 | } | |
868 | 0 | } |
869 | ||
870 | /** | |
871 | * Add a Fig that is enclosed by this figure. | |
872 | * | |
873 | * @param fig The fig to be added | |
874 | */ | |
875 | public void addEnclosedFig(Fig fig) { | |
876 | 0 | enclosedFigs.add(fig); |
877 | 0 | } |
878 | ||
879 | /** | |
880 | * Removes the given Fig from the list of enclosed Figs. | |
881 | * | |
882 | * @param fig The Fig to be removed | |
883 | */ | |
884 | public void removeEnclosedFig(Fig fig) { | |
885 | 0 | enclosedFigs.remove(fig); |
886 | 0 | } |
887 | ||
888 | /* | |
889 | * @see org.tigris.gef.presentation.Fig#getEnclosedFigs() | |
890 | */ | |
891 | @Override | |
892 | public Vector<Fig> getEnclosedFigs() { | |
893 | 0 | return enclosedFigs; |
894 | } | |
895 | ||
896 | ||
897 | /* | |
898 | * @see org.tigris.gef.presentation.Fig#makeSelection() | |
899 | */ | |
900 | @Override | |
901 | public Selection makeSelection() { | |
902 | 0 | return new SelectionDefaultClarifiers(this); |
903 | } | |
904 | ||
905 | /** | |
906 | * Displays visual indications of pending ToDoItems. | |
907 | * Please note that the list of advices (ToDoList) is not the same | |
908 | * as the list of element known by the FigNode (_figs). Therefore, | |
909 | * it is necessary to check if the graphic item exists before drawing | |
910 | * on it. See ClAttributeCompartment for an example. | |
911 | * @param g the graphics device | |
912 | * @see org.argouml.uml.cognitive.critics.ClAttributeCompartment | |
913 | */ | |
914 | public void paintClarifiers(Graphics g) { | |
915 | // TODO: Generalize extension and remove critic specific stuff | |
916 | 0 | int iconX = getX(); |
917 | 0 | int iconY = getY() - 10; |
918 | 0 | ToDoList tdList = Designer.theDesigner().getToDoList(); |
919 | 0 | List<ToDoItem> items = tdList.elementListForOffender(getOwner()); |
920 | 0 | for (ToDoItem item : items) { |
921 | 0 | Icon icon = item.getClarifier(); |
922 | 0 | if (icon instanceof Clarifier) { |
923 | 0 | ((Clarifier) icon).setFig(this); |
924 | 0 | ((Clarifier) icon).setToDoItem(item); |
925 | } | |
926 | 0 | if (icon != null) { |
927 | 0 | icon.paintIcon(null, g, iconX, iconY); |
928 | 0 | iconX += icon.getIconWidth(); |
929 | } | |
930 | 0 | } |
931 | 0 | items = tdList.elementListForOffender(this); |
932 | 0 | for (ToDoItem item : items) { |
933 | 0 | Icon icon = item.getClarifier(); |
934 | 0 | if (icon instanceof Clarifier) { |
935 | 0 | ((Clarifier) icon).setFig(this); |
936 | 0 | ((Clarifier) icon).setToDoItem(item); |
937 | } | |
938 | 0 | if (icon != null) { |
939 | 0 | icon.paintIcon(null, g, iconX, iconY); |
940 | 0 | iconX += icon.getIconWidth(); |
941 | } | |
942 | 0 | } |
943 | 0 | } |
944 | ||
945 | /** | |
946 | * @param x the x of the hit | |
947 | * @param y the y of the hit | |
948 | * @return the todo item of which the clarifier has been hit | |
949 | */ | |
950 | protected ToDoItem hitClarifier(int x, int y) { | |
951 | // TODO: ToDoItem stuff should be made an opaque extension | |
952 | 0 | int iconX = getX(); |
953 | 0 | ToDoList tdList = Designer.theDesigner().getToDoList(); |
954 | 0 | List<ToDoItem> items = tdList.elementListForOffender(getOwner()); |
955 | 0 | for (ToDoItem item : items) { |
956 | 0 | Icon icon = item.getClarifier(); |
957 | 0 | int width = icon.getIconWidth(); |
958 | 0 | if (y >= getY() - 15 |
959 | && y <= getY() + 10 | |
960 | && x >= iconX | |
961 | && x <= iconX + width) { | |
962 | 0 | return item; |
963 | } | |
964 | 0 | iconX += width; |
965 | 0 | } |
966 | 0 | for (ToDoItem item : items) { |
967 | 0 | Icon icon = item.getClarifier(); |
968 | 0 | if (icon instanceof Clarifier) { |
969 | 0 | ((Clarifier) icon).setFig(this); |
970 | 0 | ((Clarifier) icon).setToDoItem(item); |
971 | 0 | if (((Clarifier) icon).hit(x, y)) { |
972 | 0 | return item; |
973 | } | |
974 | } | |
975 | 0 | } |
976 | 0 | items = tdList.elementListForOffender(this); |
977 | 0 | for (ToDoItem item : items) { |
978 | 0 | Icon icon = item.getClarifier(); |
979 | 0 | int width = icon.getIconWidth(); |
980 | 0 | if (y >= getY() - 15 |
981 | && y <= getY() + 10 | |
982 | && x >= iconX | |
983 | && x <= iconX + width) { | |
984 | 0 | return item; |
985 | } | |
986 | 0 | iconX += width; |
987 | 0 | } |
988 | 0 | for (ToDoItem item : items) { |
989 | 0 | Icon icon = item.getClarifier(); |
990 | 0 | if (icon instanceof Clarifier) { |
991 | 0 | ((Clarifier) icon).setFig(this); |
992 | 0 | ((Clarifier) icon).setToDoItem(item); |
993 | 0 | if (((Clarifier) icon).hit(x, y)) { |
994 | 0 | return item; |
995 | } | |
996 | } | |
997 | 0 | } |
998 | 0 | return null; |
999 | } | |
1000 | ||
1001 | /* | |
1002 | * @see org.tigris.gef.presentation.Fig#getTipString(java.awt.event.MouseEvent) | |
1003 | */ | |
1004 | @Override | |
1005 | public String getTipString(MouseEvent me) { | |
1006 | // TODO: Generalize extension and remove critic specific code | |
1007 | 0 | ToDoItem item = hitClarifier(me.getX(), me.getY()); |
1008 | 0 | String tip = ""; |
1009 | 0 | if (item != null |
1010 | && Globals.curEditor().getSelectionManager().containsFig(this)) { | |
1011 | 0 | tip = item.getHeadline() + " "; |
1012 | 0 | } else if (getOwner() != null) { |
1013 | 0 | tip = Model.getFacade().getTipString(getOwner()); |
1014 | } else { | |
1015 | 0 | tip = toString(); |
1016 | } | |
1017 | 0 | if (tip != null && tip.length() > 0 && !tip.endsWith(" ")) { |
1018 | 0 | tip += " "; |
1019 | } | |
1020 | 0 | return tip; |
1021 | } | |
1022 | ||
1023 | //////////////////////////////////////////////////////////////// | |
1024 | // event handlers | |
1025 | ||
1026 | /* | |
1027 | * @see java.beans.VetoableChangeListener#vetoableChange(java.beans.PropertyChangeEvent) | |
1028 | */ | |
1029 | public void vetoableChange(PropertyChangeEvent pce) { | |
1030 | 0 | LOG.debug("in vetoableChange"); |
1031 | 0 | Object src = pce.getSource(); |
1032 | 0 | if (src == getOwner()) { |
1033 | 0 | DelayedChangeNotify delayedNotify = |
1034 | new DelayedChangeNotify(this, pce); | |
1035 | 0 | SwingUtilities.invokeLater(delayedNotify); |
1036 | 0 | } else { |
1037 | 0 | LOG.debug("FigNodeModelElement got vetoableChange" |
1038 | + " from non-owner:" | |
1039 | + src); | |
1040 | } | |
1041 | 0 | } |
1042 | ||
1043 | /* | |
1044 | * @see org.argouml.kernel.DelayedVChangeListener#delayedVetoableChange(java.beans.PropertyChangeEvent) | |
1045 | */ | |
1046 | public void delayedVetoableChange(PropertyChangeEvent pce) { | |
1047 | 0 | LOG.debug("in delayedVetoableChange"); |
1048 | // update any text, colors, fonts, etc. | |
1049 | 0 | renderingChanged(); |
1050 | 0 | endTrans(); |
1051 | 0 | } |
1052 | ||
1053 | /** | |
1054 | * Determine new bounds. <p> | |
1055 | * | |
1056 | * This algorithm makes the box grow | |
1057 | * (if the calculated minimum size grows), | |
1058 | * but then it can never shrink again | |
1059 | * (not even if the calculated minimum size is smaller).<p> | |
1060 | * | |
1061 | * If the user can not resize the fig, e.g. like the FigActor or FigFinalState, | |
1062 | * then we return the original size. | |
1063 | */ | |
1064 | protected void updateBounds() { | |
1065 | 0 | if (!checkSize) { |
1066 | 0 | return; |
1067 | } | |
1068 | 0 | Rectangle bbox = getBounds(); |
1069 | 0 | Dimension minSize = getMinimumSize(); |
1070 | 0 | if (isResizable()) { |
1071 | 0 | bbox.width = Math.max(bbox.width, minSize.width); |
1072 | 0 | bbox.height = Math.max(bbox.height, minSize.height); |
1073 | } | |
1074 | /* Only update the bounds if they change: */ | |
1075 | 0 | if (bbox.x != getX() |
1076 | || bbox.y != getY() | |
1077 | || bbox.width != getWidth() | |
1078 | || bbox.height != getHeight()) { | |
1079 | 0 | setBounds(bbox.x, bbox.y, bbox.width, bbox.height); |
1080 | } | |
1081 | 0 | } |
1082 | ||
1083 | /* | |
1084 | * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) | |
1085 | */ | |
1086 | public void propertyChange(final PropertyChangeEvent pve) { | |
1087 | 32 | final Object src = pve.getSource(); |
1088 | 32 | final String pName = pve.getPropertyName(); |
1089 | 32 | if (pve instanceof DeleteInstanceEvent && src == getOwner()) { |
1090 | 0 | removeFromDiagram(); |
1091 | 0 | return; |
1092 | } | |
1093 | ||
1094 | // We are getting events we don't want. Filter them out. | |
1095 | 32 | if (pve.getPropertyName().equals("supplierDependency") |
1096 | && Model.getFacade().isADependency(pve.getOldValue())) { | |
1097 | // TODO: Can we instruct the model event pump not to send these in | |
1098 | // the first place? See defect 5095. | |
1099 | 0 | return; |
1100 | } | |
1101 | ||
1102 | // We handle and consume editing events | |
1103 | 32 | if (pName.equals("editing") |
1104 | && Boolean.FALSE.equals(pve.getNewValue())) { | |
1105 | try { | |
1106 | //parse the text that was edited | |
1107 | 0 | textEdited((FigText) src); |
1108 | // resize the FigNode to accommodate the new text | |
1109 | 0 | final Rectangle bbox = getBounds(); |
1110 | 0 | final Dimension minSize = getMinimumSize(); |
1111 | 0 | bbox.width = Math.max(bbox.width, minSize.width); |
1112 | 0 | bbox.height = Math.max(bbox.height, minSize.height); |
1113 | 0 | setBounds(bbox.x, bbox.y, bbox.width, bbox.height); |
1114 | 0 | endTrans(); |
1115 | 0 | } catch (PropertyVetoException ex) { |
1116 | 0 | LOG.error("could not parse the text entered. " |
1117 | + "PropertyVetoException", | |
1118 | ex); | |
1119 | 0 | } |
1120 | 32 | } else if (pName.equals("editing") |
1121 | && Boolean.TRUE.equals(pve.getNewValue())) { | |
1122 | 0 | if (!isReadOnly()) { |
1123 | 0 | textEditStarted((FigText) src); |
1124 | } | |
1125 | } else { | |
1126 | 32 | super.propertyChange(pve); |
1127 | } | |
1128 | 32 | if (Model.getFacade().isAUMLElement(src)) { |
1129 | 0 | final UmlChangeEvent event = (UmlChangeEvent) pve; |
1130 | /* If the source of the event is an UML object, | |
1131 | * e.g. the owner of this Fig (but not always only the owner | |
1132 | * is shown, e.g. for a class, also its attributes are shown), | |
1133 | * then the UML model has been changed. | |
1134 | */ | |
1135 | 0 | final Object owner = getOwner(); |
1136 | 0 | if (owner == null) { |
1137 | // TODO: Should this not be an assert? | |
1138 | 0 | return; |
1139 | } | |
1140 | ||
1141 | try { | |
1142 | 0 | modelChanged(event); |
1143 | 0 | } catch (InvalidElementException e) { |
1144 | 0 | if (LOG.isDebugEnabled()) { |
1145 | 0 | LOG.debug("modelChanged method accessed deleted element" |
1146 | + formatEvent(event), | |
1147 | e); | |
1148 | } | |
1149 | 0 | } |
1150 | ||
1151 | 0 | if (event.getSource() == owner |
1152 | && "stereotype".equals(event.getPropertyName())) { | |
1153 | 0 | stereotypeChanged(event); |
1154 | } | |
1155 | ||
1156 | 0 | Runnable doWorkRunnable = new Runnable() { |
1157 | public void run() { | |
1158 | try { | |
1159 | 0 | updateLayout(event); |
1160 | 0 | } catch (InvalidElementException e) { |
1161 | 0 | if (LOG.isDebugEnabled()) { |
1162 | 0 | LOG.debug("updateLayout method accessed " |
1163 | + "deleted element " | |
1164 | + formatEvent(event), e); | |
1165 | } | |
1166 | 0 | } |
1167 | 0 | } |
1168 | }; | |
1169 | 0 | SwingUtilities.invokeLater(doWorkRunnable); |
1170 | } | |
1171 | 32 | } |
1172 | ||
1173 | private String formatEvent(PropertyChangeEvent event) { | |
1174 | 0 | return "\n\t event = " + event.getClass().getName() |
1175 | + "\n\t source = " + event.getSource() | |
1176 | + "\n\t old = " + event.getOldValue() | |
1177 | + "\n\t name = " + event.getPropertyName(); | |
1178 | } | |
1179 | ||
1180 | /** | |
1181 | * Return true if the model element that this Fig represents is read only | |
1182 | * @return The model element is read only. | |
1183 | */ | |
1184 | private boolean isReadOnly() { | |
1185 | try { | |
1186 | 0 | return Model.getModelManagementHelper().isReadOnly(getOwner()); |
1187 | 0 | } catch (InvalidElementException e) { |
1188 | 0 | return true; |
1189 | } | |
1190 | } | |
1191 | ||
1192 | /** | |
1193 | * Called by propertyChanged when it detects that a stereotype | |
1194 | * has been added or removed. On removal the FigNode removes its | |
1195 | * listener to that stereotype. When a new stereotype is detected | |
1196 | * we add a listener. | |
1197 | * TODO: Bob says: In my opinion we shouldn't be doing this here. | |
1198 | * FigStereotype should always be listening to change of its | |
1199 | * owners name. | |
1200 | * FigStereotypesCompartment should always be listening for add | |
1201 | * or remove of Stereotypes to its owner. | |
1202 | * Those classes will need to pass some event to the FigNode on | |
1203 | * the AWT thread only if a change results in a change of size | |
1204 | * that requires a redraw. | |
1205 | * <p>NOTE: Runs at the Model (MDR) Thread </p> | |
1206 | * @param event the UmlChangeEvent that caused the change | |
1207 | */ | |
1208 | private void stereotypeChanged(final UmlChangeEvent event) { | |
1209 | 0 | final Object owner = getOwner(); |
1210 | 0 | assert owner != null; |
1211 | try { | |
1212 | 0 | if (event.getOldValue() != null) { |
1213 | 0 | removeElementListener(event.getOldValue()); |
1214 | } | |
1215 | 0 | if (event.getNewValue() != null) { |
1216 | 0 | addElementListener(event.getNewValue(), "name"); |
1217 | } | |
1218 | 0 | } catch (InvalidElementException e) { |
1219 | 0 | LOG.debug("stereotypeChanged method accessed deleted element ", e); |
1220 | 0 | } |
1221 | 0 | } |
1222 | ||
1223 | /** | |
1224 | * This method is called when the user doubleclicked on the text field, | |
1225 | * and starts editing. Subclasses should overrule this field to e.g. | |
1226 | * supply help to the user about the used format. <p> | |
1227 | * | |
1228 | * It is also possible to alter the text to be edited | |
1229 | * already here, e.g. by adding the stereotype in front of the name, | |
1230 | * by using setFullyHandleStereotypes(true) in the NotationSettings | |
1231 | * argument of the NotationProvider.toString() function, | |
1232 | * but that seems not user-friendly. See issue 3838. | |
1233 | * | |
1234 | * @param ft the FigText that will be edited and contains the start-text | |
1235 | */ | |
1236 | protected void textEditStarted(FigText ft) { | |
1237 | 0 | if (ft == getNameFig()) { |
1238 | 0 | showHelp(notationProviderName.getParsingHelp()); |
1239 | 0 | ft.setText(notationProviderName.toString(getOwner(), |
1240 | getNotationSettings())); | |
1241 | } | |
1242 | 0 | if (ft instanceof CompartmentFigText) { |
1243 | 0 | final CompartmentFigText figText = (CompartmentFigText) ft; |
1244 | 0 | showHelp(figText.getNotationProvider().getParsingHelp()); |
1245 | 0 | figText.setText(figText.getNotationProvider().toString( |
1246 | figText.getOwner(), getNotationSettings())); | |
1247 | } | |
1248 | 0 | } |
1249 | ||
1250 | /** | |
1251 | * Utility function to localize the given string with help text, and show it | |
1252 | * in the status bar of the ArgoUML window. This function is used in favour | |
1253 | * of the inline call to enable later improvements; e.g. it would be | |
1254 | * possible to show a help-balloon. | |
1255 | * <p> | |
1256 | * TODO: Work this out. One matter to possibly improve: show multiple lines. | |
1257 | * | |
1258 | * @param s the given string to be localized and shown | |
1259 | */ | |
1260 | protected void showHelp(String s) { | |
1261 | 0 | if (s == null) { |
1262 | // Convert null to empty string and clear help message | |
1263 | 0 | ArgoEventPump.fireEvent(new ArgoHelpEvent( |
1264 | ArgoEventTypes.HELP_CHANGED, this, "")); | |
1265 | } else { | |
1266 | 0 | ArgoEventPump.fireEvent(new ArgoHelpEvent( |
1267 | ArgoEventTypes.HELP_CHANGED, this, Translator.localize(s))); | |
1268 | } | |
1269 | 0 | } |
1270 | ||
1271 | /** | |
1272 | * This method is called after the user finishes editing a text | |
1273 | * field that is in the FigNodeModelElement. Determine which | |
1274 | * field and update the model. This class handles the name, | |
1275 | * the stereotype, and any CompartmentFigTexts. | |
1276 | * Subclasses should override to handle other text elements. | |
1277 | * | |
1278 | * @param ft the FigText that has been edited and contains the new text | |
1279 | * @throws PropertyVetoException thrown when new text represents | |
1280 | * an unacceptable value | |
1281 | */ | |
1282 | protected void textEdited(FigText ft) throws PropertyVetoException { | |
1283 | 0 | if (ft == nameFig) { |
1284 | // TODO: Can we delegate this to a specialist FigName class? | |
1285 | 0 | if (getOwner() == null) { |
1286 | 0 | return; |
1287 | } | |
1288 | 0 | notationProviderName.parse(getOwner(), ft.getText()); |
1289 | 0 | ft.setText(notationProviderName.toString(getOwner(), |
1290 | getNotationSettings())); | |
1291 | } | |
1292 | 0 | if (ft instanceof CompartmentFigText) { |
1293 | 0 | final CompartmentFigText figText = (CompartmentFigText) ft; |
1294 | 0 | figText.getNotationProvider().parse(ft.getOwner(), ft.getText()); |
1295 | 0 | ft.setText(figText.getNotationProvider().toString( |
1296 | ft.getOwner(), getNotationSettings())); | |
1297 | } | |
1298 | 0 | } |
1299 | ||
1300 | //////////////////////////////////////////////////////////////// | |
1301 | // event handlers - MouseListener implementation | |
1302 | ||
1303 | /* | |
1304 | * If the user double clicks on any part of this FigNode, pass it | |
1305 | * down to one of the internal Figs. This allows the user to | |
1306 | * initiate direct text editing. | |
1307 | * | |
1308 | * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) | |
1309 | */ | |
1310 | @Override | |
1311 | public void mouseClicked(MouseEvent me) { | |
1312 | 0 | if (!readyToEdit) { |
1313 | 0 | if (Model.getFacade().isAModelElement(getOwner())) { |
1314 | // TODO: Why is this clearing the name?!?! - tfm | |
1315 | 0 | Model.getCoreHelper().setName(getOwner(), ""); |
1316 | 0 | readyToEdit = true; |
1317 | } else { | |
1318 | 0 | LOG.debug("not ready to edit name"); |
1319 | 0 | return; |
1320 | } | |
1321 | } | |
1322 | 0 | if (me.isConsumed()) { |
1323 | 0 | return; |
1324 | } | |
1325 | 0 | if (me.getClickCount() >= 2 |
1326 | && !(me.isPopupTrigger() | |
1327 | || me.getModifiers() == InputEvent.BUTTON3_MASK) | |
1328 | && getOwner() != null | |
1329 | && !isReadOnly()) { | |
1330 | 0 | Rectangle r = new Rectangle(me.getX() - 2, me.getY() - 2, 4, 4); |
1331 | 0 | Fig f = hitFig(r); |
1332 | 0 | if (f instanceof MouseListener && f.isVisible()) { |
1333 | 0 | ((MouseListener) f).mouseClicked(me); |
1334 | 0 | } else if (f instanceof FigGroup && f.isVisible()) { |
1335 | //this enables direct text editing for sub figs of a | |
1336 | //FigGroup object: | |
1337 | 0 | Fig f2 = ((FigGroup) f).hitFig(r); |
1338 | 0 | if (f2 instanceof MouseListener) { |
1339 | 0 | ((MouseListener) f2).mouseClicked(me); |
1340 | } else { | |
1341 | 0 | createContainedModelElement((FigGroup) f, me); |
1342 | } | |
1343 | } | |
1344 | } | |
1345 | 0 | } |
1346 | ||
1347 | /* | |
1348 | * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent) | |
1349 | */ | |
1350 | public void keyPressed(KeyEvent ke) { | |
1351 | 0 | if (ke.isConsumed() || getOwner() == null) { |
1352 | 0 | return; |
1353 | } | |
1354 | 0 | nameFig.keyPressed(ke); |
1355 | 0 | } |
1356 | ||
1357 | /* | |
1358 | * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent) | |
1359 | */ | |
1360 | public void keyReleased(KeyEvent ke) { | |
1361 | 0 | if (ke.isConsumed() || getOwner() == null) { |
1362 | 0 | return; |
1363 | } | |
1364 | 0 | nameFig.keyReleased(ke); |
1365 | 0 | } |
1366 | ||
1367 | /* | |
1368 | * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent) | |
1369 | */ | |
1370 | public void keyTyped(KeyEvent ke) { | |
1371 | 0 | if (!editable || isReadOnly()) { |
1372 | 0 | return; |
1373 | } | |
1374 | 0 | if (!readyToEdit) { |
1375 | 0 | if (Model.getFacade().isAModelElement(getOwner())) { |
1376 | 0 | Model.getCoreHelper().setName(getOwner(), ""); |
1377 | 0 | readyToEdit = true; |
1378 | } else { | |
1379 | 0 | LOG.debug("not ready to edit name"); |
1380 | 0 | return; |
1381 | } | |
1382 | } | |
1383 | 0 | if (ke.isConsumed() || getOwner() == null) { |
1384 | 0 | return; |
1385 | } | |
1386 | 0 | nameFig.keyTyped(ke); |
1387 | 0 | } |
1388 | ||
1389 | /** | |
1390 | * This is a template method called by the ArgoUML framework as the result | |
1391 | * of a change to a model element. Do not call this method directly | |
1392 | * yourself. | |
1393 | * <p>Override this in any subclasses in order to change what model | |
1394 | * elements the FigNode is listening to as a result of change to the model. | |
1395 | * </p> | |
1396 | * <p>This method is guaranteed by the framework to be running on the same | |
1397 | * thread as the model subsystem.</p> | |
1398 | * TODO: Lets refactor this at some time to take UmlChangeEvent argument | |
1399 | * | |
1400 | * @param event the UmlChangeEvent that caused the change | |
1401 | */ | |
1402 | protected void modelChanged(PropertyChangeEvent event) { | |
1403 | 0 | if (event instanceof AssociationChangeEvent |
1404 | || event instanceof AttributeChangeEvent) { | |
1405 | // TODO: This brute force approach of updating listeners on each | |
1406 | // and every event, without checking the event type or any other | |
1407 | // information is going to cause lots of InvalidElementExceptions | |
1408 | // in subclasses implementations of updateListeners (and they | |
1409 | // won't have the event information to make their own decisions) | |
1410 | 0 | updateListeners(getOwner(), getOwner()); |
1411 | } | |
1412 | 0 | } |
1413 | ||
1414 | //////////////////////////////////////////////////////////////// | |
1415 | // internal methods | |
1416 | ||
1417 | /** | |
1418 | * This is a template method called by the ArgoUML framework as the result | |
1419 | * of a change to a model element. Do not call this method directly | |
1420 | * yourself. | |
1421 | * <p>Override this in any subclasses in order to restructure the FigNode | |
1422 | * due to change of any model element that this FigNode is listening to.</p> | |
1423 | * <p>This method automatically updates the stereotype rendering.</p> | |
1424 | * <p>The default behavior is to update the name and stereotype text.</p> | |
1425 | * <p>For e.g. a Package, if the visibility is changed | |
1426 | * via the properties panel, then | |
1427 | * the display of it on the diagram has to follow the change. | |
1428 | * This is not handled here, but by the notationProviderName.</p> | |
1429 | * <p>This method is guaranteed by the framework to be running on the | |
1430 | * Swing/AWT thread.</p> | |
1431 | * | |
1432 | * @param event the UmlChangeEvent that caused the change | |
1433 | */ | |
1434 | protected void updateLayout(UmlChangeEvent event) { | |
1435 | 0 | assert event != null; |
1436 | 0 | final Object owner = getOwner(); |
1437 | 0 | assert owner != null; |
1438 | 0 | if (owner == null) { |
1439 | 0 | return; |
1440 | } | |
1441 | 0 | boolean needDamage = false; |
1442 | 0 | if (event instanceof AssociationChangeEvent |
1443 | || event instanceof AttributeChangeEvent) { | |
1444 | 0 | if (notationProviderName != null) { |
1445 | 0 | updateNameText(); |
1446 | } | |
1447 | 0 | needDamage = true; |
1448 | } | |
1449 | 0 | if (event.getSource() == owner |
1450 | && "stereotype".equals(event.getPropertyName())) { | |
1451 | 0 | updateStereotypeText(); |
1452 | 0 | updateStereotypeIcon(); |
1453 | 0 | needDamage = true; |
1454 | } | |
1455 | 0 | if (needDamage) { |
1456 | 0 | damage(); |
1457 | } | |
1458 | 0 | } |
1459 | ||
1460 | /** | |
1461 | * Create a new model element contained in the fig owner. | |
1462 | * Used by subclasses to, for example, create an attribute | |
1463 | * within a class. | |
1464 | * | |
1465 | * @param fg The fig group to which this applies | |
1466 | * @param me The input event that triggered us. In the current | |
1467 | * implementation a mouse double click. | |
1468 | */ | |
1469 | protected void createContainedModelElement(FigGroup fg, InputEvent me) { | |
1470 | // must be overridden to make sense | |
1471 | // (I didn't want to make it abstract because it might not be required) | |
1472 | 0 | } |
1473 | ||
1474 | /** | |
1475 | * @param o the given object | |
1476 | * @return true if one of my figs has the given object as owner | |
1477 | */ | |
1478 | protected boolean isPartlyOwner(Object o) { | |
1479 | 0 | if (o == null || o == getOwner()) { |
1480 | 0 | return true; |
1481 | } | |
1482 | 0 | for (Object fig : getFigs()) { |
1483 | 0 | if (isPartlyOwner((Fig) fig, o)) { |
1484 | 0 | return true; |
1485 | } | |
1486 | } | |
1487 | 0 | return false; |
1488 | } | |
1489 | ||
1490 | /** | |
1491 | * @param fig the given fig (may be a group) | |
1492 | * @param o the given object | |
1493 | * @return true if one of the given figs has the given object as owner | |
1494 | */ | |
1495 | protected boolean isPartlyOwner(Fig fig, Object o) { | |
1496 | 0 | if (o == null) { |
1497 | 0 | return false; |
1498 | } | |
1499 | 0 | if (o == fig.getOwner()) { |
1500 | 0 | return true; |
1501 | } | |
1502 | 0 | if (fig instanceof FigGroup) { |
1503 | 0 | for (Object fig2 : ((FigGroup) fig).getFigs()) { |
1504 | 0 | if (isPartlyOwner((Fig) fig2, o)) { |
1505 | 0 | return true; |
1506 | } | |
1507 | } | |
1508 | } | |
1509 | 0 | return false; |
1510 | } | |
1511 | ||
1512 | /* | |
1513 | * @see org.tigris.gef.presentation.Fig#deleteFromModel() | |
1514 | */ | |
1515 | @Override | |
1516 | public void deleteFromModel() { | |
1517 | 0 | Object own = getOwner(); |
1518 | 0 | if (own != null) { |
1519 | 0 | getProject().moveToTrash(own); |
1520 | } | |
1521 | 0 | for (Object fig : getFigs()) { |
1522 | 0 | ((Fig) fig).deleteFromModel(); |
1523 | } | |
1524 | 0 | super.deleteFromModel(); |
1525 | 0 | } |
1526 | ||
1527 | /** | |
1528 | * Replace the NotationProvider(s). <p> | |
1529 | * | |
1530 | * This method shall not be used for the initial creation of | |
1531 | * notation providers, but only for replacing them when required. | |
1532 | * Initialization must be done in the | |
1533 | * constructor using methods which | |
1534 | * can't be overridden. <p> | |
1535 | * NotationProviders can not be updated - they | |
1536 | * are lightweight throw-away objects. | |
1537 | * Hence this method creates a (new) NotationProvider whenever | |
1538 | * needed. E.g. when the notation language is | |
1539 | * changed by the user, then the NPs are to be re-created. | |
1540 | * So, this method shall not be | |
1541 | * called from a Fig constructor.<p> | |
1542 | * | |
1543 | * After the removal of the deprecated method setOwner(), | |
1544 | * this method shall contain the following statement: | |
1545 | * assert notationProviderName != null | |
1546 | * | |
1547 | * @param own owning UML element | |
1548 | */ | |
1549 | protected void initNotationProviders(Object own) { | |
1550 | 0 | if (notationProviderName != null) { |
1551 | 0 | notationProviderName.cleanListener(); |
1552 | } | |
1553 | 0 | if (Model.getFacade().isAUMLElement(own)) { |
1554 | 0 | NotationName notation = Notation.findNotation( |
1555 | getNotationSettings().getNotationLanguage()); | |
1556 | 0 | notationProviderName = |
1557 | NotationProviderFactory2.getInstance().getNotationProvider( | |
1558 | getNotationProviderType(), own, this, | |
1559 | notation); | |
1560 | } | |
1561 | 0 | } |
1562 | ||
1563 | /** | |
1564 | * Overrule this for subclasses | |
1565 | * that need a different NotationProvider. | |
1566 | * | |
1567 | * @return the type of the notation provider | |
1568 | */ | |
1569 | protected int getNotationProviderType() { | |
1570 | 0 | return NotationProviderFactory2.TYPE_NAME; |
1571 | } | |
1572 | ||
1573 | /** | |
1574 | * Updates the text of the stereotype FigText. Override in subclasses to get | |
1575 | * wanted behaviour. | |
1576 | */ | |
1577 | protected void updateStereotypeText() { | |
1578 | 0 | if (getOwner() == null) { |
1579 | 0 | LOG.warn("Null owner for [" + this.toString() + "/" |
1580 | + this.getClass()); | |
1581 | 0 | return; |
1582 | } | |
1583 | 0 | if (getStereotypeFig() != null) { |
1584 | 0 | getStereotypeFig().populate(); |
1585 | } | |
1586 | 0 | } |
1587 | ||
1588 | /** | |
1589 | * Updates the text of the name FigText. | |
1590 | * This includes text changes, | |
1591 | * but also changes in rendering like bold. | |
1592 | */ | |
1593 | protected void updateNameText() { | |
1594 | 0 | if (readyToEdit) { |
1595 | 0 | if (getOwner() == null) { |
1596 | 0 | return; |
1597 | } | |
1598 | 0 | if (notationProviderName != null) { |
1599 | 0 | nameFig.setText(notationProviderName.toString( |
1600 | getOwner(), getNotationSettings())); | |
1601 | 0 | updateFont(); |
1602 | 0 | updateBounds(); |
1603 | } | |
1604 | } | |
1605 | 0 | } |
1606 | ||
1607 | /* | |
1608 | * @see org.argouml.uml.diagram.ui.PathContainer#isPathVisible() | |
1609 | */ | |
1610 | public boolean isPathVisible() { | |
1611 | 0 | return getNotationSettings().isShowPaths(); |
1612 | } | |
1613 | ||
1614 | /* | |
1615 | * @see org.argouml.uml.diagram.ui.PathContainer#setPathVisible(boolean) | |
1616 | */ | |
1617 | public void setPathVisible(boolean visible) { | |
1618 | 0 | NotationSettings ns = getNotationSettings(); |
1619 | 0 | if (ns.isShowPaths() == visible) { |
1620 | 0 | return; |
1621 | } | |
1622 | 0 | MutableGraphSupport.enableSaveAction(); |
1623 | // TODO: Use this event mechanism to update | |
1624 | // the checkmark on the Presentation Tab: | |
1625 | 0 | firePropChange("pathVisible", !visible, visible); |
1626 | 0 | ns.setShowPaths(visible); |
1627 | 0 | if (readyToEdit) { |
1628 | 0 | renderingChanged(); |
1629 | 0 | damage(); |
1630 | } | |
1631 | 0 | } |
1632 | ||
1633 | /** | |
1634 | * At creation time of the Fig, we determine | |
1635 | * if the path should be visible by default. <p> | |
1636 | * | |
1637 | * The path is a concatenation of the names of all packages by which | |
1638 | * this modelelement is contained, | |
1639 | * seperated by "::" (for UML at least). <p> | |
1640 | * | |
1641 | * If the default namespace of the diagram corresponds | |
1642 | * to the namespace of the modelelement, | |
1643 | * then we do NOT show the path. Otherwise, we do. <p> | |
1644 | * | |
1645 | * RRose uses the same heuristic algorithm, | |
1646 | * but shows "(from <path>)" below the name, | |
1647 | * while we follow the UML syntax. | |
1648 | */ | |
1649 | protected void determineDefaultPathVisible() { | |
1650 | 0 | Object modelElement = getOwner(); |
1651 | 0 | LayerPerspective layer = (LayerPerspective) getLayer(); |
1652 | 0 | if ((layer != null) |
1653 | && Model.getFacade().isAModelElement(modelElement)) { | |
1654 | 0 | ArgoDiagram diagram = (ArgoDiagram) layer.getDiagram(); |
1655 | 0 | Object elementNs = Model.getFacade().getNamespace(modelElement); |
1656 | 0 | Object diagramNs = diagram.getNamespace(); |
1657 | 0 | if (elementNs != null) { |
1658 | 0 | boolean visible = (elementNs != diagramNs); |
1659 | 0 | getNotationSettings().setShowPaths(visible); |
1660 | 0 | updateNameText(); |
1661 | 0 | damage(); |
1662 | } | |
1663 | // it is done | |
1664 | } | |
1665 | // either layer or owner was null | |
1666 | 0 | } |
1667 | ||
1668 | /* | |
1669 | * @see org.tigris.gef.presentation.Fig#classNameAndBounds() | |
1670 | */ | |
1671 | @Deprecated | |
1672 | @Override | |
1673 | public String classNameAndBounds() { | |
1674 | 0 | return getClass().getName() |
1675 | + "[" + getX() + ", " + getY() + ", " | |
1676 | + getWidth() + ", " + getHeight() + "]" | |
1677 | + "pathVisible=" + isPathVisible() + ";" | |
1678 | + "stereotypeView=" + getStereotypeView() + ";"; | |
1679 | } | |
1680 | ||
1681 | /** | |
1682 | * Implementations of this method should register/unregister the fig for all | |
1683 | * (model)events. For FigNodeModelElement only the fig itself is registered | |
1684 | * as listening to (all) events fired by the owner itself. | |
1685 | * But for, for example, | |
1686 | * FigClass the fig must also register for events fired by the operations | |
1687 | * and attributes of the owner. <p> | |
1688 | * | |
1689 | * An explanation of the original | |
1690 | * purpose of this method is given in issue 1321.<p> | |
1691 | * | |
1692 | * This function is used by the modelChanged() | |
1693 | * function.<p> | |
1694 | * | |
1695 | * In certain cases, it is imperative that indeed ALL listeners are | |
1696 | * updated, since they are ALL removed | |
1697 | * by a call to removeElementListener. <p> | |
1698 | * | |
1699 | * IF this method is called with both the oldOwner and the | |
1700 | * newOwner equal and not null, | |
1701 | * AND we listen only to the owner itself, | |
1702 | * THEN we can safely ignore the call, but | |
1703 | * ELSE we need to update the listeners of the related elements, | |
1704 | * since the related elements may have been replaced. | |
1705 | * | |
1706 | * @param newOwner null, or the owner of this. | |
1707 | * The former means that all listeners have to be removed. | |
1708 | * @param oldOwner null, or the previous owner | |
1709 | * The former means that all listeners have to be set. | |
1710 | */ | |
1711 | protected void updateListeners(Object oldOwner, Object newOwner) { | |
1712 | 0 | if (oldOwner == newOwner) { |
1713 | 0 | return; |
1714 | } | |
1715 | 0 | if (oldOwner != null) { |
1716 | 0 | removeElementListener(oldOwner); |
1717 | } | |
1718 | 0 | if (newOwner != null) { |
1719 | 0 | addElementListener(newOwner); |
1720 | } | |
1721 | ||
1722 | 0 | } |
1723 | ||
1724 | /** | |
1725 | * @see org.argouml.application.events.ArgoNotationEventListener#notationChanged(org.argouml.application.events.ArgoNotationEvent) | |
1726 | * @deprecated for 0.27.2 by tfmorris. Changes to notatation provider are | |
1727 | * now handled by the owning diagram. | |
1728 | */ | |
1729 | @Deprecated | |
1730 | public void notationChanged(ArgoNotationEvent event) { | |
1731 | 0 | if (getOwner() == null) { |
1732 | 0 | return; |
1733 | } | |
1734 | try { | |
1735 | 0 | renderingChanged(); |
1736 | 0 | } catch (Exception e) { |
1737 | 0 | LOG.error("Exception", e); |
1738 | 0 | } |
1739 | 0 | } |
1740 | ||
1741 | /** | |
1742 | * @see org.argouml.application.events.ArgoNotationEventListener#notationAdded(org.argouml.application.events.ArgoNotationEvent) | |
1743 | * @deprecated for 0.27.2 by tfmorris. | |
1744 | */ | |
1745 | @Deprecated | |
1746 | public void notationAdded(ArgoNotationEvent event) { | |
1747 | // Default is to do nothing | |
1748 | 0 | } |
1749 | ||
1750 | /** | |
1751 | * @see org.argouml.application.events.ArgoNotationEventListener#notationRemoved(org.argouml.application.events.ArgoNotationEvent) | |
1752 | * @deprecated for 0.27.2 by tfmorris. | |
1753 | */ | |
1754 | @Deprecated | |
1755 | public void notationRemoved(ArgoNotationEvent event) { | |
1756 | // Default is to do nothing | |
1757 | 0 | } |
1758 | ||
1759 | /** | |
1760 | * @see org.argouml.application.events.ArgoNotationEventListener#notationProviderAdded(org.argouml.application.events.ArgoNotationEvent) | |
1761 | * @deprecated for 0.27.2 by tfmorris. | |
1762 | */ | |
1763 | @Deprecated | |
1764 | public void notationProviderAdded(ArgoNotationEvent event) { | |
1765 | // Default is to do nothing | |
1766 | 0 | } |
1767 | ||
1768 | /** | |
1769 | * @see org.argouml.application.events.ArgoNotationEventListener#notationProviderRemoved(org.argouml.application.events.ArgoNotationEvent) | |
1770 | * @deprecated for 0.27.2 by tfmorris. | |
1771 | */ | |
1772 | @Deprecated | |
1773 | public void notationProviderRemoved(ArgoNotationEvent event) { | |
1774 | // Default is to do nothing | |
1775 | 0 | } |
1776 | ||
1777 | /** | |
1778 | * Rerender the entire fig. | |
1779 | * <p> | |
1780 | * This is may be an expensive operation for subclasses which are complex, | |
1781 | * so should be used sparingly. This functionality was originally the | |
1782 | * functionality of modelChanged but modelChanged takes the event now into | |
1783 | * account. | |
1784 | */ | |
1785 | public void renderingChanged() { | |
1786 | 0 | initNotationProviders(getOwner()); |
1787 | 0 | updateNameText(); |
1788 | 0 | updateStereotypeText(); |
1789 | 0 | updateStereotypeIcon(); |
1790 | 0 | updateBounds(); |
1791 | 0 | damage(); |
1792 | 0 | } |
1793 | ||
1794 | protected void updateStereotypeIcon() { | |
1795 | 0 | if (getOwner() == null) { |
1796 | 0 | LOG.warn("Owner of [" + this.toString() + "/" + this.getClass() |
1797 | + "] is null."); | |
1798 | 0 | LOG.warn("I return..."); |
1799 | 0 | return; |
1800 | } | |
1801 | ||
1802 | 0 | if (stereotypeFigProfileIcon != null) { |
1803 | 0 | for (Object fig : getFigs()) { |
1804 | 0 | ((Fig) fig).setVisible(fig != stereotypeFigProfileIcon); |
1805 | } | |
1806 | ||
1807 | 0 | this.removeFig(stereotypeFigProfileIcon); |
1808 | 0 | stereotypeFigProfileIcon = null; |
1809 | } | |
1810 | ||
1811 | 0 | if (originalNameFig != null) { |
1812 | 0 | this.setNameFig(originalNameFig); |
1813 | 0 | originalNameFig = null; |
1814 | } | |
1815 | ||
1816 | 0 | for (Fig icon : floatingStereotypes) { |
1817 | 0 | this.removeFig(icon); |
1818 | } | |
1819 | 0 | floatingStereotypes.clear(); |
1820 | ||
1821 | ||
1822 | 0 | int practicalView = getPracticalView(); |
1823 | 0 | Object modelElement = getOwner(); |
1824 | 0 | Collection stereos = Model.getFacade().getStereotypes(modelElement); |
1825 | ||
1826 | 0 | boolean hiding = |
1827 | practicalView == DiagramAppearance.STEREOTYPE_VIEW_SMALL_ICON; | |
1828 | 0 | if (getStereotypeFig() != null) { |
1829 | 0 | getStereotypeFig().setHidingStereotypesWithIcon(hiding); |
1830 | } | |
1831 | ||
1832 | 0 | if (practicalView == DiagramAppearance.STEREOTYPE_VIEW_BIG_ICON) { |
1833 | ||
1834 | 0 | Image replaceIcon = null; |
1835 | ||
1836 | 0 | if (stereos.size() == 1) { |
1837 | 0 | Object stereo = stereos.iterator().next(); |
1838 | // TODO: Find a way to replace this dependency on Project | |
1839 | 0 | replaceIcon = getProject() |
1840 | .getProfileConfiguration().getFigNodeStrategy() | |
1841 | .getIconForStereotype(stereo); | |
1842 | } | |
1843 | ||
1844 | 0 | if (replaceIcon != null) { |
1845 | 0 | stereotypeFigProfileIcon = new FigProfileIcon(settings, |
1846 | replaceIcon, getName()); | |
1847 | 0 | stereotypeFigProfileIcon.setOwner(getOwner()); |
1848 | ||
1849 | 0 | stereotypeFigProfileIcon.setLocation(getBigPort() |
1850 | .getLocation()); | |
1851 | 0 | addFig(stereotypeFigProfileIcon); |
1852 | ||
1853 | 0 | originalNameFig = this.getNameFig(); |
1854 | 0 | final FigText labelFig = |
1855 | stereotypeFigProfileIcon.getLabelFig(); | |
1856 | 0 | setNameFig(labelFig); |
1857 | ||
1858 | 0 | labelFig.addPropertyChangeListener(this); |
1859 | ||
1860 | 0 | getBigPort().setBounds(stereotypeFigProfileIcon.getBounds()); |
1861 | ||
1862 | 0 | for (Object fig : getFigs()) { |
1863 | 0 | ((Fig) fig).setVisible(fig == stereotypeFigProfileIcon); |
1864 | } | |
1865 | ||
1866 | } | |
1867 | 0 | } else if (practicalView |
1868 | == DiagramAppearance.STEREOTYPE_VIEW_SMALL_ICON) { | |
1869 | 0 | int i = this.getX() + this.getWidth() - ICON_WIDTH - 2; |
1870 | ||
1871 | 0 | for (Object stereo : stereos) { |
1872 | // TODO: Find a way to replace this dependency on Project | |
1873 | 0 | Image icon = getProject() |
1874 | .getProfileConfiguration().getFigNodeStrategy() | |
1875 | .getIconForStereotype(stereo); | |
1876 | 0 | if (icon != null) { |
1877 | 0 | FigImage fimg = new FigImage(i, |
1878 | this.getBigPort().getY() + 2, icon); | |
1879 | 0 | fimg.setSize(ICON_WIDTH, |
1880 | (icon.getHeight(null) * ICON_WIDTH) | |
1881 | / icon.getWidth(null)); | |
1882 | ||
1883 | 0 | addFig(fimg); |
1884 | 0 | floatingStereotypes.add(fimg); |
1885 | ||
1886 | 0 | i -= ICON_WIDTH - 2; |
1887 | } | |
1888 | 0 | } |
1889 | ||
1890 | 0 | updateSmallIcons(this.getWidth()); |
1891 | } | |
1892 | ||
1893 | // TODO: This is a redundant invocation | |
1894 | 0 | updateStereotypeText(); |
1895 | ||
1896 | 0 | damage(); |
1897 | 0 | calcBounds(); |
1898 | 0 | updateEdges(); |
1899 | 0 | updateBounds(); |
1900 | 0 | redraw(); |
1901 | 0 | } |
1902 | ||
1903 | /* | |
1904 | * @see org.tigris.gef.presentation.Fig#calcBounds() | |
1905 | */ | |
1906 | public void calcBounds() { | |
1907 | 2142 | if (suppressCalcBounds) { |
1908 | 0 | return; |
1909 | } | |
1910 | 2142 | super.calcBounds(); |
1911 | 2142 | } |
1912 | ||
1913 | /** | |
1914 | * The setter for checkSize. | |
1915 | * | |
1916 | * @param flag the new value | |
1917 | */ | |
1918 | public void enableSizeChecking(boolean flag) { | |
1919 | 0 | checkSize = flag; |
1920 | 0 | } |
1921 | ||
1922 | /* | |
1923 | * Necessary since GEF contains some errors regarding the hit subject. | |
1924 | * | |
1925 | * @see org.tigris.gef.presentation.Fig#hit(Rectangle) | |
1926 | */ | |
1927 | @Override | |
1928 | public boolean hit(Rectangle r) { | |
1929 | 0 | int cornersHit = countCornersContained(r.x, r.y, r.width, r.height); |
1930 | 0 | if (_filled) { |
1931 | 0 | return cornersHit > 0 || intersects(r); |
1932 | } | |
1933 | 0 | return (cornersHit > 0 && cornersHit < 4) || intersects(r); |
1934 | } | |
1935 | ||
1936 | /* | |
1937 | * @see org.tigris.gef.presentation.Fig#removeFromDiagram() | |
1938 | */ | |
1939 | @Override | |
1940 | public final void removeFromDiagram() { | |
1941 | 0 | Fig delegate = getRemoveDelegate(); |
1942 | 0 | if (delegate instanceof FigNodeModelElement) { |
1943 | 0 | ((FigNodeModelElement) delegate).removeFromDiagramImpl(); |
1944 | 0 | } else if (delegate instanceof FigEdgeModelElement) { |
1945 | 0 | ((FigEdgeModelElement) delegate).removeFromDiagramImpl(); |
1946 | 0 | } else if (delegate != null) { |
1947 | 0 | removeFromDiagramImpl(); |
1948 | } | |
1949 | 0 | } |
1950 | ||
1951 | /** | |
1952 | * Subclasses should override this to redirect a remove request from | |
1953 | * one Fig to another. | |
1954 | * e.g. FigClassAssociationClass uses this to delegate the remove to | |
1955 | * its attached FigAssociationClass. | |
1956 | * @return the fig that handles the remove request | |
1957 | */ | |
1958 | protected Fig getRemoveDelegate() { | |
1959 | 0 | return this; |
1960 | } | |
1961 | ||
1962 | /** | |
1963 | * If you override this method, make sure to remove all listeners: | |
1964 | * If you don't, objects in a deleted project will still receive events.<p> | |
1965 | * | |
1966 | * Also important for remove from diagram! | |
1967 | */ | |
1968 | protected void removeFromDiagramImpl() { | |
1969 | 0 | if (notationProviderName != null) { //This test needed for a FigPool |
1970 | 0 | notationProviderName.cleanListener(); |
1971 | } | |
1972 | 0 | removeAllElementListeners(); |
1973 | 0 | setShadowSize(0); |
1974 | 0 | super.removeFromDiagram(); |
1975 | // Get model listeners removed: | |
1976 | 0 | if (getStereotypeFig() != null) { |
1977 | 0 | getStereotypeFig().removeFromDiagram(); |
1978 | } | |
1979 | 0 | } |
1980 | ||
1981 | /** | |
1982 | * Get the Fig containing the stereotype(s). | |
1983 | * | |
1984 | * @return the stereotype FigGroup | |
1985 | */ | |
1986 | protected FigStereotypesGroup getStereotypeFig() { | |
1987 | 0 | return stereotypeFig; |
1988 | } | |
1989 | ||
1990 | ||
1991 | /** | |
1992 | * @param bp the bigPort, which is the port where edges | |
1993 | * connect to this node | |
1994 | * @deprecated by MVW since V0.28.1. Use {@link #createBigPortFig} | |
1995 | * instead, to guarantee correct initialization. | |
1996 | */ | |
1997 | protected void setBigPort(Fig bp) { | |
1998 | 0 | this.bigPort = bp; |
1999 | 0 | bindPort(getOwner(), bigPort); |
2000 | 0 | } |
2001 | ||
2002 | /** | |
2003 | * @return the fig which is the port where edges connect to this node | |
2004 | */ | |
2005 | public Fig getBigPort() { | |
2006 | 1071 | return bigPort; |
2007 | } | |
2008 | ||
2009 | /** | |
2010 | * @return Returns the checkSize. | |
2011 | */ | |
2012 | protected boolean isCheckSize() { | |
2013 | 0 | return checkSize; |
2014 | } | |
2015 | ||
2016 | /* | |
2017 | * @see org.tigris.gef.presentation.FigNode#isDragConnectable() | |
2018 | */ | |
2019 | public boolean isDragConnectable() { | |
2020 | 0 | return false; |
2021 | } | |
2022 | ||
2023 | /** | |
2024 | * @param e The _encloser to set. | |
2025 | */ | |
2026 | protected void setEncloser(Fig e) { | |
2027 | 0 | this.encloser = e; |
2028 | 0 | } |
2029 | ||
2030 | /** | |
2031 | * @return Returns the _encloser. | |
2032 | */ | |
2033 | protected Fig getEncloser() { | |
2034 | 0 | return encloser; |
2035 | } | |
2036 | /** | |
2037 | * @return Returns the ReadyToEdit. | |
2038 | */ | |
2039 | protected boolean isReadyToEdit() { | |
2040 | 0 | return readyToEdit; |
2041 | } | |
2042 | ||
2043 | /** | |
2044 | * @param v if ready to edit | |
2045 | */ | |
2046 | protected void setReadyToEdit(boolean v) { | |
2047 | 0 | readyToEdit = v; |
2048 | 0 | } |
2049 | ||
2050 | /** | |
2051 | * TODO: Move this in FigGroup (in GEF). | |
2052 | * | |
2053 | * @param scb The suppressCalcBounds to set. | |
2054 | */ | |
2055 | protected void setSuppressCalcBounds(boolean scb) { | |
2056 | 0 | this.suppressCalcBounds = scb; |
2057 | 0 | } |
2058 | ||
2059 | /** | |
2060 | * Set visibility of figure. If the field {@link #invisibleAllowed} is not | |
2061 | * <code>true</code> and this method is passed a parameter of | |
2062 | * <code>false</code> it will throw an IllegalArgumentException. | |
2063 | * | |
2064 | * @param visible | |
2065 | * new visibility - <code>true</code> = visible. | |
2066 | * | |
2067 | * @see org.tigris.gef.presentation.Fig#setVisible(boolean) | |
2068 | */ | |
2069 | public void setVisible(boolean visible) { | |
2070 | 0 | if (!visible && !invisibleAllowed) { |
2071 | 0 | throw new IllegalArgumentException( |
2072 | "Visibility of a FigNode should never be false"); | |
2073 | } | |
2074 | 0 | } |
2075 | ||
2076 | /** | |
2077 | * To redraw each element correctly when changing its location | |
2078 | * with X and Y additions. Also manages relocation of enclosed | |
2079 | * Figs. | |
2080 | * | |
2081 | * @param xInc the increment in the x direction | |
2082 | * @param yInc the increment in the y direction | |
2083 | */ | |
2084 | public void displace (int xInc, int yInc) { | |
2085 | List<Fig> figsVector; | |
2086 | 0 | Rectangle rFig = getBounds(); |
2087 | 0 | setLocation(rFig.x + xInc, rFig.y + yInc); |
2088 | 0 | figsVector = ((List<Fig>) getEnclosedFigs().clone()); |
2089 | 0 | if (!figsVector.isEmpty()) { |
2090 | 0 | for (int i = 0; i < figsVector.size(); i++) { |
2091 | 0 | ((FigNodeModelElement) figsVector.get(i)) |
2092 | .displace(xInc, yInc); | |
2093 | } | |
2094 | } | |
2095 | 0 | } |
2096 | ||
2097 | ||
2098 | /** | |
2099 | * @param allowed true if the function RemoveFromDiagram is allowed | |
2100 | */ | |
2101 | protected void allowRemoveFromDiagram(boolean allowed) { | |
2102 | 0 | this.removeFromDiagram = allowed; |
2103 | 0 | } |
2104 | ||
2105 | public void setDiElement(DiElement element) { | |
2106 | 0 | this.diElement = element; |
2107 | 0 | } |
2108 | ||
2109 | public DiElement getDiElement() { | |
2110 | 0 | return diElement; |
2111 | } | |
2112 | ||
2113 | /** | |
2114 | * @return Returns the popupAddOffset. | |
2115 | */ | |
2116 | protected static int getPopupAddOffset() { | |
2117 | 0 | return popupAddOffset; |
2118 | } | |
2119 | ||
2120 | /** | |
2121 | * Determine if this node can be edited. | |
2122 | * @return editable state | |
2123 | */ | |
2124 | public boolean isEditable() { | |
2125 | 0 | return editable; |
2126 | } | |
2127 | ||
2128 | /** | |
2129 | * By default a node is directly editable by simply selecting | |
2130 | * that node and starting to type. | |
2131 | * Should a subclass of FigNodeModelElement not desire this behaviour | |
2132 | * then it should call setEditable(false) in its constructor. | |
2133 | * | |
2134 | * @param canEdit new state, false = editing disabled. | |
2135 | */ | |
2136 | protected void setEditable(boolean canEdit) { | |
2137 | 0 | this.editable = canEdit; |
2138 | 0 | } |
2139 | ||
2140 | /** | |
2141 | * Add an element listener and remember the registration. | |
2142 | * | |
2143 | * @param element | |
2144 | * element to listen for changes on | |
2145 | * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, Object, String) | |
2146 | */ | |
2147 | protected void addElementListener(Object element) { | |
2148 | 0 | listeners.add(new Object[] {element, null}); |
2149 | 0 | Model.getPump().addModelEventListener(this, element); |
2150 | 0 | } |
2151 | ||
2152 | /** | |
2153 | * Add a listener for a given property name and remember the registration. | |
2154 | * | |
2155 | * @param element | |
2156 | * element to listen for changes on | |
2157 | * @param property | |
2158 | * name of property to listen for changes of | |
2159 | * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, | |
2160 | * Object, String) | |
2161 | */ | |
2162 | protected void addElementListener(Object element, String property) { | |
2163 | 0 | listeners.add(new Object[] {element, property}); |
2164 | 0 | Model.getPump().addModelEventListener(this, element, property); |
2165 | 0 | } |
2166 | ||
2167 | /** | |
2168 | * Add a listener for an array of property names and remember the | |
2169 | * registration. | |
2170 | * | |
2171 | * @param element | |
2172 | * element to listen for changes on | |
2173 | * @param property | |
2174 | * array of property names (Strings) to listen for changes of | |
2175 | * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, | |
2176 | * Object, String) | |
2177 | */ | |
2178 | protected void addElementListener(Object element, String[] property) { | |
2179 | 0 | listeners.add(new Object[] {element, property}); |
2180 | 0 | Model.getPump().addModelEventListener(this, element, property); |
2181 | 0 | } |
2182 | ||
2183 | /** | |
2184 | * Remove an element listener and remembered registration. | |
2185 | * | |
2186 | * @param element | |
2187 | * element to listen for changes on | |
2188 | * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, Object, String) | |
2189 | */ | |
2190 | protected void removeElementListener(Object element) { | |
2191 | 0 | listeners.remove(new Object[] {element, null}); |
2192 | 0 | Model.getPump().removeModelEventListener(this, element); |
2193 | 0 | } |
2194 | ||
2195 | /** | |
2196 | * Unregister all listeners registered through addElementListener | |
2197 | * @see #addElementListener(Object, String) | |
2198 | */ | |
2199 | protected void removeAllElementListeners() { | |
2200 | 0 | removeElementListeners(listeners); |
2201 | 0 | } |
2202 | ||
2203 | private void removeElementListeners(Set<Object[]> listenerSet) { | |
2204 | 0 | for (Object[] listener : listenerSet) { |
2205 | 0 | Object property = listener[1]; |
2206 | 0 | if (property == null) { |
2207 | 0 | Model.getPump().removeModelEventListener(this, listener[0]); |
2208 | 0 | } else if (property instanceof String[]) { |
2209 | 0 | Model.getPump().removeModelEventListener(this, listener[0], |
2210 | (String[]) property); | |
2211 | 0 | } else if (property instanceof String) { |
2212 | 0 | Model.getPump().removeModelEventListener(this, listener[0], |
2213 | (String) property); | |
2214 | } else { | |
2215 | 0 | throw new RuntimeException( |
2216 | "Internal error in removeAllElementListeners"); | |
2217 | } | |
2218 | 0 | } |
2219 | 0 | listeners.removeAll(listenerSet); |
2220 | 0 | } |
2221 | ||
2222 | private void addElementListeners(Set<Object[]> listenerSet) { | |
2223 | 0 | for (Object[] listener : listenerSet) { |
2224 | 0 | Object property = listener[1]; |
2225 | 0 | if (property == null) { |
2226 | 0 | addElementListener(listener[0]); |
2227 | 0 | } else if (property instanceof String[]) { |
2228 | 0 | addElementListener(listener[0], (String[]) property); |
2229 | 0 | } else if (property instanceof String) { |
2230 | 0 | addElementListener(listener[0], (String) property); |
2231 | } else { | |
2232 | 0 | throw new RuntimeException( |
2233 | "Internal error in addElementListeners"); | |
2234 | } | |
2235 | 0 | } |
2236 | 0 | } |
2237 | ||
2238 | /** | |
2239 | * Update the set of registered listeners to match the given set using | |
2240 | * a minimal update strategy to remove unneeded listeners and add new | |
2241 | * listeners. | |
2242 | * | |
2243 | * @param listenerSet a set of arrays containing a tuple of a UML element | |
2244 | * to be listened to and a set of property to be listened for. | |
2245 | */ | |
2246 | protected void updateElementListeners(Set<Object[]> listenerSet) { | |
2247 | 0 | Set<Object[]> removes = new HashSet<Object[]>(listeners); |
2248 | 0 | removes.removeAll(listenerSet); |
2249 | 0 | removeElementListeners(removes); |
2250 | ||
2251 | 0 | Set<Object[]> adds = new HashSet<Object[]>(listenerSet); |
2252 | 0 | adds.removeAll(listeners); |
2253 | 0 | addElementListeners(adds); |
2254 | 0 | } |
2255 | ||
2256 | /** | |
2257 | * This optional method is not implemented. It will throw an | |
2258 | * {@link UnsupportedOperationException} if used. Figs are | |
2259 | * added to a GraphModel which is, in turn, owned by a project.<p> | |
2260 | * | |
2261 | * @param project the project | |
2262 | * @deprecated | |
2263 | */ | |
2264 | @SuppressWarnings("deprecation") | |
2265 | @Deprecated | |
2266 | public void setProject(Project project) { | |
2267 | 0 | throw new UnsupportedOperationException(); |
2268 | } | |
2269 | ||
2270 | /** | |
2271 | * @deprecated for 0.27.2 by tfmorris. Implementations should have all | |
2272 | * the information that they require in the DiagramSettings object. | |
2273 | * | |
2274 | * @return the owning project | |
2275 | * @see org.argouml.uml.diagram.ui.ArgoFig#getProject() | |
2276 | */ | |
2277 | @Deprecated | |
2278 | public Project getProject() { | |
2279 | 0 | return ArgoFigUtil.getProject(this); |
2280 | } | |
2281 | ||
2282 | /** | |
2283 | * Determine if this Fig is the sole selected target in | |
2284 | * the TargetManager | |
2285 | * @return true if this is the sole target. | |
2286 | */ | |
2287 | protected boolean isSingleTarget() { | |
2288 | 0 | return TargetManager.getInstance().getSingleModelTarget() |
2289 | == getOwner(); | |
2290 | } | |
2291 | ||
2292 | ||
2293 | /** | |
2294 | * @return current stereotype view | |
2295 | * @deprecated for 0.27.2 by tfmorris. Use {@link #getStereotypeStyle()}. | |
2296 | */ | |
2297 | public int getStereotypeView() { | |
2298 | 0 | return stereotypeStyle.ordinal(); |
2299 | } | |
2300 | ||
2301 | /** | |
2302 | * @return | |
2303 | * @see org.argouml.uml.diagram.ui.StereotypeStyled#getStereotypeStyle() | |
2304 | */ | |
2305 | public StereotypeStyle getStereotypeStyle() { | |
2306 | 0 | return stereotypeStyle; |
2307 | } | |
2308 | ||
2309 | /** | |
2310 | * Return a stereotype view which is most practical for the current | |
2311 | * conditions. If the current mode is set to <code>BigIcon</code> mode and | |
2312 | * the model element has zero or more than one stereotype the practical view | |
2313 | * should be the textual view. | |
2314 | * | |
2315 | * @return current practical stereotype view | |
2316 | */ | |
2317 | private int getPracticalView() { | |
2318 | // TODO assert modelElement != null??? | |
2319 | 0 | int practicalView = getStereotypeView(); |
2320 | 0 | Object modelElement = getOwner(); |
2321 | ||
2322 | 0 | if (modelElement != null) { |
2323 | 0 | int stereotypeCount = getStereotypeCount(); |
2324 | ||
2325 | 0 | if (getStereotypeView() |
2326 | == DiagramAppearance.STEREOTYPE_VIEW_BIG_ICON) { | |
2327 | // TODO: Find a way to replace this dependency on Project | |
2328 | 0 | FigNodeStrategy figNodeStrategy = getProject() |
2329 | .getProfileConfiguration().getFigNodeStrategy(); | |
2330 | 0 | Iterator<FigStereotype> figsIterator = getStereotypeFig() |
2331 | .getStereotypeFigs().iterator(); | |
2332 | 0 | Object owner = figsIterator.hasNext() ? |
2333 | figsIterator.next().getOwner() : null; | |
2334 | 0 | if (stereotypeCount != 1 |
2335 | || figNodeStrategy == null | |
2336 | || owner == null | |
2337 | || (stereotypeCount == 1 | |
2338 | && figNodeStrategy.getIconForStereotype(owner) | |
2339 | == null)) { | |
2340 | 0 | practicalView = DiagramAppearance.STEREOTYPE_VIEW_TEXTUAL; |
2341 | } | |
2342 | } | |
2343 | } | |
2344 | 0 | return practicalView; |
2345 | } | |
2346 | ||
2347 | /** | |
2348 | * Get the number of stereotypes contained in this FigNode | |
2349 | * @return the number of stereotypes contained in the FigNode | |
2350 | */ | |
2351 | public int getStereotypeCount() { | |
2352 | 0 | if (getStereotypeFig() == null) { |
2353 | 0 | return 0; |
2354 | } | |
2355 | 0 | return getStereotypeFig().getStereotypeCount(); |
2356 | } | |
2357 | ||
2358 | /** | |
2359 | * Sets the stereotype view. | |
2360 | * | |
2361 | * @param s the stereotype view to be set | |
2362 | * @deprecated for 0.27.2 by tfmorris. Use | |
2363 | * {@link #setStereotypeStyle(StereotypeStyle)}. | |
2364 | */ | |
2365 | public void setStereotypeView(int s) { | |
2366 | 0 | setStereotypeStyle(StereotypeStyle.getEnum(s)); |
2367 | 0 | } |
2368 | ||
2369 | /** | |
2370 | * Set the stereotype style to be used for rendering this fig. | |
2371 | * | |
2372 | * @param style the stereotype style to be set | |
2373 | */ | |
2374 | public void setStereotypeStyle(StereotypeStyle style) { | |
2375 | 0 | stereotypeStyle = style; |
2376 | 0 | renderingChanged(); |
2377 | 0 | } |
2378 | ||
2379 | /** | |
2380 | * Sets the bounds of this node taking the stereotype view into | |
2381 | * consideration.<br> | |
2382 | * | |
2383 | * Do not override this method, override | |
2384 | * {@link #setStandardBounds(int, int, int, int)} instead. | |
2385 | * | |
2386 | * {@inheritDoc} | |
2387 | */ | |
2388 | @Override | |
2389 | protected void setBoundsImpl(final int x, final int y, final int w, | |
2390 | final int h) { | |
2391 | ||
2392 | 0 | if (getPracticalView() == DiagramAppearance.STEREOTYPE_VIEW_BIG_ICON) { |
2393 | 0 | if (stereotypeFigProfileIcon != null) { |
2394 | 0 | stereotypeFigProfileIcon.setBounds(stereotypeFigProfileIcon |
2395 | .getX(), stereotypeFigProfileIcon.getY(), w, h); | |
2396 | // FigClass calls setBoundsImpl before we set | |
2397 | // the stereotypeFigProfileIcon | |
2398 | } | |
2399 | } else { | |
2400 | 0 | setStandardBounds(x, y, w, h); |
2401 | 0 | if (getStereotypeView() |
2402 | == DiagramAppearance.STEREOTYPE_VIEW_SMALL_ICON) { | |
2403 | 0 | updateSmallIcons(w); |
2404 | } | |
2405 | } | |
2406 | 0 | } |
2407 | ||
2408 | private void updateSmallIcons(int wid) { | |
2409 | 0 | int i = this.getX() + wid - ICON_WIDTH - 2; |
2410 | ||
2411 | 0 | for (Fig ficon : floatingStereotypes) { |
2412 | 0 | ficon.setLocation(i, this.getBigPort().getY() + 2); |
2413 | 0 | i -= ICON_WIDTH - 2; |
2414 | } | |
2415 | ||
2416 | 0 | getNameFig().setRightMargin( |
2417 | floatingStereotypes.size() * (ICON_WIDTH + 5)); | |
2418 | 0 | } |
2419 | ||
2420 | /** | |
2421 | * Returns the minimum size of the Fig. This is the smallest size that the | |
2422 | * user can make this Fig by dragging. <p> | |
2423 | * | |
2424 | * Do not call this function if the Fig is not resizable! | |
2425 | * In ArgoUML we decided that it is not needed to implement | |
2426 | * suitable getMinimumSize() methods | |
2427 | * for Figs that are not resizable. | |
2428 | */ | |
2429 | @Override | |
2430 | public Dimension getMinimumSize() { | |
2431 | 0 | assert isResizable(); |
2432 | 0 | return super.getMinimumSize(); |
2433 | } | |
2434 | ||
2435 | /** | |
2436 | * Replaces {@link #setBoundsImpl(int, int, int, int)}. | |
2437 | * | |
2438 | * @param x Desired X coordinate of upper left corner | |
2439 | * @param y Desired Y coordinate of upper left corner | |
2440 | * @param w Desired width of the FigClass | |
2441 | * @param h Desired height of the FigClass | |
2442 | * @see org.tigris.gef.presentation.Fig#setBoundsImpl(int, int, int, int) | |
2443 | */ | |
2444 | protected void setStandardBounds(final int x, final int y, | |
2445 | final int w, final int h) { | |
2446 | ||
2447 | 0 | } |
2448 | ||
2449 | /** | |
2450 | * Handles diagram font changing. | |
2451 | * @param e the event or null | |
2452 | * @see org.argouml.application.events.ArgoDiagramAppearanceEventListener#diagramFontChanged(org.argouml.application.events.ArgoDiagramAppearanceEvent) | |
2453 | * @deprecated for 0.27.2 by tfmorris. The owning diagram manages global | |
2454 | * changes to rendering defaults. | |
2455 | */ | |
2456 | public void diagramFontChanged(ArgoDiagramAppearanceEvent e) { | |
2457 | 0 | updateFont(); |
2458 | 0 | updateBounds(); |
2459 | 0 | damage(); |
2460 | 0 | } |
2461 | ||
2462 | /** | |
2463 | * This function should, for all FigTexts, | |
2464 | * recalculate the font-style (plain, bold, italic, bold/italic), | |
2465 | * and apply it by calling FigText.setFont(). <p> | |
2466 | * | |
2467 | * If the "deepUpdateFont" function does not | |
2468 | * work for a subclass, then override this method. | |
2469 | */ | |
2470 | protected void updateFont() { | |
2471 | 0 | int style = getNameFigFontStyle(); |
2472 | 0 | Font f = getSettings().getFont(style); |
2473 | 0 | nameFig.setFont(f); |
2474 | 0 | deepUpdateFont(this); |
2475 | 0 | } |
2476 | ||
2477 | /** | |
2478 | * Determines the font style based on the UML model. | |
2479 | * Overrule this in Figs that have to show bold or italic based on the | |
2480 | * UML model they represent. | |
2481 | * E.g. abstract classes show their name in italic. | |
2482 | * | |
2483 | * @return the font style for the nameFig. | |
2484 | */ | |
2485 | protected int getNameFigFontStyle() { | |
2486 | // TODO: Why do we need this when we can just change the font and | |
2487 | // achieve the same effect? | |
2488 | 0 | showBoldName = getSettings().isShowBoldNames(); |
2489 | 0 | return showBoldName ? Font.BOLD : Font.PLAIN; |
2490 | } | |
2491 | ||
2492 | /** | |
2493 | * Changes the font for all Figs contained in the given FigGroup. <p> | |
2494 | * | |
2495 | * TODO: In fact, there is a design error in this method: | |
2496 | * E.g. for a class, if the name is Italic since the class is abstract, | |
2497 | * then the classes features should be in Plain font. | |
2498 | * This problem can be fixed by implementing | |
2499 | * the updateFont() method in all subclasses. | |
2500 | * | |
2501 | * @param fg the FigGroup to change the font of. | |
2502 | */ | |
2503 | private void deepUpdateFont(FigGroup fg) { | |
2504 | // TODO: Fonts shouldn't be handled any differently than other | |
2505 | // rendering attributes | |
2506 | 0 | boolean changed = false; |
2507 | 0 | List<Fig> figs = fg.getFigs(); |
2508 | 0 | for (Fig f : figs) { |
2509 | 0 | if (f instanceof ArgoFigText) { |
2510 | 0 | ((ArgoFigText) f).renderingChanged(); |
2511 | 0 | changed = true; |
2512 | } | |
2513 | 0 | if (f instanceof FigGroup) { |
2514 | 0 | deepUpdateFont((FigGroup) f); |
2515 | } | |
2516 | } | |
2517 | 0 | if (changed) { |
2518 | 0 | fg.calcBounds(); |
2519 | } | |
2520 | 0 | } |
2521 | ||
2522 | ||
2523 | public DiagramSettings getSettings() { | |
2524 | // TODO: This is a temporary crutch to use until all Figs are updated | |
2525 | // to use the constructor that accepts a DiagramSettings object | |
2526 | 3213 | if (settings == null) { |
2527 | 0 | LOG.debug("Falling back to project-wide settings"); |
2528 | 0 | Project p = getProject(); |
2529 | 0 | if (p != null) { |
2530 | 0 | return p.getProjectSettings().getDefaultDiagramSettings(); |
2531 | } | |
2532 | } | |
2533 | 3213 | return settings; |
2534 | } | |
2535 | ||
2536 | public void setSettings(DiagramSettings renderSettings) { | |
2537 | 0 | settings = renderSettings; |
2538 | 0 | renderingChanged(); |
2539 | 0 | } |
2540 | ||
2541 | protected NotationSettings getNotationSettings() { | |
2542 | 0 | return notationSettings; |
2543 | } | |
2544 | ||
2545 | public void setLineWidth(int w) { | |
2546 | 0 | super.setLineWidth(w); |
2547 | // Default for name and stereotype is no border | |
2548 | 0 | getNameFig().setLineWidth(0); |
2549 | 0 | if (getStereotypeFig() != null) { |
2550 | 0 | getStereotypeFig().setLineWidth(0); |
2551 | } | |
2552 | 0 | } |
2553 | ||
2554 | /** | |
2555 | * A default "clarifier" to be used for selection if the subclass doesn't | |
2556 | * override makeSelection and provide its own. | |
2557 | */ | |
2558 | 0 | class SelectionDefaultClarifiers extends SelectionNodeClarifiers2 { |
2559 | ||
2560 | /** Construct a new SelectionNodeClarifiers for the given Fig | |
2561 | * | |
2562 | * @param f the given Fig | |
2563 | */ | |
2564 | 0 | private SelectionDefaultClarifiers(Fig f) { |
2565 | 0 | super(f); |
2566 | 0 | } |
2567 | ||
2568 | @Override | |
2569 | protected Icon[] getIcons() { | |
2570 | 0 | return null; |
2571 | } | |
2572 | ||
2573 | @Override | |
2574 | protected String getInstructions(int index) { | |
2575 | 0 | return null; |
2576 | } | |
2577 | ||
2578 | @Override | |
2579 | protected Object getNewNodeType(int index) { | |
2580 | 0 | return null; |
2581 | } | |
2582 | ||
2583 | @Override | |
2584 | protected Object getNewEdgeType(int index) { | |
2585 | 0 | return null; |
2586 | } | |
2587 | ||
2588 | @Override | |
2589 | protected boolean isReverseEdge(int index) { | |
2590 | 0 | return false; |
2591 | } | |
2592 | } | |
2593 | ||
2594 | ||
2595 | /** | |
2596 | * Setting the owner of the Fig must be done in the constructor and not | |
2597 | * changed afterwards for all ArgoUML figs. | |
2598 | * | |
2599 | * @param owner owning UML element | |
2600 | * @see org.tigris.gef.presentation.Fig#setOwner(java.lang.Object) | |
2601 | * @throws UnsupportedOperationException | |
2602 | * @deprecated for 0.27.3 by tfmorris. Set owner in constructor. This method | |
2603 | * is implemented in GEF, so we'll leave this implementation | |
2604 | * here to block any attempts to use it within ArgoUML. | |
2605 | */ | |
2606 | @SuppressWarnings("deprecation") | |
2607 | @Deprecated | |
2608 | public void setOwner(Object owner) { | |
2609 | 0 | if (owner != getOwner()) { |
2610 | 0 | throw new UnsupportedOperationException( |
2611 | "Owner must be set in constructor and left unchanged"); | |
2612 | } | |
2613 | 0 | } |
2614 | ||
2615 | /* | |
2616 | * Override FigNode implementation to keep setOwner from getting called. | |
2617 | */ | |
2618 | @Override | |
2619 | public void bindPort(Object port, Fig f) { | |
2620 | 0 | if (f.getOwner() != port) { |
2621 | 0 | f.setOwner(port); |
2622 | } | |
2623 | 0 | } |
2624 | ||
2625 | public void notationRenderingChanged(NotationProvider np, String rendering) { | |
2626 | 0 | if (notationProviderName == np) { |
2627 | 0 | nameFig.setText(rendering); |
2628 | 0 | updateBounds(); |
2629 | 0 | damage(); |
2630 | } | |
2631 | 0 | } |
2632 | ||
2633 | public NotationSettings getNotationSettings(NotationProvider np) { | |
2634 | 0 | if (notationProviderName == np) { |
2635 | 0 | return getNotationSettings(); |
2636 | } | |
2637 | 0 | return null; |
2638 | } | |
2639 | ||
2640 | public Object getOwner(NotationProvider np) { | |
2641 | 0 | if (notationProviderName == np) { |
2642 | 0 | return getOwner(); |
2643 | } | |
2644 | 0 | return null; |
2645 | } | |
2646 | } |