author | peterz |
Mon, 21 Dec 2009 19:26:58 +0300 | |
changeset 4841 | ae658e3b0f27 |
parent 3932 | c9cd7ff79037 |
child 5506 | 202f599c92aa |
permissions | -rw-r--r-- |
2658 | 1 |
/* |
2 |
* Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. |
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
7 |
* published by the Free Software Foundation. Sun designates this |
|
8 |
* particular file as subject to the "Classpath" exception as provided |
|
9 |
* by Sun in the LICENSE file that accompanied this code. |
|
10 |
* |
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
21 |
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 |
* CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 |
* have any questions. |
|
24 |
*/ |
|
25 |
package ${PACKAGE}; |
|
26 |
||
27 |
import javax.swing.Painter; |
|
28 |
import java.awt.Graphics; |
|
3928 | 29 |
import sun.font.FontUtilities; |
2658 | 30 |
import sun.swing.plaf.synth.DefaultSynthStyle; |
31 |
import javax.swing.BorderFactory; |
|
32 |
import javax.swing.JComponent; |
|
33 |
import javax.swing.JInternalFrame; |
|
34 |
import javax.swing.UIDefaults; |
|
35 |
import javax.swing.UIManager; |
|
36 |
import javax.swing.plaf.BorderUIResource; |
|
37 |
import javax.swing.plaf.ColorUIResource; |
|
38 |
import javax.swing.plaf.DimensionUIResource; |
|
39 |
import javax.swing.plaf.FontUIResource; |
|
40 |
import javax.swing.plaf.InsetsUIResource; |
|
41 |
import javax.swing.plaf.synth.Region; |
|
42 |
import javax.swing.plaf.synth.SynthStyle; |
|
43 |
import java.awt.Color; |
|
44 |
import java.awt.Component; |
|
45 |
import java.awt.Dimension; |
|
46 |
import java.awt.Font; |
|
47 |
import java.awt.Graphics2D; |
|
48 |
import java.awt.Insets; |
|
49 |
import java.awt.image.BufferedImage; |
|
50 |
import static java.awt.image.BufferedImage.*; |
|
51 |
import java.beans.PropertyChangeEvent; |
|
52 |
import java.beans.PropertyChangeListener; |
|
53 |
import java.lang.ref.WeakReference; |
|
54 |
import java.lang.reflect.Constructor; |
|
55 |
import java.util.ArrayList; |
|
56 |
import java.util.HashMap; |
|
57 |
import java.util.LinkedList; |
|
58 |
import java.util.List; |
|
59 |
import java.util.Map; |
|
60 |
import java.util.Set; |
|
61 |
import java.util.WeakHashMap; |
|
62 |
import javax.swing.border.Border; |
|
63 |
import javax.swing.plaf.UIResource; |
|
64 |
||
65 |
/** |
|
66 |
* This class contains all the implementation details related to |
|
67 |
* ${LAF_NAME}. It contains all the code for initializing the UIDefaults table, |
|
68 |
* as well as for selecting |
|
69 |
* a SynthStyle based on a JComponent/Region pair. |
|
70 |
* |
|
71 |
* @author Richard Bair |
|
72 |
*/ |
|
73 |
final class ${LAF_NAME}Defaults { |
|
74 |
/** |
|
75 |
* The map of SynthStyles. This map is keyed by Region. Each Region maps |
|
76 |
* to a List of LazyStyles. Each LazyStyle has a reference to the prefix |
|
77 |
* that was registered with it. This reference can then be inspected to see |
|
78 |
* if it is the proper lazy style. |
|
79 |
* <p/> |
|
80 |
* There can be more than one LazyStyle for a single Region if there is more |
|
81 |
* than one prefix defined for a given region. For example, both Button and |
|
82 |
* "MyButton" might be prefixes assigned to the Region.Button region. |
|
83 |
*/ |
|
84 |
private Map<Region, List<LazyStyle>> m; |
|
85 |
/** |
|
86 |
* A map of regions which have been registered. |
|
87 |
* This mapping is maintained so that the Region can be found based on |
|
88 |
* prefix in a very fast manner. This is used in the "matches" method of |
|
89 |
* LazyStyle. |
|
90 |
*/ |
|
91 |
private Map<String, Region> registeredRegions = |
|
92 |
new HashMap<String, Region>(); |
|
4841
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
93 |
|
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
94 |
private Map<JComponent, Map<Region, SynthStyle>> overridesCache = |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
95 |
new WeakHashMap<JComponent, Map<Region, SynthStyle>>(); |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
96 |
|
2658 | 97 |
/** |
98 |
* Our fallback style to avoid NPEs if the proper style cannot be found in |
|
99 |
* this class. Not sure if relying on DefaultSynthStyle is the best choice. |
|
100 |
*/ |
|
101 |
private DefaultSynthStyle defaultStyle; |
|
102 |
/** |
|
103 |
* The default font that will be used. I store this value so that it can be |
|
104 |
* set in the UIDefaults when requested. |
|
105 |
*/ |
|
106 |
private FontUIResource defaultFont; |
|
107 |
||
3741 | 108 |
private ColorTree colorTree = new ColorTree(); |
2658 | 109 |
|
110 |
/** Listener for changes to user defaults table */ |
|
111 |
private DefaultsListener defaultsListener = new DefaultsListener(); |
|
112 |
||
113 |
/** Called by UIManager when this look and feel is installed. */ |
|
114 |
void initialize() { |
|
115 |
// add listener for derived colors |
|
116 |
UIManager.addPropertyChangeListener(defaultsListener); |
|
3741 | 117 |
UIManager.getDefaults().addPropertyChangeListener(colorTree); |
2658 | 118 |
} |
119 |
||
120 |
/** Called by UIManager when this look and feel is uninstalled. */ |
|
121 |
void uninitialize() { |
|
122 |
// remove listener for derived colors |
|
123 |
UIManager.removePropertyChangeListener(defaultsListener); |
|
3741 | 124 |
UIManager.getDefaults().removePropertyChangeListener(colorTree); |
2658 | 125 |
} |
126 |
||
127 |
/** |
|
128 |
* Create a new ${LAF_NAME}Defaults. This constructor is only called from |
|
129 |
* within ${LAF_NAME}LookAndFeel. |
|
130 |
*/ |
|
131 |
${LAF_NAME}Defaults() { |
|
132 |
m = new HashMap<Region, List<LazyStyle>>(); |
|
133 |
||
134 |
//Create the default font and default style. Also register all of the |
|
135 |
//regions and their states that this class will use for later lookup. |
|
136 |
//Additional regions can be registered later by 3rd party components. |
|
137 |
//These are simply the default registrations. |
|
3928 | 138 |
defaultFont = FontUtilities.getFontConfigFUIR("sans", Font.PLAIN, 12); |
2658 | 139 |
defaultStyle = new DefaultSynthStyle(); |
140 |
defaultStyle.setFont(defaultFont); |
|
141 |
||
142 |
//initialize the map of styles |
|
143 |
${STYLE_INIT} |
|
144 |
} |
|
145 |
||
146 |
//--------------- Methods called by ${LAF_NAME}LookAndFeel |
|
147 |
||
148 |
/** |
|
149 |
* Called from ${LAF_NAME}LookAndFeel to initialize the UIDefaults. |
|
150 |
* |
|
151 |
* @param d UIDefaults table to initialize. This will never be null. |
|
152 |
* If listeners are attached to <code>d</code>, then you will |
|
153 |
* only receive notification of LookAndFeel level defaults, not |
|
154 |
* all defaults on the UIManager. |
|
155 |
*/ |
|
156 |
void initializeDefaults(UIDefaults d) { |
|
157 |
${UI_DEFAULT_INIT} |
|
158 |
} |
|
159 |
||
160 |
/** |
|
161 |
* <p>Registers the given region and prefix. The prefix, if it contains |
|
162 |
* quoted sections, refers to certain named components. If there are not |
|
163 |
* quoted sections, then the prefix refers to a generic component type.</p> |
|
164 |
* |
|
165 |
* <p>If the given region/prefix combo has already been registered, then |
|
166 |
* it will not be registered twice. The second registration attempt will |
|
167 |
* fail silently.</p> |
|
168 |
* |
|
169 |
* @param region The Synth Region that is being registered. Such as Button, |
|
170 |
* or ScrollBarThumb. |
|
171 |
* @param prefix The UIDefault prefix. For example, could be ComboBox, or if |
|
172 |
* a named components, "MyComboBox", or even something like |
|
173 |
* ToolBar:"MyComboBox":"ComboBox.arrowButton" |
|
174 |
*/ |
|
175 |
void register(Region region, String prefix) { |
|
176 |
//validate the method arguments |
|
177 |
if (region == null || prefix == null) { |
|
178 |
throw new IllegalArgumentException( |
|
179 |
"Neither Region nor Prefix may be null"); |
|
180 |
} |
|
181 |
||
182 |
//Add a LazyStyle for this region/prefix to m. |
|
183 |
List<LazyStyle> styles = m.get(region); |
|
184 |
if (styles == null) { |
|
185 |
styles = new LinkedList<LazyStyle>(); |
|
186 |
styles.add(new LazyStyle(prefix)); |
|
187 |
m.put(region, styles); |
|
188 |
} else { |
|
189 |
//iterate over all the current styles and see if this prefix has |
|
190 |
//already been registered. If not, then register it. |
|
191 |
for (LazyStyle s : styles) { |
|
192 |
if (prefix.equals(s.prefix)) { |
|
193 |
return; |
|
194 |
} |
|
195 |
} |
|
196 |
styles.add(new LazyStyle(prefix)); |
|
197 |
} |
|
198 |
||
199 |
//add this region to the map of registered regions |
|
200 |
registeredRegions.put(region.getName(), region); |
|
201 |
} |
|
202 |
||
203 |
/** |
|
204 |
* <p>Locate the style associated with the given region, and component. |
|
205 |
* This is called from ${LAF_NAME}LookAndFeel in the SynthStyleFactory |
|
206 |
* implementation.</p> |
|
207 |
* |
|
208 |
* <p>Lookup occurs as follows:<br/> |
|
209 |
* Check the map of styles <code>m</code>. If the map contains no styles at |
|
210 |
* all, then simply return the defaultStyle. If the map contains styles, |
|
211 |
* then iterate over all of the styles for the Region <code>r</code> looking |
|
212 |
* for the best match, based on prefix. If a match was made, then return |
|
213 |
* that SynthStyle. Otherwise, return the defaultStyle.</p> |
|
214 |
* |
|
215 |
* @param comp The component associated with this region. For example, if |
|
216 |
* the Region is Region.Button then the component will be a JButton. |
|
217 |
* If the Region is a subregion, such as ScrollBarThumb, then the |
|
218 |
* associated component will be the component that subregion belongs |
|
219 |
* to, such as JScrollBar. The JComponent may be named. It may not be |
|
220 |
* null. |
|
221 |
* @param r The region we are looking for a style for. May not be null. |
|
222 |
*/ |
|
223 |
SynthStyle getStyle(JComponent comp, Region r) { |
|
224 |
//validate method arguments |
|
225 |
if (comp == null || r == null) { |
|
226 |
throw new IllegalArgumentException( |
|
227 |
"Neither comp nor r may be null"); |
|
228 |
} |
|
229 |
||
230 |
//if there are no lazy styles registered for the region r, then return |
|
231 |
//the default style |
|
232 |
List<LazyStyle> styles = m.get(r); |
|
233 |
if (styles == null || styles.size() == 0) { |
|
234 |
return defaultStyle; |
|
235 |
} |
|
236 |
||
237 |
//Look for the best SynthStyle for this component/region pair. |
|
238 |
LazyStyle foundStyle = null; |
|
239 |
for (LazyStyle s : styles) { |
|
240 |
if (s.matches(comp)) { |
|
241 |
//replace the foundStyle if foundStyle is null, or |
|
242 |
//if the new style "s" is more specific (ie, its path was |
|
243 |
//longer), or if the foundStyle was "simple" and the new style |
|
244 |
//was not (ie: the foundStyle was for something like Button and |
|
245 |
//the new style was for something like "MyButton", hence, being |
|
246 |
//more specific.) In all cases, favor the most specific style |
|
247 |
//found. |
|
248 |
if (foundStyle == null || |
|
249 |
(foundStyle.parts.length < s.parts.length) || |
|
250 |
(foundStyle.parts.length == s.parts.length |
|
251 |
&& foundStyle.simple && !s.simple)) { |
|
252 |
foundStyle = s; |
|
253 |
} |
|
254 |
} |
|
255 |
} |
|
256 |
||
257 |
//return the style, if found, or the default style if not found |
|
4841
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
258 |
return foundStyle == null ? defaultStyle : foundStyle.getStyle(comp, r); |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
259 |
} |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
260 |
|
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
261 |
public void clearOverridesCache(JComponent c) { |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
262 |
overridesCache.remove(c); |
2658 | 263 |
} |
264 |
||
265 |
/* |
|
266 |
Various public helper classes. |
|
267 |
These may be used to register 3rd party values into UIDefaults |
|
268 |
*/ |
|
269 |
||
270 |
/** |
|
271 |
* <p>Derives its font value based on a parent font and a set of offsets and |
|
272 |
* attributes. This class is an ActiveValue, meaning that it will recompute |
|
273 |
* its value each time it is requested from UIDefaults. It is therefore |
|
274 |
* recommended to read this value once and cache it in the UI delegate class |
|
275 |
* until asked to reinitialize.</p> |
|
276 |
* |
|
277 |
* <p>To use this class, create an instance with the key of the font in the |
|
278 |
* UI defaults table from which to derive this font, along with a size |
|
279 |
* offset (if any), and whether it is to be bold, italic, or left in its |
|
280 |
* default form.</p> |
|
281 |
*/ |
|
3093
3ae581eddd21
6849277: Nimbus L&F: lots of painter classes were added to JDK7 as public
peterz
parents:
2658
diff
changeset
|
282 |
static final class DerivedFont implements UIDefaults.ActiveValue { |
2658 | 283 |
private float sizeOffset; |
284 |
private Boolean bold; |
|
285 |
private Boolean italic; |
|
286 |
private String parentKey; |
|
287 |
||
288 |
/** |
|
289 |
* Create a new DerivedFont. |
|
290 |
* |
|
291 |
* @param key The UIDefault key associated with this derived font's |
|
292 |
* parent or source. If this key leads to a null value, or a |
|
293 |
* value that is not a font, then null will be returned as |
|
294 |
* the derived font. The key must not be null. |
|
295 |
* @param sizeOffset The size offset, as a percentage, to use. For |
|
296 |
* example, if the source font was a 12pt font and the |
|
297 |
* sizeOffset were specified as .9, then the new font |
|
298 |
* will be 90% of what the source font was, or, 10.8 |
|
299 |
* pts which is rounded to 11pts. This fractional |
|
300 |
* based offset allows for proper font scaling in high |
|
301 |
* DPI or large system font scenarios. |
|
302 |
* @param bold Whether the new font should be bold. If null, then this |
|
303 |
* new font will inherit the bold setting of the source |
|
304 |
* font. |
|
305 |
* @param italic Whether the new font should be italicized. If null, |
|
306 |
* then this new font will inherit the italic setting of |
|
307 |
* the source font. |
|
308 |
*/ |
|
309 |
public DerivedFont(String key, float sizeOffset, Boolean bold, |
|
310 |
Boolean italic) { |
|
311 |
//validate the constructor arguments |
|
312 |
if (key == null) { |
|
313 |
throw new IllegalArgumentException("You must specify a key"); |
|
314 |
} |
|
315 |
||
316 |
//set the values |
|
317 |
this.parentKey = key; |
|
318 |
this.sizeOffset = sizeOffset; |
|
319 |
this.bold = bold; |
|
320 |
this.italic = italic; |
|
321 |
} |
|
322 |
||
323 |
/** |
|
324 |
* @inheritDoc |
|
325 |
*/ |
|
326 |
@Override |
|
327 |
public Object createValue(UIDefaults defaults) { |
|
328 |
Font f = defaults.getFont(parentKey); |
|
329 |
if (f != null) { |
|
330 |
// always round size for now so we have exact int font size |
|
331 |
// (or we may have lame looking fonts) |
|
332 |
float size = Math.round(f.getSize2D() * sizeOffset); |
|
333 |
int style = f.getStyle(); |
|
334 |
if (bold != null) { |
|
335 |
if (bold.booleanValue()) { |
|
336 |
style = style | Font.BOLD; |
|
337 |
} else { |
|
338 |
style = style & ~Font.BOLD; |
|
339 |
} |
|
340 |
} |
|
341 |
if (italic != null) { |
|
342 |
if (italic.booleanValue()) { |
|
343 |
style = style | Font.ITALIC; |
|
344 |
} else { |
|
345 |
style = style & ~Font.ITALIC; |
|
346 |
} |
|
347 |
} |
|
348 |
return f.deriveFont(style, size); |
|
349 |
} else { |
|
350 |
return null; |
|
351 |
} |
|
352 |
} |
|
353 |
} |
|
354 |
||
355 |
||
356 |
/** |
|
357 |
* This class is private because it relies on the constructor of the |
|
358 |
* auto-generated AbstractRegionPainter subclasses. Hence, it is not |
|
359 |
* generally useful, and is private. |
|
360 |
* <p/> |
|
361 |
* LazyPainter is a LazyValue class. It will create the |
|
362 |
* AbstractRegionPainter lazily, when asked. It uses reflection to load the |
|
363 |
* proper class and invoke its constructor. |
|
364 |
*/ |
|
365 |
private static final class LazyPainter implements UIDefaults.LazyValue { |
|
366 |
private int which; |
|
367 |
private AbstractRegionPainter.PaintContext ctx; |
|
368 |
private String className; |
|
369 |
||
370 |
LazyPainter(String className, int which, Insets insets, |
|
371 |
Dimension canvasSize, boolean inverted) { |
|
372 |
if (className == null) { |
|
373 |
throw new IllegalArgumentException( |
|
374 |
"The className must be specified"); |
|
375 |
} |
|
376 |
||
377 |
this.className = className; |
|
378 |
this.which = which; |
|
379 |
this.ctx = new AbstractRegionPainter.PaintContext( |
|
380 |
insets, canvasSize, inverted); |
|
381 |
} |
|
382 |
||
383 |
LazyPainter(String className, int which, Insets insets, |
|
384 |
Dimension canvasSize, boolean inverted, |
|
385 |
AbstractRegionPainter.PaintContext.CacheMode cacheMode, |
|
386 |
double maxH, double maxV) { |
|
387 |
if (className == null) { |
|
388 |
throw new IllegalArgumentException( |
|
389 |
"The className must be specified"); |
|
390 |
} |
|
391 |
||
392 |
this.className = className; |
|
393 |
this.which = which; |
|
394 |
this.ctx = new AbstractRegionPainter.PaintContext( |
|
395 |
insets, canvasSize, inverted, cacheMode, maxH, maxV); |
|
396 |
} |
|
397 |
||
398 |
@Override |
|
399 |
public Object createValue(UIDefaults table) { |
|
400 |
try { |
|
401 |
Class c; |
|
402 |
Object cl; |
|
403 |
// See if we should use a separate ClassLoader |
|
404 |
if (table == null || !((cl = table.get("ClassLoader")) |
|
405 |
instanceof ClassLoader)) { |
|
406 |
cl = Thread.currentThread(). |
|
407 |
getContextClassLoader(); |
|
408 |
if (cl == null) { |
|
409 |
// Fallback to the system class loader. |
|
410 |
cl = ClassLoader.getSystemClassLoader(); |
|
411 |
} |
|
412 |
} |
|
413 |
||
414 |
c = Class.forName(className, true, (ClassLoader)cl); |
|
415 |
Constructor constructor = c.getConstructor( |
|
416 |
AbstractRegionPainter.PaintContext.class, int.class); |
|
417 |
if (constructor == null) { |
|
418 |
throw new NullPointerException( |
|
419 |
"Failed to find the constructor for the class: " + |
|
420 |
className); |
|
421 |
} |
|
422 |
return constructor.newInstance(ctx, which); |
|
423 |
} catch (Exception e) { |
|
424 |
e.printStackTrace(); |
|
425 |
return null; |
|
426 |
} |
|
427 |
} |
|
428 |
} |
|
429 |
||
430 |
/** |
|
431 |
* A class which creates the NimbusStyle associated with it lazily, but also |
|
432 |
* manages a lot more information about the style. It is less of a LazyValue |
|
433 |
* type of class, and more of an Entry or Item type of class, as it |
|
434 |
* represents an entry in the list of LazyStyles in the map m. |
|
435 |
* |
|
436 |
* The primary responsibilities of this class include: |
|
437 |
* <ul> |
|
438 |
* <li>Determining whether a given component/region pair matches this |
|
439 |
* style</li> |
|
440 |
* <li>Splitting the prefix specified in the constructor into its |
|
441 |
* constituent parts to facilitate quicker matching</li> |
|
442 |
* <li>Creating and vending a NimbusStyle lazily.</li> |
|
443 |
* </ul> |
|
444 |
*/ |
|
445 |
private final class LazyStyle { |
|
446 |
/** |
|
447 |
* The prefix this LazyStyle was registered with. Something like |
|
448 |
* Button or ComboBox:"ComboBox.arrowButton" |
|
449 |
*/ |
|
450 |
private String prefix; |
|
451 |
/** |
|
452 |
* Whether or not this LazyStyle represents an unnamed component |
|
453 |
*/ |
|
454 |
private boolean simple = true; |
|
455 |
/** |
|
456 |
* The various parts, or sections, of the prefix. For example, |
|
457 |
* the prefix: |
|
458 |
* ComboBox:"ComboBox.arrowButton" |
|
459 |
* |
|
460 |
* will be broken into two parts, |
|
461 |
* ComboBox and "ComboBox.arrowButton" |
|
462 |
*/ |
|
463 |
private Part[] parts; |
|
464 |
/** |
|
465 |
* Cached shared style. |
|
466 |
*/ |
|
467 |
private NimbusStyle style; |
|
468 |
||
469 |
/** |
|
470 |
* Create a new LazyStyle. |
|
471 |
* |
|
472 |
* @param prefix The prefix associated with this style. Cannot be null. |
|
473 |
*/ |
|
474 |
private LazyStyle(String prefix) { |
|
475 |
if (prefix == null) { |
|
476 |
throw new IllegalArgumentException( |
|
477 |
"The prefix must not be null"); |
|
478 |
} |
|
479 |
||
480 |
this.prefix = prefix; |
|
481 |
||
482 |
//there is one odd case that needs to be supported here: cell |
|
483 |
//renderers. A cell renderer is defined as a named internal |
|
484 |
//component, so for example: |
|
485 |
// List."List.cellRenderer" |
|
486 |
//The problem is that the component named List.cellRenderer is not a |
|
487 |
//child of a JList. Rather, it is treated more as a direct component |
|
488 |
//Thus, if the prefix ends with "cellRenderer", then remove all the |
|
489 |
//previous dotted parts of the prefix name so that it becomes, for |
|
490 |
//example: "List.cellRenderer" |
|
491 |
//Likewise, we have a hacked work around for cellRenderer, renderer, |
|
492 |
//and listRenderer. |
|
493 |
String temp = prefix; |
|
494 |
if (temp.endsWith("cellRenderer\"") |
|
495 |
|| temp.endsWith("renderer\"") |
|
496 |
|| temp.endsWith("listRenderer\"")) { |
|
497 |
temp = temp.substring(temp.lastIndexOf(":\"") + 1); |
|
498 |
} |
|
499 |
||
500 |
//otherwise, normal code path |
|
501 |
List<String> sparts = split(temp); |
|
502 |
parts = new Part[sparts.size()]; |
|
503 |
for (int i = 0; i < parts.length; i++) { |
|
504 |
parts[i] = new Part(sparts.get(i)); |
|
505 |
if (parts[i].named) { |
|
506 |
simple = false; |
|
507 |
} |
|
508 |
} |
|
509 |
} |
|
510 |
||
511 |
/** |
|
512 |
* Gets the style. Creates it if necessary. |
|
513 |
* @return the style |
|
514 |
*/ |
|
4841
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
515 |
SynthStyle getStyle(JComponent c, Region r) { |
2658 | 516 |
// if the component has overrides, it gets its own unique style |
517 |
// instead of the shared style. |
|
518 |
if (c.getClientProperty("Nimbus.Overrides") != null) { |
|
4841
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
519 |
Map<Region, SynthStyle> map = overridesCache.get(c); |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
520 |
SynthStyle s = null; |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
521 |
if (map == null) { |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
522 |
map = new HashMap<Region, SynthStyle>(); |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
523 |
overridesCache.put(c, map); |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
524 |
} else { |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
525 |
s = map.get(r); |
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
526 |
} |
2658 | 527 |
if (s == null) { |
528 |
s = new NimbusStyle(prefix, c); |
|
4841
ae658e3b0f27
6860433: [Nimbus] Code to set a single slider's thumb background doesn't work as specified
peterz
parents:
3932
diff
changeset
|
529 |
map.put(r, s); |
2658 | 530 |
} |
531 |
return s; |
|
532 |
} |
|
533 |
||
534 |
// lazily create the style if necessary |
|
535 |
if (style == null) |
|
536 |
style = new NimbusStyle(prefix, null); |
|
537 |
||
538 |
// return the style |
|
539 |
return style; |
|
540 |
} |
|
541 |
||
542 |
/** |
|
543 |
* This LazyStyle is a match for the given component if, and only if, |
|
544 |
* for each part of the prefix the component hierarchy matches exactly. |
|
545 |
* That is, if given "a":something:"b", then: |
|
546 |
* c.getName() must equals "b" |
|
547 |
* c.getParent() can be anything |
|
548 |
* c.getParent().getParent().getName() must equal "a". |
|
549 |
*/ |
|
550 |
boolean matches(JComponent c) { |
|
551 |
return matches(c, parts.length - 1); |
|
552 |
} |
|
553 |
||
554 |
private boolean matches(Component c, int partIndex) { |
|
555 |
if (partIndex < 0) return true; |
|
556 |
if (c == null) return false; |
|
557 |
//only get here if partIndex > 0 and c == null |
|
558 |
||
559 |
String name = c.getName(); |
|
560 |
if (parts[partIndex].named && parts[partIndex].s.equals(name)) { |
|
561 |
//so far so good, recurse |
|
562 |
return matches(c.getParent(), partIndex - 1); |
|
563 |
} else if (!parts[partIndex].named) { |
|
564 |
//if c is not named, and parts[partIndex] has an expected class |
|
565 |
//type registered, then check to make sure c is of the |
|
566 |
//right type; |
|
567 |
Class clazz = parts[partIndex].c; |
|
568 |
if (clazz != null && clazz.isAssignableFrom(c.getClass())) { |
|
569 |
//so far so good, recurse |
|
570 |
return matches(c.getParent(), partIndex - 1); |
|
571 |
} else if (clazz == null && |
|
572 |
registeredRegions.containsKey(parts[partIndex].s)) { |
|
573 |
Region r = registeredRegions.get(parts[partIndex].s); |
|
574 |
Component parent = r.isSubregion() ? c : c.getParent(); |
|
575 |
//special case the JInternalFrameTitlePane, because it |
|
576 |
//doesn't fit the mold. very, very funky. |
|
577 |
if (r == Region.INTERNAL_FRAME_TITLE_PANE && parent != null |
|
578 |
&& parent instanceof JInternalFrame.JDesktopIcon) { |
|
579 |
JInternalFrame.JDesktopIcon icon = |
|
580 |
(JInternalFrame.JDesktopIcon) parent; |
|
581 |
parent = icon.getInternalFrame(); |
|
582 |
} |
|
583 |
//it was the name of a region. So far, so good. Recurse. |
|
584 |
return matches(parent, partIndex - 1); |
|
585 |
} |
|
586 |
} |
|
587 |
||
588 |
return false; |
|
589 |
} |
|
590 |
||
591 |
/** |
|
592 |
* Given some dot separated prefix, split on the colons that are |
|
593 |
* not within quotes, and not within brackets. |
|
594 |
* |
|
595 |
* @param prefix |
|
596 |
* @return |
|
597 |
*/ |
|
598 |
private List<String> split(String prefix) { |
|
599 |
List<String> parts = new ArrayList<String>(); |
|
600 |
int bracketCount = 0; |
|
601 |
boolean inquotes = false; |
|
602 |
int lastIndex = 0; |
|
603 |
for (int i = 0; i < prefix.length(); i++) { |
|
604 |
char c = prefix.charAt(i); |
|
605 |
||
606 |
if (c == '[') { |
|
607 |
bracketCount++; |
|
608 |
continue; |
|
609 |
} else if (c == '"') { |
|
610 |
inquotes = !inquotes; |
|
611 |
continue; |
|
612 |
} else if (c == ']') { |
|
613 |
bracketCount--; |
|
614 |
if (bracketCount < 0) { |
|
615 |
throw new RuntimeException( |
|
616 |
"Malformed prefix: " + prefix); |
|
617 |
} |
|
618 |
continue; |
|
619 |
} |
|
620 |
||
621 |
if (c == ':' && !inquotes && bracketCount == 0) { |
|
622 |
//found a character to split on. |
|
623 |
parts.add(prefix.substring(lastIndex, i)); |
|
624 |
lastIndex = i + 1; |
|
625 |
} |
|
626 |
} |
|
627 |
if (lastIndex < prefix.length() - 1 && !inquotes |
|
628 |
&& bracketCount == 0) { |
|
629 |
parts.add(prefix.substring(lastIndex)); |
|
630 |
} |
|
631 |
return parts; |
|
632 |
||
633 |
} |
|
634 |
||
635 |
private final class Part { |
|
636 |
private String s; |
|
637 |
//true if this part represents a component name |
|
638 |
private boolean named; |
|
639 |
private Class c; |
|
640 |
||
641 |
Part(String s) { |
|
642 |
named = s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"'; |
|
643 |
if (named) { |
|
644 |
this.s = s.substring(1, s.length() - 1); |
|
645 |
} else { |
|
646 |
this.s = s; |
|
647 |
//TODO use a map of known regions for Synth and Swing, and |
|
648 |
//then use [classname] instead of org_class_name style |
|
649 |
try { |
|
650 |
c = Class.forName("javax.swing.J" + s); |
|
651 |
} catch (Exception e) { |
|
652 |
} |
|
653 |
try { |
|
654 |
c = Class.forName(s.replace("_", ".")); |
|
655 |
} catch (Exception e) { |
|
656 |
} |
|
657 |
} |
|
658 |
} |
|
659 |
} |
|
660 |
} |
|
661 |
||
3741 | 662 |
private void addColor(UIDefaults d, String uin, int r, int g, int b, int a) { |
663 |
Color color = new ColorUIResource(new Color(r, g, b, a)); |
|
664 |
colorTree.addColor(uin, color); |
|
665 |
d.put(uin, color); |
|
666 |
} |
|
667 |
||
668 |
private void addColor(UIDefaults d, String uin, String parentUin, |
|
669 |
float hOffset, float sOffset, float bOffset, int aOffset) { |
|
670 |
addColor(d, uin, parentUin, hOffset, sOffset, bOffset, aOffset, true); |
|
671 |
} |
|
672 |
||
673 |
private void addColor(UIDefaults d, String uin, String parentUin, |
|
674 |
float hOffset, float sOffset, float bOffset, |
|
675 |
int aOffset, boolean uiResource) { |
|
676 |
Color color = getDerivedColor(uin, parentUin, |
|
677 |
hOffset, sOffset, bOffset, aOffset, uiResource); |
|
678 |
d.put(uin, color); |
|
2658 | 679 |
} |
680 |
||
681 |
/** |
|
682 |
* Get a derived color, derived colors are shared instances and will be |
|
683 |
* updated when its parent UIDefault color changes. |
|
684 |
* |
|
685 |
* @param uiDefaultParentName The parent UIDefault key |
|
686 |
* @param hOffset The hue offset |
|
687 |
* @param sOffset The saturation offset |
|
688 |
* @param bOffset The brightness offset |
|
689 |
* @param aOffset The alpha offset |
|
690 |
* @param uiResource True if the derived color should be a UIResource, |
|
691 |
* false if it should not be a UIResource |
|
692 |
* @return The stored derived color |
|
693 |
*/ |
|
3741 | 694 |
public DerivedColor getDerivedColor(String parentUin, |
2658 | 695 |
float hOffset, float sOffset, |
696 |
float bOffset, int aOffset, |
|
697 |
boolean uiResource){ |
|
3741 | 698 |
return getDerivedColor(null, parentUin, |
699 |
hOffset, sOffset, bOffset, aOffset, uiResource); |
|
700 |
} |
|
701 |
||
702 |
private DerivedColor getDerivedColor(String uin, String parentUin, |
|
703 |
float hOffset, float sOffset, |
|
704 |
float bOffset, int aOffset, |
|
705 |
boolean uiResource) { |
|
706 |
DerivedColor color; |
|
707 |
if (uiResource) { |
|
708 |
color = new DerivedColor.UIResource(parentUin, |
|
709 |
hOffset, sOffset, bOffset, aOffset); |
|
710 |
} else { |
|
711 |
color = new DerivedColor(parentUin, hOffset, sOffset, |
|
712 |
bOffset, aOffset); |
|
2658 | 713 |
} |
3741 | 714 |
|
715 |
if (derivedColors.containsKey(color)) { |
|
716 |
return derivedColors.get(color); |
|
717 |
} else { |
|
718 |
derivedColors.put(color, color); |
|
719 |
color.rederiveColor(); /// move to ARP.decodeColor() ? |
|
720 |
colorTree.addColor(uin, color); |
|
721 |
return color; |
|
722 |
} |
|
2658 | 723 |
} |
724 |
||
3741 | 725 |
private Map<DerivedColor, DerivedColor> derivedColors = |
726 |
new HashMap<DerivedColor, DerivedColor>(); |
|
2658 | 727 |
|
3741 | 728 |
private class ColorTree implements PropertyChangeListener { |
729 |
private Node root = new Node(null, null); |
|
730 |
private Map<String, Node> nodes = new HashMap<String, Node>(); |
|
2658 | 731 |
|
3741 | 732 |
public Color getColor(String uin) { |
733 |
return nodes.get(uin).color; |
|
2658 | 734 |
} |
735 |
||
3741 | 736 |
public void addColor(String uin, Color color) { |
737 |
Node parent = getParentNode(color); |
|
738 |
Node node = new Node(color, parent); |
|
739 |
parent.children.add(node); |
|
740 |
if (uin != null) { |
|
741 |
nodes.put(uin, node); |
|
742 |
} |
|
743 |
} |
|
744 |
||
745 |
private Node getParentNode(Color color) { |
|
746 |
Node parent = root; |
|
747 |
if (color instanceof DerivedColor) { |
|
748 |
String parentUin = ((DerivedColor)color).getUiDefaultParentName(); |
|
749 |
Node p = nodes.get(parentUin); |
|
750 |
if (p != null) { |
|
751 |
parent = p; |
|
752 |
} |
|
753 |
} |
|
754 |
return parent; |
|
755 |
} |
|
756 |
||
757 |
public void update() { |
|
758 |
root.update(); |
|
2658 | 759 |
} |
760 |
||
761 |
@Override |
|
3741 | 762 |
public void propertyChange(PropertyChangeEvent ev) { |
763 |
String name = ev.getPropertyName(); |
|
764 |
Node node = nodes.get(name); |
|
765 |
if (node != null) { |
|
766 |
// this is a registered color |
|
767 |
node.parent.children.remove(node); |
|
768 |
Color color = (Color) ev.getNewValue(); |
|
769 |
Node parent = getParentNode(color); |
|
770 |
node.set(color, parent); |
|
771 |
parent.children.add(node); |
|
772 |
node.update(); |
|
773 |
} |
|
2658 | 774 |
} |
775 |
||
3741 | 776 |
class Node { |
777 |
Color color; |
|
778 |
Node parent; |
|
779 |
List<Node> children = new LinkedList<Node>(); |
|
780 |
||
781 |
Node(Color color, Node parent) { |
|
782 |
set(color, parent); |
|
783 |
} |
|
784 |
||
785 |
public void set(Color color, Node parent) { |
|
786 |
this.color = color; |
|
787 |
this.parent = parent; |
|
788 |
} |
|
789 |
||
790 |
public void update() { |
|
791 |
if (color instanceof DerivedColor) { |
|
792 |
((DerivedColor)color).rederiveColor(); |
|
793 |
} |
|
794 |
for (Node child: children) { |
|
795 |
child.update(); |
|
796 |
} |
|
797 |
} |
|
2658 | 798 |
} |
799 |
} |
|
800 |
||
801 |
/** |
|
802 |
* Listener to update derived colors on UIManager Defaults changes |
|
803 |
*/ |
|
804 |
private class DefaultsListener implements PropertyChangeListener { |
|
805 |
@Override |
|
806 |
public void propertyChange(PropertyChangeEvent evt) { |
|
3741 | 807 |
if ("lookAndFeel".equals(evt.getPropertyName())) { |
2658 | 808 |
// LAF has been installed, this is the first point at which we |
809 |
// can access our defaults table via UIManager so before now |
|
810 |
// all derived colors will be incorrect. |
|
811 |
// First we need to update |
|
3741 | 812 |
colorTree.update(); |
2658 | 813 |
} |
814 |
} |
|
815 |
} |
|
816 |
||
817 |
private static final class PainterBorder implements Border, UIResource { |
|
818 |
private Insets insets; |
|
819 |
private Painter painter; |
|
820 |
private String painterKey; |
|
821 |
||
822 |
PainterBorder(String painterKey, Insets insets) { |
|
823 |
this.insets = insets; |
|
824 |
this.painterKey = painterKey; |
|
825 |
} |
|
826 |
||
827 |
@Override |
|
828 |
public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { |
|
829 |
if (painter == null) { |
|
830 |
painter = (Painter)UIManager.get(painterKey); |
|
831 |
if (painter == null) return; |
|
832 |
} |
|
833 |
||
834 |
g.translate(x, y); |
|
835 |
if (g instanceof Graphics2D) |
|
836 |
painter.paint((Graphics2D)g, c, w, h); |
|
837 |
else { |
|
838 |
BufferedImage img = new BufferedImage(w, h, TYPE_INT_ARGB); |
|
839 |
Graphics2D gfx = img.createGraphics(); |
|
840 |
painter.paint(gfx, c, w, h); |
|
841 |
gfx.dispose(); |
|
842 |
g.drawImage(img, x, y, null); |
|
843 |
img = null; |
|
844 |
} |
|
845 |
g.translate(-x, -y); |
|
846 |
} |
|
847 |
||
848 |
@Override |
|
849 |
public Insets getBorderInsets(Component c) { |
|
850 |
return (Insets)insets.clone(); |
|
851 |
} |
|
852 |
||
853 |
@Override |
|
854 |
public boolean isBorderOpaque() { |
|
855 |
return false; |
|
856 |
} |
|
857 |
} |
|
858 |
} |