Line 0
Link Here
|
|
|
1 |
/* |
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
3 |
* |
4 |
* Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
5 |
* |
6 |
* The contents of this file are subject to the terms of either the GNU |
7 |
* General Public License Version 2 only ("GPL") or the Common |
8 |
* Development and Distribution License("CDDL") (collectively, the |
9 |
* "License"). You may not use this file except in compliance with the |
10 |
* License. You can obtain a copy of the License at |
11 |
* http://www.netbeans.org/cddl-gplv2.html |
12 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the |
13 |
* specific language governing permissions and limitations under the |
14 |
* License. When distributing the software, include this License Header |
15 |
* Notice in each file and include the License file at |
16 |
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this |
17 |
* particular file as subject to the "Classpath" exception as provided |
18 |
* by Sun in the GPL Version 2 section of the License file that |
19 |
* accompanied this code. If applicable, add the following below the |
20 |
* License Header, with the fields enclosed by brackets [] replaced by |
21 |
* your own identifying information: |
22 |
* "Portions Copyrighted [year] [name of copyright owner]" |
23 |
* |
24 |
* If you wish your version of this file to be governed by only the CDDL |
25 |
* or only the GPL Version 2, indicate your decision by adding |
26 |
* "[Contributor] elects to include this software in this distribution |
27 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a |
28 |
* single choice of license, a recipient has the option to distribute |
29 |
* your version of this file under either the CDDL, the GPL Version 2 or |
30 |
* to extend the choice of license to its licensees as provided above. |
31 |
* However, if you add GPL Version 2 code and therefore, elected the GPL |
32 |
* Version 2 license, then the option applies only if the new code is |
33 |
* made subject to such option by the copyright holder. |
34 |
* |
35 |
* Contributor(s): |
36 |
* |
37 |
* Portions Copyrighted 2009 Sun Microsystems, Inc. |
38 |
*/ |
39 |
|
40 |
package org.openide.nodes; |
41 |
|
42 |
import java.awt.Graphics; |
43 |
import java.awt.Image; |
44 |
import java.awt.image.ImageObserver; |
45 |
import java.awt.image.ImageProducer; |
46 |
import java.lang.reflect.Method; |
47 |
import java.util.Collections; |
48 |
import java.util.LinkedList; |
49 |
import java.util.List; |
50 |
import java.util.Map; |
51 |
import java.util.WeakHashMap; |
52 |
import javax.swing.Action; |
53 |
import org.openide.util.Exceptions; |
54 |
import org.openide.util.ImageUtilities; |
55 |
import org.openide.util.Lookup; |
56 |
import org.openide.util.lookup.Lookups; |
57 |
import org.openide.util.lookup.ProxyLookup; |
58 |
|
59 |
/** |
60 |
* Node subclass for simple use-cases where the icon and display name are |
61 |
* known ahead of time, lookup contents may be known ahead of time, which |
62 |
* takes care of computing its child nodes without exposing a Children |
63 |
* object. |
64 |
* <p/> |
65 |
* This class basically simplifies the following common cases for using |
66 |
* AbstractNode + Children.Keys: |
67 |
* <ul> |
68 |
* <li>Icon can be passed as constructor parameter, without a subclass being |
69 |
* required</li> |
70 |
* <li>Display name can be annotated with HTML without subclassing, either |
71 |
* using <code>setValue (SimpleNode.ERROR, Boolean.TRUE)</code> or, e.g., |
72 |
* <code>setValue(SimpleNode.HTML_PREFIX, "<b>")</code></li> |
73 |
* <li>Children objects do not need to be dealt with directly - simply |
74 |
* override <code>createKeys(List)</code> and <code>createNodeForKey(KeyType)</code></li> |
75 |
* <li>Actions can be handled more simply, and the default Properties action |
76 |
* is suppressed</li> |
77 |
* <li>Fixed Lookup contents can be passed in directly using one of the |
78 |
* static factory methods</li> |
79 |
* <li>No lookup, or fixed lookup contents may be passed into constructor, |
80 |
* or createLookup() can be overridden (but not both). The Lookup does not |
81 |
* need to exist at the time the node is constructed, eliminating some |
82 |
* awkward code constructs that would otherwise be needed. |
83 |
* </li> |
84 |
* </ul> |
85 |
* Note: Do not call <code>getCookieSet()</code> to alter the contents of the |
86 |
* lookup of a SimpleNode - it is not used. Instead, pass an appropriate |
87 |
* lookup as a constructor parameter. |
88 |
* <p/> |
89 |
* <h2>Managing Child Nodes</h2> |
90 |
* If your node should have a set of child nodes that can be shown when your |
91 |
* node is expanded, subclass SimpleNode and override <code>createKeys(List)</code> |
92 |
* and <code>createNodeForKey(KeyType)</code>. |
93 |
* <p/> |
94 |
* <b>How this works:</b> In <code>createKeys(List)</code>, you create (or fetch |
95 |
* somehow) model objects each of which represents one child node, and add them |
96 |
* to the passed list. Later, when the nodes really need to be shown, |
97 |
* <code>createNodeForKey(KeyType)</code> is called once for every object you |
98 |
* added to the list in <code>createKeys(List)</code>. |
99 |
* <p/> |
100 |
* If you need to update the set of child nodes because something has changed, |
101 |
* simply call <code>refreshChildren()</code> - this will trigger another call |
102 |
* to <code>createKeys(List)</code> and so forth. |
103 |
* <p/> |
104 |
* If the set of child nodes can change due to external code, and you can |
105 |
* listen for those changes, override <code>onStartListeningForChildChanges()</code> |
106 |
* to attach your listeners and <code>onStopListeningForChildChanges()</code> |
107 |
* <p/> |
108 |
* If you override <code>createKeys(List)</code>, you must also override |
109 |
* <code>createNodeForKey(KeyType)</code>. |
110 |
* |
111 |
* |
112 |
* @since 7.12 |
113 |
* @author Tim Boudreau |
114 |
* @param KeyType the type of object used for the key objects that |
115 |
* represent child nodes. If your node has no children, use |
116 |
* <code>SimpleNode<Void></code> |
117 |
*/ |
118 |
public class SimpleNode<KeyType> extends AbstractNode { |
119 |
private final Image icon; |
120 |
/** |
121 |
* Key which can be used in a call to setValue(ERROR, Boolean), to |
122 |
* specify that this node should receive error badging in its display |
123 |
* name and/or icon |
124 |
*/ |
125 |
public static final String ERROR = "_error"; //NOI18N |
126 |
|
127 |
/** |
128 |
* Key which can be used in a call to setValue(HTML_PREFIX, String), to |
129 |
* specify an HTML prefix for the display name of this node, to affect |
130 |
* its appearance somehow |
131 |
*/ |
132 |
public static final String HTML_PREFIX = "_prefix"; //NOI18N |
133 |
/** |
134 |
* Flag which is true if setValue() has ever been called - an optimization |
135 |
* for handling getHtmlDisplayName() |
136 |
*/ |
137 |
private volatile boolean valueSet; |
138 |
private final CF<KeyType> childFactory; |
139 |
|
140 |
/** |
141 |
* Create a new SimpleNode with unspecified lookup, display name, |
142 |
* and lookup contents |
143 |
*/ |
144 |
public SimpleNode() { |
145 |
this (null, (Image) null, (Lookup) null); |
146 |
} |
147 |
|
148 |
/** |
149 |
* Create a new SimpleNode with the passed display name and |
150 |
* icon resource path |
151 |
* @param displayName A localized display name |
152 |
* @param iconResourcePath A resource path to an icon, e.g. com/foo/bar/Icon.png |
153 |
*/ |
154 |
public SimpleNode (String displayName, String iconResourcePath) { |
155 |
this (displayName, new PathIcon(iconResourcePath)); |
156 |
} |
157 |
|
158 |
/** |
159 |
* Create a new SimpleNode with the specified display name and icon |
160 |
* @param displayName The display name, or null |
161 |
* @param icon The icon, or null |
162 |
*/ |
163 |
public SimpleNode (String displayName, Image icon) { |
164 |
this (displayName, icon, (Lookup) null); |
165 |
} |
166 |
|
167 |
/** |
168 |
* Create a new SimpleNode with the specified display name and lookup |
169 |
* contents |
170 |
* @param displayName The display name, or null |
171 |
* @param lookupContents The lookup contents, or null |
172 |
*/ |
173 |
public static Node create (String displayName, Object... lookupContents) { |
174 |
return new SimpleNode<Void>(displayName, lookupContents); |
175 |
} |
176 |
|
177 |
/** |
178 |
* Create a new SimpleNode with the specified display name, icon and lookup |
179 |
* contents |
180 |
* @param displayName The display name, or null |
181 |
* @param lookupContents The lookup contents, or null |
182 |
*/ |
183 |
public static Node create (String displayName, Image icon, Object... lookupContents) { |
184 |
return new SimpleNode<Void>(displayName, icon, lookupContents); |
185 |
} |
186 |
|
187 |
/** |
188 |
* Create a new SimpleNode with the specified display name, icon and lookup |
189 |
* contents |
190 |
* @param displayName The display name, or null |
191 |
* @param iconResourcePath A resource path to an icon, e.g. com/foo/bar/Icon.png |
192 |
* @param lookupContents The lookup contents, or null |
193 |
*/ |
194 |
public static Node create (String displayName, String iconResourcePath, Object... lookupContents) { |
195 |
return new SimpleNode<Void>(displayName, iconResourcePath == null ? null : new PathIcon(iconResourcePath), lookupContents); |
196 |
} |
197 |
|
198 |
|
199 |
/** |
200 |
* Create a new SimpleNode with the specified display name and lookup |
201 |
* contents |
202 |
* @param displayName The display name, or null |
203 |
* @param lookupContents The lookup contents, or null |
204 |
*/ |
205 |
SimpleNode (String displayName, Object... lookupContents) { |
206 |
//package private to avoid constructor calls requiring extra casts |
207 |
this (displayName, null, lookupContents); |
208 |
} |
209 |
|
210 |
/** |
211 |
* Create a new SimpleNode with the specified display name, icon and lookup contents |
212 |
* @param displayName The display name, or null |
213 |
* @param icon The icon, or null |
214 |
* @param lookupContents the contents of the lookup (may be empty but not |
215 |
* null) |
216 |
*/ |
217 |
SimpleNode (String displayName, Image icon, Object... lookupContents) { |
218 |
//package private to avoid constructor calls requiring extra casts |
219 |
this (displayName, icon, Lookups.fixed(lookupContents)); |
220 |
} |
221 |
|
222 |
/** |
223 |
* Create a new SimpleNode with the specified display name, icon and lookup |
224 |
* @param displayName The display name, or null |
225 |
* @param iconResourcePath A resource path to an icon, e.g. com/foo/bar/Icon.png |
226 |
* @param lookup The lookup, or null |
227 |
*/ |
228 |
public SimpleNode (String displayName, String iconResourcePath, Lookup lookup) { |
229 |
this (displayName, iconResourcePath == null ? null : new PathIcon (iconResourcePath), lookup); |
230 |
} |
231 |
|
232 |
/** |
233 |
* Create a new SimpleNode with the specified display name, icon and lookup |
234 |
* @param displayName The display name, or null |
235 |
* @param icon The icon, or null |
236 |
* @param lookup The lookup, or null |
237 |
*/ |
238 |
public SimpleNode (String displayName, Image icon, Lookup lookup) { |
239 |
super(Children.LEAF, new L()); |
240 |
if (lookup != null) assert !overridesCreateLookup() : "Passing a " + //NOI18N |
241 |
"non-null lookup to a SimpleNode which overrides createLookup." + //NOI18N |
242 |
"Passed lookup will not be used"; //NOI18N |
243 |
if (displayName != null) { |
244 |
setName (displayName); |
245 |
setDisplayName(displayName); |
246 |
} |
247 |
((L) getLookup()).set(lookup == null ? createLookup() : lookup); |
248 |
if (overridesCreateKeys()) { |
249 |
setChildren (Children.create(childFactory = new CF<KeyType>(this), true)); |
250 |
} else { |
251 |
childFactory = null; |
252 |
} |
253 |
this.icon = icon; |
254 |
} |
255 |
|
256 |
/** |
257 |
* Create this node's lookup. This method is called once, from the |
258 |
* superclass constructor (be careful not to refer to instance fields |
259 |
* if you override this method), and only if no Lookup or array of |
260 |
* Lookup contents was passed to the constructor. |
261 |
* |
262 |
* @return A Lookup. The default implementation returns |
263 |
* Lookups.singleton(this) |
264 |
*/ |
265 |
protected Lookup createLookup() { |
266 |
return Lookups.singleton(this); |
267 |
} |
268 |
|
269 |
/** |
270 |
* Populate a list of "key" objects which will be passed |
271 |
* individually to createNodeForKey() to create this node's child nodes. |
272 |
* Think of this as keys in a map, where the values are Nodes. Create |
273 |
* your collection of keys in this method, and add them all to the passed |
274 |
* list. Later, when the nodes need to be displayed, the system will |
275 |
* call createNodeForKey(), passing each key you provided individually, |
276 |
* at which point you create a child node. |
277 |
* <p/> |
278 |
* If you override this method, <i>you must also override createNodeForKey()</i> |
279 |
* or an exception will be thrown if your node is expanded at runtime. |
280 |
* <p/> |
281 |
* Note that this method is not called on the AWT event thread, but on a |
282 |
* background thread. Code inside this method should not directly |
283 |
* call other code that is not thread-safe. |
284 |
* <p/> |
285 |
* If your node should not have child nodes, simply do not override this |
286 |
* method. |
287 |
* @param toPopulate A list which can be added to |
288 |
* @return true if the list has been fully populated, false if another call |
289 |
* to createKeys() should be enqueued to add more key objects to the list |
290 |
* (do this if computing the keys is very slow, perhaps involving I/O, |
291 |
* and you want to show a partial result). |
292 |
*/ |
293 |
protected boolean createKeys(List<KeyType> toPopulate) { |
294 |
return true; |
295 |
} |
296 |
|
297 |
/** |
298 |
* Call this method if the list of children of this node changes, to trigger |
299 |
* an update of its children. |
300 |
* |
301 |
* @param immediate If true, the children should be updated immediately |
302 |
* (resulting in a synchronous call to createKeys() - if creating the keys |
303 |
* is slow, do not call this method with an argument of true from the |
304 |
* AWT event thread) |
305 |
*/ |
306 |
protected final void refreshChildren(boolean immediate) { |
307 |
if (childFactory != null) { |
308 |
childFactory.refresh(immediate); |
309 |
} |
310 |
} |
311 |
|
312 |
/** |
313 |
* Create a child node for the passed key object. The key abject is one |
314 |
* of the objects created and added to the passed list in toPopulate. |
315 |
* |
316 |
* @param key The key object |
317 |
* @return A node |
318 |
*/ |
319 |
protected Node createChildNodeFor(KeyType key) { |
320 |
// XXX new BeanNode(key) ? |
321 |
throw new UnsupportedOperationException("Must override createNodeForKey"); //NOI18N |
322 |
} |
323 |
|
324 |
/** |
325 |
* Called when the user expands this node in the UI, or something |
326 |
* programmatically expresses an interest in the children of this node. |
327 |
* If the set of child nodes can change due to external events, such as |
328 |
* a file being deleted or created, start listening for changes in whatever |
329 |
* model object determines the set of child nodes here. |
330 |
* <p/> |
331 |
* The default implementation does nothing. This method will only ever |
332 |
* be called if you are overriding <code>createKeys()</code> and |
333 |
* <code>createNodeForKey()</code> |
334 |
*/ |
335 |
protected void onStartListeningForChildChanges() { |
336 |
//do nothing |
337 |
} |
338 |
|
339 |
/** |
340 |
* Called some time after the user has collapsed this node in the UI, or the |
341 |
* last listener interested in the children of this node is removed or |
342 |
* garbage collected.. |
343 |
* If the set of child nodes can change due to external events, such as |
344 |
* a file being deleted or created, detach your listeners from whatever |
345 |
* model object determines the children here. |
346 |
* <p/> |
347 |
* The default implementation does nothing. This method will only ever |
348 |
* be called if you are overriding <code>createKeys()</code> and |
349 |
* <code>createNodeForKey()</code> |
350 |
*/ |
351 |
protected void onStopListeningForChildChanges() { |
352 |
//do nothing |
353 |
} |
354 |
|
355 |
|
356 |
/** |
357 |
* Overridden to be final, to prevent overriding in subclasses. To |
358 |
* affect the contents of this Node's Lookup (and thus the return values |
359 |
* of getCookie()), provide your own Lookup as a constructor parameter or |
360 |
* as the return value from <code>createLookup()</code> |
361 |
* @param <T> the cookie type |
362 |
* @param type the cookie type |
363 |
* @return a object of type T |
364 |
* @deprecated use getLookup().lookup(Class) instead |
365 |
*/ |
366 |
@Deprecated |
367 |
@Override |
368 |
public final <T extends Cookie> T getCookie(Class<T> type) { |
369 |
return super.getCookie(type); |
370 |
} |
371 |
|
372 |
/** |
373 |
* Overridden to make use of flags that can be set on this node. |
374 |
* Call setValue(SimpleNode.ERROR, Boolean.TRUE) to have this node's |
375 |
* title show up in the default error color (usually red). Call |
376 |
* setValue (SimpleNode.HTML_PREFIX, "<b>") with an html prefix |
377 |
* to prepend to the display name. |
378 |
* <p/> |
379 |
* Note that if you override this method, calls to setValue(HTML_PREFIX, String) |
380 |
* and setValue(ERROR, Boolean) will have no effect unless you handle these |
381 |
* values in your own code or call super.getHtmlDisplayName() and decorate |
382 |
* but do not replace the result. |
383 |
* |
384 |
* @return The node's display name decorated with HTML or null if HTML |
385 |
* is not needed |
386 |
*/ |
387 |
@Override |
388 |
public String getHtmlDisplayName() { |
389 |
if (valueSet) { |
390 |
String pfix = (String) getValue(HTML_PREFIX); |
391 |
boolean err = Boolean.TRUE.equals(getValue(ERROR)); |
392 |
if (err) { |
393 |
String errHead = "<font color='!nb.errorForeground'>"; //NOI18N |
394 |
pfix = pfix == null ? errHead : pfix + errHead; |
395 |
return pfix + getDisplayName(); |
396 |
} else if (pfix != null) { |
397 |
return pfix + getDisplayName(); |
398 |
} |
399 |
} |
400 |
return super.getHtmlDisplayName(); |
401 |
} |
402 |
|
403 |
/** |
404 |
* Allows nodes to contain ad-hoc key/value pairs. Will trigger a |
405 |
* display name change event if a key which affects html display name |
406 |
* is passed |
407 |
* @param key The key |
408 |
* @param o The value |
409 |
*/ |
410 |
@Override |
411 |
public final void setValue(String key, Object o) { |
412 |
valueSet = true; |
413 |
boolean isChange = ERROR.equals(key) || HTML_PREFIX.equals(key); |
414 |
String oldName = isChange ? getDisplayName() : null; |
415 |
super.setValue(key, o); |
416 |
if (isChange) { |
417 |
fireDisplayNameChange(oldName, getDisplayName()); |
418 |
if (ERROR.equals(key)) { |
419 |
fireIconChange(); |
420 |
} |
421 |
} |
422 |
} |
423 |
|
424 |
/** |
425 |
* Overridden to return the icon passed as a constructor argument, if any. |
426 |
* @param type The icon type, |
427 |
* @return the icon passed to the constructor, or the return value of |
428 |
* a call to super.getIcon(type) if that was null |
429 |
*/ |
430 |
@Override |
431 |
public Image getIcon(int type) { |
432 |
Image result = icon == null ? super.getIcon(type) : |
433 |
icon instanceof PathIcon ? ((PathIcon) icon).actualImage() : icon; |
434 |
return badge(result); |
435 |
} |
436 |
|
437 |
/** |
438 |
* Overridden to return the icon passed as a constructor argument, or |
439 |
* if null, the return value of super.getOpenedIcon(type). |
440 |
* @param type The icon type, |
441 |
* @return the icon passed to the constructor, or the return value of |
442 |
* a call to super.getOpenedIcon(type) if that was null. |
443 |
*/ |
444 |
@Override |
445 |
public Image getOpenedIcon(int type) { |
446 |
Image result = icon == null ? super.getOpenedIcon(type) : |
447 |
icon instanceof PathIcon ? ((PathIcon) icon).actualImage() : icon; |
448 |
return badge(result); |
449 |
} |
450 |
|
451 |
private Image badge(Image img) { |
452 |
if (img != null && valueSet && Boolean.TRUE.equals(getValue(ERROR))) { |
453 |
Image badge = ImageUtilities.loadImage( |
454 |
"org/openide/nodes/errorBadge.png"); //NOI18N |
455 |
img = ImageUtilities.mergeImages(img, badge, 8, 8); |
456 |
} |
457 |
return img; |
458 |
} |
459 |
|
460 |
/** |
461 |
* Populate the passed list with any actions which should be available |
462 |
* @param actions A list of actions which can be added to |
463 |
*/ |
464 |
public void createActions(List<Action> actions) { |
465 |
//do nothing |
466 |
} |
467 |
|
468 |
/** |
469 |
* Overridden as final to delegate to createActions(List) |
470 |
* @param context ignored |
471 |
* @return an array of actions |
472 |
*/ |
473 |
@Override |
474 |
public final Action[] getActions(boolean context) { |
475 |
List<Action> l = new LinkedList<Action>(); |
476 |
createActions(l); |
477 |
return l.toArray(new Action[0]); |
478 |
} |
479 |
|
480 |
private static class CF<KeyType> extends ChildFactory.Detachable <KeyType> { |
481 |
private final SimpleNode<KeyType> nd; |
482 |
CF (SimpleNode<KeyType> nd) { |
483 |
this.nd = nd; |
484 |
} |
485 |
|
486 |
@Override |
487 |
protected boolean createKeys(List<KeyType> toPopulate) { |
488 |
return nd.createKeys(toPopulate); |
489 |
} |
490 |
|
491 |
@Override |
492 |
protected Node createNodeForKey(KeyType key) { |
493 |
return nd.createChildNodeFor(key); |
494 |
} |
495 |
|
496 |
@Override |
497 |
protected void addNotify() { |
498 |
nd.onStartListeningForChildChanges(); |
499 |
} |
500 |
|
501 |
@Override |
502 |
protected void removeNotify() { |
503 |
nd.onStopListeningForChildChanges(); |
504 |
} |
505 |
} |
506 |
|
507 |
private static final class L extends ProxyLookup { |
508 |
void set(Lookup real) { |
509 |
setLookups(real); |
510 |
} |
511 |
} |
512 |
|
513 |
private static final Map<Class<?>, Boolean> CREATE_KEYS_OVERRIDERS = |
514 |
Collections.synchronizedMap(new WeakHashMap<Class<?>,Boolean>()); |
515 |
/** |
516 |
* Used to determine if Children.LEAF should be retained as the |
517 |
* Children object |
518 |
* @return true if this class overrides the createKeys() method |
519 |
*/ |
520 |
private boolean overridesCreateKeys() { |
521 |
if (getClass() == SimpleNode.class) return false; |
522 |
Boolean val = CREATE_KEYS_OVERRIDERS.get(getClass()); |
523 |
if (val == null) { |
524 |
try { |
525 |
val = overrides ("createKeys", List.class); |
526 |
CREATE_KEYS_OVERRIDERS.put (getClass(), val); |
527 |
} catch (SecurityException ex) { |
528 |
Exceptions.printStackTrace(ex); |
529 |
val = false; |
530 |
} |
531 |
} |
532 |
return val; |
533 |
} |
534 |
|
535 |
private boolean overrides (String name, Class... ptypes) { |
536 |
Class<?> type = getClass(); |
537 |
boolean result = false; |
538 |
while (type != SimpleNode.class) { |
539 |
try { |
540 |
type.getDeclaredMethod(name, ptypes); |
541 |
result = true; |
542 |
break; |
543 |
} catch (NoSuchMethodException e) { |
544 |
//do nothing |
545 |
} |
546 |
type = type.getSuperclass(); |
547 |
} |
548 |
return result; |
549 |
} |
550 |
|
551 |
/** |
552 |
* Used to trigger an assertion error of createLookup() is overridden |
553 |
* and a non-null lookup is also passed to the constructor |
554 |
* @return whether or not this subclass overrides createLookup() |
555 |
*/ |
556 |
private boolean overridesCreateLookup() { |
557 |
if (getClass() == SimpleNode.class) return false; |
558 |
try { |
559 |
return overrides ("createLookup"); |
560 |
} catch (SecurityException ex) { |
561 |
Exceptions.printStackTrace(ex); |
562 |
} |
563 |
return false; |
564 |
} |
565 |
|
566 |
private static final class PathIcon extends Image { |
567 |
private final String path; |
568 |
PathIcon(String path) { |
569 |
this.path = path; |
570 |
} |
571 |
|
572 |
Image actualImage() { |
573 |
return ImageUtilities.loadImage(path); |
574 |
} |
575 |
|
576 |
|
577 |
@Override |
578 |
public int getWidth(ImageObserver observer) { |
579 |
throw new AssertionError(); |
580 |
} |
581 |
|
582 |
@Override |
583 |
public int getHeight(ImageObserver observer) { |
584 |
throw new AssertionError(); |
585 |
} |
586 |
|
587 |
@Override |
588 |
public ImageProducer getSource() { |
589 |
throw new AssertionError(); |
590 |
} |
591 |
|
592 |
@Override |
593 |
public Graphics getGraphics() { |
594 |
throw new AssertionError(); |
595 |
} |
596 |
|
597 |
@Override |
598 |
public Object getProperty(String name, ImageObserver observer) { |
599 |
throw new AssertionError(); |
600 |
} |
601 |
|
602 |
} |
603 |
} |