2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
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]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
* EditorPropertyDisplayer_java
44
* Created on 18 October 2003, 16:36
46
package org.openide.explorer.propertysheet;
48
import org.openide.nodes.Node.Property;
49
import org.openide.util.Utilities;
51
import java.awt.Color;
52
import java.awt.Component;
53
import java.awt.Dimension;
54
import java.awt.EventQueue;
55
import java.awt.Image;
56
import java.awt.KeyboardFocusManager;
57
import java.awt.event.FocusEvent;
58
import java.awt.event.KeyEvent;
60
import java.beans.FeatureDescriptor;
61
import java.beans.PropertyEditor;
63
import java.lang.ref.WeakReference;
68
/** An implementation of PropertyDisplayer_Inline which replicates the inline
69
* editor mode of PropertyPanel. This class is a base class which is simply
70
* responsible for finding and embedding the correct inline editor - it contains
71
* no code for actually updating the value or anything such. That code is in
72
* EditablePropertyDisplayer, to improve readability and maintainability.
74
* @author Tim Boudreau
76
class EditorPropertyDisplayer extends JComponent implements PropertyDisplayer_Inline {
77
private Property prop = null;
78
private InplaceEditor inplace = null;
79
private JComponent inner = null;
80
private int radioButtonMax = 0;
81
private boolean showCustomEditorButton = true;
82
private boolean tableUI = false;
83
private boolean useLabels = true;
84
private PropertyEnv env = null;
85
private boolean radioBoolean = false;
86
protected WeakReference<PropertyModel> modelRef = null;
87
protected boolean inReplaceInner = false;
88
private InplaceEditorFactory factory1 = null;
89
private InplaceEditorFactory factory2 = null;
90
private ReusablePropertyEnv reusableEnv = new ReusablePropertyEnv(); //XXX supply from PP?
92
/** Creates a new instance of EditorPropertyDisplayer */
93
public EditorPropertyDisplayer(Property p) {
97
EditorPropertyDisplayer(Property p, PropertyModel mdl) {
99
throw new NullPointerException("Property may not be null"); //NOI18N
105
modelRef = new WeakReference<PropertyModel>(mdl);
109
public void addNotify() {
119
protected void processFocusEvent(FocusEvent fe) {
120
super.processFocusEvent(fe);
122
if ((fe.getID() == FocusEvent.FOCUS_GAINED) && (inner != null)) {
123
inner.requestFocus();
127
public void removeNotify() {
128
super.removeNotify();
129
setInplaceEditor(null);
133
public final Component getComponent() {
137
public final Property getProperty() {
141
public final int getRadioButtonMax() {
142
return radioButtonMax;
145
public final boolean isShowCustomEditorButton() {
146
boolean result = showCustomEditorButton;
148
if (getProperty() != null) {
149
Boolean explicit = (Boolean) getProperty().getValue("suppressCustomEditor"); //NOI18N
151
if (explicit != null) {
152
result = !explicit.booleanValue();
159
public final boolean isTableUI() {
163
public final void refresh() {
164
if (isDisplayable()) {
169
public final boolean isUseLabels() {
173
public final void setRadioButtonMax(int max) {
174
if (max != radioButtonMax) {
175
int old = radioButtonMax;
176
boolean needChange = false;
178
if (inplace != null) {
179
InplaceEditor innermost = PropUtils.findInnermostInplaceEditor(inplace);
181
if (innermost instanceof JComboBox || innermost instanceof RadioInplaceEditor) {
182
PropertyEditor ped = innermost.getPropertyEditor();
183
int tagCount = (ped.getTags() == null) ? (-1) : ped.getTags().length;
184
needChange = (old <= tagCount) != (max <= tagCount);
188
radioButtonMax = max;
190
if (needChange && (inner != null)) {
192
firePropertyChange("preferredSize", null, null); //NOI18N
197
public final void setShowCustomEditorButton(boolean val) {
198
//If the property descriptor explicitly says it does not
199
//want a custom editor button, this overrides anything set on
201
if (getProperty() != null) {
202
Property p = getProperty();
203
Boolean explicit = (Boolean) p.getValue("suppressCustomEditor"); //NOI18N
205
if (explicit != null) {
206
val = explicit.booleanValue();
207
System.err.println("Found explicit value: " + val);
211
if (showCustomEditorButton != val) {
212
showCustomEditorButton = val;
217
public final void setTableUI(boolean val) {
218
if (val != tableUI) {
224
public final void setUseLabels(boolean useLabels) {
225
if (useLabels != this.useLabels) {
226
boolean needChange = false;
229
InplaceEditor innermost = PropUtils.findInnermostInplaceEditor(inplace);
231
needChange = (innermost instanceof RadioInplaceEditor || innermost instanceof JCheckBox);
234
this.useLabels = useLabels;
236
if (needChange && (inner != null)) {
242
public final void requestFocus() {
244
inner.requestFocus();
246
super.requestFocus();
250
public final Dimension getPreferredSize() {
254
//Use the renderer infrastructure to do it if we're not initialized
255
result = new RendererPropertyDisplayer(getProperty()).getRenderer(this).getPreferredSize();
257
result = inner.getPreferredSize();
263
public final boolean requestFocusInWindow() {
267
result = inner.requestFocusInWindow();
269
result = super.requestFocusInWindow();
275
private final void installInner(JComponent c) {
276
synchronized (getTreeLock()) {
284
c.setBounds(0, 0, getWidth(), getHeight());
290
protected final void replaceInner() {
291
inReplaceInner = true;
294
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
295
boolean hadFocus = isEnabled() &&
296
((focusOwner == this) || isAncestorOf(focusOwner) ||
297
((getInplaceEditor() != null) && getInplaceEditor().isKnownComponent(focusOwner)));
299
//Figure out if a combo popup was open, so we can re-open it.
300
//If we're processing a mouse event, close it because that's
301
//the normal behavior of a popup. We want arrow keyboard events
302
//to not trigger closing of the popup if they caused a change
303
//to a value that is marked as invalid
304
boolean wasComboPopup = hadFocus && focusOwner instanceof JComboBox &&
305
((JComboBox) focusOwner).isPopupVisible() &&
306
(EventQueue.getCurrentEvent() instanceof KeyEvent &&
307
((((KeyEvent) EventQueue.getCurrentEvent()).getKeyCode() == KeyEvent.VK_UP) ||
308
(((KeyEvent) EventQueue.getCurrentEvent()).getKeyCode() == KeyEvent.VK_DOWN)));
310
// System.err.println("REPLACE INNER - " + prop.getDisplayName() + " focus:" + hadFocus);
312
//We don't want focus to jump to another component and back
313
KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
316
setInplaceEditor(createInplaceEditor());
321
//At this point the focus owner is still null, the event is queued -
323
//We have to let the request focus on the event queue get
324
//processed before this can be done, or the component will
325
//not yet be laid out and the popup will be 1 pixel wide
326
SwingUtilities.invokeLater(
329
InplaceEditor ied = getInplaceEditor();
330
ied = PropUtils.findInnermostInplaceEditor(ied);
332
JComponent c = ied.getComponent();
334
if (c instanceof JComboBox && c.isShowing()) {
335
((JComboBox) c).showPopup();
345
} catch (Exception e) {
348
inReplaceInner = false;
352
protected final JComponent getInner() {
356
protected void setInplaceEditor(InplaceEditor ed) {
358
if (inplace != null) {
367
JComponent comp = inplace.getComponent();
368
prepareComponent(inplace);
374
public void setEnabled(boolean b) {
382
public void setBackground(Color c) {
383
super.setBackground(c);
386
if ((inplace != null) && inplace.supportsTextEntry()) {
387
inner.setBackground(PropUtils.getTextFieldBackground());
389
inner.setBackground(c);
394
public void setForeground(Color c) {
395
super.setForeground(c);
398
if ((inplace != null) && inplace.supportsTextEntry()) {
399
inner.setForeground(PropUtils.getTextFieldForeground());
401
inner.setForeground(c);
406
protected void prepareComponent(InplaceEditor inplace) {
407
InplaceEditor innermost = PropUtils.findInnermostInplaceEditor(inplace);
408
JComponent comp = innermost.getComponent();
410
if (!isTableUI() && inplace.supportsTextEntry()) {
411
comp.setBackground(PropUtils.getTextFieldBackground());
412
comp.setForeground(PropUtils.getTextFieldForeground());
414
comp.setBackground(getBackground());
416
if (!isEnabled() || !prop.canWrite()) {
417
comp.setForeground(UIManager.getColor("textInactiveText"));
419
comp.setForeground(getForeground());
423
if( comp instanceof ComboInplaceEditor )
424
comp.setEnabled( isEnabled() && getPropertyEnv().isEditable() );
426
comp.setEnabled(isEnabled() && PropUtils.checkEnabled(this, inplace.getPropertyEditor(), getPropertyEnv()));
429
@SuppressWarnings("deprecation")
430
public void reshape(int x, int y, int w, int h) {
432
inner.setBounds(0, 0, w, h);
435
super.reshape(x, y, w, h);
438
protected void setPropertyEnv(PropertyEnv env) {
442
public final PropertyEnv getPropertyEnv() {
446
protected final InplaceEditor getInplaceEditor() {
450
protected void configureButtonPanel(ButtonPanel bp) {
453
/** Basically some hacks to acquire the underlying property descriptor in
454
* the case of a wrapper. This is here because some property editors will
455
* cast the result of PropertyEnv.getFeatureDescriptor() as a specific
456
* implementation type, so even if we're wrapping a property model, we
457
* still need to make sure we're returning the class they expect. */
458
static final FeatureDescriptor findFeatureDescriptor(PropertyDisplayer pd) {
459
if (pd instanceof EditorPropertyDisplayer) {
460
//Issue 38004, more gunk to ensure we get the right feature
462
EditorPropertyDisplayer epd = (EditorPropertyDisplayer) pd;
464
if (epd.modelRef != null) {
465
PropertyModel pm = epd.modelRef.get();
467
if (pm instanceof ExPropertyModel) {
468
FeatureDescriptor fd = ((ExPropertyModel) pm).getFeatureDescriptor();
477
Property p = pd.getProperty();
479
if (p instanceof ModelProperty) {
480
return ((ModelProperty) p).getFeatureDescriptor();
481
} else if (p instanceof ModelProperty.DPMWrapper) {
482
return ((ModelProperty.DPMWrapper) p).getFeatureDescriptor();
488
private InplaceEditor createInplaceEditor() {
489
PropertyEnv env = new PropertyEnv();
490
env.setFeatureDescriptor(findFeatureDescriptor(this));
492
InplaceEditor result;
494
//Get the real inplace editor
495
InplaceEditor innermost = result = factory(this).getInplaceEditor(getProperty(), env, true);
497
// System.err.println(" CREATE INPLACE EDITOR - INNERMOST IS " + innermost);
498
//See if it should be embedded in an instance of ButtonPanel to show
499
//the custom editor button
500
if (isShowCustomEditorButton() && innermost.getPropertyEditor().supportsCustomEditor()) {
501
ButtonPanel bp = new ButtonPanel();
502
bp.setInplaceEditor(innermost);
504
// System.err.println(" wrapping in a buttonpanel");
505
configureButtonPanel(bp);
511
//See if there's an icon to display, either invalid state or
512
//a property-specified icon
513
if (env.getState() == env.STATE_INVALID) {
514
ic = new ImageIcon(Utilities.loadImage("org/openide/resources/propertysheet/invalid.gif")); //NOI18N
515
} else if (getProperty().getValue("valueIcon") != null) { //NOI18N
517
Object o = getProperty().getValue("valueIcon"); //NOI18N
519
if (o instanceof Image) {
520
ic = new ImageIcon((Image) o);
526
//If we have an icon, use an IconPanel to display it
528
IconPanel iconPanel = new IconPanel();
529
iconPanel.setIcon(ic);
530
iconPanel.setInplaceEditor(result);
532
// System.err.println(" wrapping in an IconPanel");
541
private InplaceEditorFactory factory(PropertyDisplayer_Inline inline) {
542
InplaceEditorFactory result;
544
if (inline.isTableUI()) {
545
if (factory1 == null) {
546
factory1 = new InplaceEditorFactory(inline.isTableUI(), inline.getReusablePropertyEnv());
551
if (factory2 == null) {
552
factory2 = new InplaceEditorFactory(inline.isTableUI(), inline.getReusablePropertyEnv());
558
result.setUseRadioBoolean(inline.isRadioBoolean());
559
result.setRadioButtonMax(inline.getRadioButtonMax());
560
result.setUseLabels(inline.isUseLabels());
565
public boolean isTitleDisplayed() {
567
InplaceEditor inp = null;
569
if (inplace != null) {
572
inp = createInplaceEditor();
575
InplaceEditor most = PropUtils.findInnermostInplaceEditor(inp);
577
return (most instanceof RadioInplaceEditor || most instanceof CheckboxInplaceEditor);
583
public boolean isRadioBoolean() {
587
public void setRadioBoolean(boolean b) {
591
public ReusablePropertyEnv getReusablePropertyEnv() {
595
static final Object[] findBeans(PropertyDisplayer pd) {
596
Object[] result = null;
598
if (pd instanceof EditorPropertyDisplayer) {
599
//Issue 38132, fiendish evil to support PropertyEnv.getBeans()
600
EditorPropertyDisplayer epd = (EditorPropertyDisplayer) pd;
602
if (epd.modelRef != null) {
603
PropertyModel pm = epd.modelRef.get();
605
if (pm instanceof ExPropertyModel) {
606
result = ((ExPropertyModel) pm).getBeans();
611
if (result == null) {
612
Property p = pd.getProperty();
614
if (p instanceof ModelProperty) {
615
result = ((ModelProperty) p).getBeans();
616
} else if (p instanceof ModelProperty.DPMWrapper) {
617
result = ((ModelProperty.DPMWrapper) p).getBeans();
620
pd instanceof EditorPropertyDisplayer &&
621
((EditorPropertyDisplayer) pd).getParent() instanceof PropertyPanel
623
result = ((PropertyPanel) ((EditorPropertyDisplayer) pd).getParent()).getBeans();
625
pd instanceof RendererPropertyDisplayer &&
626
((RendererPropertyDisplayer) pd).getParent() instanceof PropertyPanel
628
result = ((PropertyPanel) ((RendererPropertyDisplayer) pd).getParent()).getBeans();