-
Notifications
You must be signed in to change notification settings - Fork 2
/
BasicHeaderUI.java
462 lines (424 loc) · 19.4 KB
/
BasicHeaderUI.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
/*
* $Id: BasicHeaderUI.java 3483 2009-09-02 12:33:21Z kleopatra $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.hdesktop.swingx.plaf.basic;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.HierarchyBoundsAdapter;
import java.awt.event.HierarchyBoundsListener;
import java.awt.event.HierarchyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import org.hdesktop.swingx.JXHeader;
import org.hdesktop.swingx.JXLabel;
import org.hdesktop.swingx.JXHeader.IconPosition;
import org.hdesktop.swingx.painter.MattePainter;
import org.hdesktop.swingx.painter.Painter;
import org.hdesktop.swingx.plaf.HeaderUI;
import org.hdesktop.swingx.plaf.PainterUIResource;
import org.hdesktop.swingx.plaf.UIManagerExt;
/**
* Base implementation of <code>Header</code> UI. <p>
*
* PENDING JW: This implementation is unusual in that it does not keep a reference
* to the component it controls. Typically, such is only the case if the ui is
* shared between instances. Historical? A consequence is that the un/install methods
* need to carry the header as parameter. Which looks funny when at the same time
* the children of the header are instance fields in this. Should think about cleanup:
* either get rid off the instance fields here, or reference the header and remove
* the param (would break subclasses).<p>
*
* PENDING JW: keys for uidefaults are inconsistent - most have prefix "JXHeader." while
* defaultIcon has prefix "Header." <p>
*
* @author rbair
* @author rah003
* @author Jeanette Winzenburg
*/
public class BasicHeaderUI extends HeaderUI {
@SuppressWarnings("unused")
private static final Logger LOG = Logger.getLogger(BasicHeaderUI.class
.getName());
// Implementation detail. Neeeded to expose getMultiLineSupport() method to allow restoring view
// lost after LAF switch
protected class DescriptionPane extends JXLabel {
@Override
public void paint(Graphics g) {
// switch off jxlabel default antialiasing
// JW: that cost me dearly to track down - it's the default foreground painter
// which is an AbstractPainter which has _global_ antialiased on by default
// and here the _text_ antialiased is turned off
// changed JXLabel default foregroundPainter to have antialiasing false by
// default, so remove interference here
// part of fix for #920 - the other part is in JXLabel, fix 1164
// ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
// RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
super.paint(g);
}
@Override
public MultiLineSupport getMultiLineSupport() {
return super.getMultiLineSupport();
}
}
protected JLabel titleLabel;
protected DescriptionPane descriptionPane;
protected JLabel imagePanel;
private PropertyChangeListener propListener;
private HierarchyBoundsListener boundsListener;
private Color gradientLightColor;
private Color gradientDarkColor;
/** Creates a new instance of BasicHeaderUI */
public BasicHeaderUI() {
}
/**
* Returns an instance of the UI delegate for the specified component.
* Each subclass must provide its own static <code>createUI</code>
* method that returns an instance of that UI delegate subclass.
* If the UI delegate subclass is stateless, it may return an instance
* that is shared by multiple components. If the UI delegate is
* stateful, then it should return a new instance per component.
* The default implementation of this method throws an error, as it
* should never be invoked.
*/
public static ComponentUI createUI(JComponent c) {
return new BasicHeaderUI();
}
/**
* Configures the specified component appropriate for the look and feel.
* This method is invoked when the <code>ComponentUI</code> instance is being installed
* as the UI delegate on the specified component. This method should
* completely configure the component for the look and feel,
* including the following:
* <ol>
* <li>Install any default property values for color, fonts, borders,
* icons, opacity, etc. on the component. Whenever possible,
* property values initialized by the client program should <i>not</i>
* be overridden.
* <li>Install a <code>LayoutManager</code> on the component if necessary.
* <li>Create/add any required sub-components to the component.
* <li>Create/install event listeners on the component.
* <li>Create/install a <code>PropertyChangeListener</code> on the component in order
* to detect and respond to component property changes appropriately.
* <li>Install keyboard UI (mnemonics, traversal, etc.) on the component.
* <li>Initialize any appropriate instance data.
* </ol>
* @param c the component where this UI delegate is being installed
*
* @see #uninstallUI
* @see javax.swing.JComponent#setUI
* @see javax.swing.JComponent#updateUI
*/
@Override
public void installUI(JComponent c) {
super.installUI(c);
assert c instanceof JXHeader;
JXHeader header = (JXHeader)c;
installDefaults(header);
installComponents(header);
installListeners(header);
}
/**
* Reverses configuration which was done on the specified component during
* <code>installUI</code>. This method is invoked when this
* <code>UIComponent</code> instance is being removed as the UI delegate
* for the specified component. This method should undo the
* configuration performed in <code>installUI</code>, being careful to
* leave the <code>JComponent</code> instance in a clean state (no
* extraneous listeners, look-and-feel-specific property objects, etc.).
* This should include the following:
* <ol>
* <li>Remove any UI-set borders from the component.
* <li>Remove any UI-set layout managers on the component.
* <li>Remove any UI-added sub-components from the component.
* <li>Remove any UI-added event/property listeners from the component.
* <li>Remove any UI-installed keyboard UI from the component.
* <li>Nullify any allocated instance data objects to allow for GC.
* </ol>
* @param c the component from which this UI delegate is being removed;
* this argument is often ignored,
* but might be used if the UI object is stateless
* and shared by multiple components
*
* @see #installUI
* @see javax.swing.JComponent#updateUI
*/
@Override
public void uninstallUI(JComponent c) {
assert c instanceof JXHeader;
JXHeader header = (JXHeader)c;
uninstallListeners(header);
uninstallComponents(header);
uninstallDefaults(header);
}
/**
* Installs default header properties.
* <p>
*
* NOTE: this method is called before the children are created, so must not
* try to access any of those!.
*
* @param header the header to install.
*/
protected void installDefaults(JXHeader header) {
gradientLightColor = UIManagerExt.getColor("JXHeader.startBackground");
if (gradientLightColor == null) {
// fallback to white
gradientLightColor = Color.WHITE;
}
gradientDarkColor = UIManagerExt.getColor("JXHeader.background");
// for backwards compatibility (mostly for substance and synthetica,
// I suspect) I'll fall back on the "control" color if
// JXHeader.background
// isn't specified.
if (gradientDarkColor == null) {
gradientDarkColor = UIManagerExt.getColor("control");
}
if (isUIInstallable(header.getBackgroundPainter())) {
header.setBackgroundPainter(createBackgroundPainter());
}
// title properties
if (isUIInstallable(header.getTitleFont())) {
Font titleFont = UIManager.getFont("JXHeader.titleFont");
// fallback to label font
header.setTitleFont(titleFont != null ? titleFont : UIManager
.getFont("Label.font"));
}
if (isUIInstallable(header.getTitleForeground())) {
Color titleForeground = UIManagerExt
.getColor("JXHeader.titleForeground");
// fallback to label foreground
header.setTitleForeground(titleForeground != null ? titleForeground
: UIManagerExt.getColor("Label.foreground"));
}
// description properties
if (isUIInstallable(header.getDescriptionFont())) {
Font descFont = UIManager.getFont("JXHeader.descriptionFont");
// fallback to label font
header.setDescriptionFont(descFont != null ? descFont : UIManager
.getFont("Label.font"));
}
if (isUIInstallable(header.getDescriptionForeground())) {
Color descForeground = UIManagerExt
.getColor("JXHeader.descriptionForeground");
// fallback to label foreground
header.setDescriptionForeground(descForeground != null ? descForeground
: UIManagerExt.getColor("Label.foreground"));
}
// icon label properties
if (isUIInstallable(header.getIcon())) {
header.setIcon(UIManager.getIcon("Header.defaultIcon"));
}
}
/**
* Uninstalls the given header's default properties. This implementation
* does nothing.
*
* @param h the header to ininstall the properties from.
*/
protected void uninstallDefaults(JXHeader h) {
}
/**
* Creates, configures, adds contained components.
* PRE: header's default properties must be set before calling this.
*
* @param header the header to install the components into.
*/
protected void installComponents(JXHeader header) {
titleLabel = new JLabel();
descriptionPane = new DescriptionPane();
imagePanel = new JLabel();
installComponentDefaults(header);
header.setLayout(new GridBagLayout());
resetLayout(header);
}
/**
* Unconfigures, removes and nulls contained components.
*
* @param header the header to install the components into.
*/
protected void uninstallComponents(JXHeader header) {
uninstallComponentDefaults(header);
header.remove(titleLabel);
header.remove(descriptionPane);
header.remove(imagePanel);
titleLabel = null;
descriptionPane = null;
imagePanel = null;
}
/**
* Configures the component default properties from the given header.
*
* @param header the header to install the components into.
*/
protected void installComponentDefaults(JXHeader header) {
// JW: force a not UIResource for properties which have ui default values
// like color, font, ??
titleLabel.setFont(getAsNotUIResource(header.getTitleFont()));
titleLabel.setForeground(getAsNotUIResource(header.getTitleForeground()));
titleLabel.setText(header.getTitle());
descriptionPane.setFont(getAsNotUIResource(header.getDescriptionFont()));
descriptionPane.setForeground(getAsNotUIResource(header.getDescriptionForeground()));
descriptionPane.setOpaque(false);
descriptionPane.setText(header.getDescription());
descriptionPane.setLineWrap(true);
imagePanel.setIcon(header.getIcon());
}
/**
* Returns a Font based on the param which is not of type UIResource.
*
* @param font the base font
* @return a font not of type UIResource, may be null.
*/
private Font getAsNotUIResource(Font font) {
if (!(font instanceof UIResource)) return font;
// PENDING JW: correct way to create another font instance?
return font.deriveFont(font.getAttributes());
}
/**
* Returns a Color based on the param which is not of type UIResource.
*
* @param color the base color
* @return a color not of type UIResource, may be null.
*/
private Color getAsNotUIResource(Color color) {
if (!(color instanceof UIResource)) return color;
// PENDING JW: correct way to create another color instance?
float[] rgb = color.getRGBComponents(null);
return new Color(rgb[0], rgb[1], rgb[2], rgb[3]);
}
/**
* Checks and returns whether the given property should be replaced
* by the UI's default value.<p>
*
* PENDING JW: move as utility method ... where?
*
* @param property the property to check.
* @return true if the given property should be replaced by the UI#s
* default value, false otherwise.
*/
private boolean isUIInstallable(Object property) {
return (property == null) || (property instanceof UIResource);
}
/**
* Uninstalls component defaults. This implementation does nothing.
*
* @param header the header to uninstall from.
*/
protected void uninstallComponentDefaults(JXHeader header) {
}
protected void installListeners(final JXHeader header) {
propListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
onPropertyChange(header, evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
}
};
boundsListener = new HierarchyBoundsAdapter() {
@Override
public void ancestorResized(HierarchyEvent e) {
if (header == e.getComponent()) {
View v = (View) descriptionPane.getClientProperty(BasicHTML.propertyKey);
// view might get lost on LAF change ...
if (v == null) {
descriptionPane.putClientProperty(BasicHTML.propertyKey,
descriptionPane.getMultiLineSupport().createView(descriptionPane));
v = (View) descriptionPane.getClientProperty(BasicHTML.propertyKey);
}
if (v != null) {
Container tla = header.getTopLevelAncestor();
if (tla == null) {
tla = header.getParent();
while (tla.getParent() != null) {
tla = tla.getParent();
}
}
int h = Math.max(descriptionPane.getHeight(), tla.getHeight());
int w = Math.min(tla.getWidth(), header.getParent().getWidth());
// 35 = description pane insets, TODO: obtain dynamically
w -= 35 + header.getInsets().left + header.getInsets().right + descriptionPane.getInsets().left + descriptionPane.getInsets().right + imagePanel.getInsets().left + imagePanel.getInsets().right + imagePanel.getWidth() + descriptionPane.getBounds().x;
v.setSize(w, h);
descriptionPane.setSize(w, (int) Math.ceil(v.getPreferredSpan(View.Y_AXIS)));
}
}
}};
header.addPropertyChangeListener(propListener);
header.addHierarchyBoundsListener(boundsListener);
}
protected void uninstallListeners(JXHeader h) {
h.removePropertyChangeListener(propListener);
h.removeHierarchyBoundsListener(boundsListener);
}
protected void onPropertyChange(JXHeader h, String propertyName, Object oldValue, final Object newValue) {
if ("title".equals(propertyName)) {
titleLabel.setText(h.getTitle());
} else if ("description".equals(propertyName)) {
descriptionPane.setText(h.getDescription());
} else if ("icon".equals(propertyName)) {
imagePanel.setIcon(h.getIcon());
} else if ("enabled".equals(propertyName)) {
boolean enabled = h.isEnabled();
titleLabel.setEnabled(enabled);
descriptionPane.setEnabled(enabled);
imagePanel.setEnabled(enabled);
} else if ("titleFont".equals(propertyName)) {
titleLabel.setFont((Font)newValue);
} else if ("descriptionFont".equals(propertyName)) {
descriptionPane.setFont((Font)newValue);
} else if ("titleForeground".equals(propertyName)) {
titleLabel.setForeground((Color)newValue);
} else if ("descriptionForeground".equals(propertyName)) {
descriptionPane.setForeground((Color)newValue);
} else if ("iconPosition".equals(propertyName)) {
resetLayout(h);
}
}
private void resetLayout(JXHeader h) {
h.remove(titleLabel);
h.remove(descriptionPane);
h.remove(imagePanel);
if (h.getIconPosition() == null || h.getIconPosition() == IconPosition.RIGHT) {
h.add(titleLabel, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(12, 12, 0, 11), 0, 0));
h.add(descriptionPane, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(0, 24, 12, 11), 0, 0));
h.add(imagePanel, new GridBagConstraints(1, 0, 1, 2, 0.0, 1.0, GridBagConstraints.FIRST_LINE_END, GridBagConstraints.NONE, new Insets(12, 0, 11, 11), 0, 0));
} else {
h.add(titleLabel, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(12, 12, 0, 11), 0, 0));
h.add(descriptionPane, new GridBagConstraints(1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(0, 24, 12, 11), 0, 0));
h.add(imagePanel, new GridBagConstraints(0, 0, 1, 2, 0.0, 1.0, GridBagConstraints.FIRST_LINE_END, GridBagConstraints.NONE, new Insets(12, 11, 0, 11), 0, 0));
}
}
protected Painter createBackgroundPainter() {
MattePainter p = new MattePainter(new GradientPaint(0, 0, gradientLightColor, 1, 0, gradientDarkColor));
p.setPaintStretched(true);
return new PainterUIResource(p);
}
}