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
package org.openide.explorer.propertysheet;
44
import org.openide.explorer.propertysheet.editors.EnhancedCustomPropertyEditor;
45
import org.openide.nodes.Node.*;
46
import org.openide.util.NbBundle;
49
import java.awt.event.ActionEvent;
50
import java.awt.event.WindowAdapter;
51
import java.awt.event.WindowEvent;
52
import java.awt.event.WindowListener;
56
import java.lang.ref.WeakReference;
58
import javax.swing.AbstractAction;
59
import javax.swing.JDialog;
60
import org.openide.util.Exceptions;
63
/** Action to invoke the custom editor.
65
* @author Tim Boudreau
67
class CustomEditorAction extends AbstractAction {
68
private Invoker invoker;
69
private WeakReference<PropertyModel> modelRef = null;
71
/** Creates a new instance of CustomEditorAction */
72
public CustomEditorAction(Invoker invoker) {
73
this.invoker = invoker;
74
putValue(SMALL_ICON, PropUtils.getCustomButtonIcon());
77
public CustomEditorAction(Invoker invoker, PropertyModel mdl) {
81
// System.err.println("Creating custom editor action for model " + mdl);
82
modelRef = new WeakReference<PropertyModel>(mdl);
86
public void actionPerformed(ActionEvent ae) {
87
if (PropUtils.isLoggable(CustomEditorAction.class)) {
88
PropUtils.log(CustomEditorAction.class, "CustomEditorAction invoked " + ae); //NOI18N
91
if (!invoker.allowInvoke()) {
92
if (PropUtils.isLoggable(CustomEditorAction.class)) {
94
CustomEditorAction.class,
95
"Invoker (" + invoker.getClass() + " allowInvoke() returned false. Aborting."
102
PropertyModel refd = (modelRef != null) ? modelRef.get() : null;
104
//get the feature descriptor in question
105
FeatureDescriptor fd = invoker.getSelection();
107
final Property p = (fd instanceof Property) ? (Property) fd : null;
109
//if it's not a property...
111
if (PropUtils.isLoggable(CustomEditorAction.class)) {
113
CustomEditorAction.class,
114
"Cant invoke custom " + "editor on " + fd + " it is null or not a Property." + "Aborting."
118
//Somebody invoked it from the keyboard on an expandable set
119
Toolkit.getDefaultToolkit().beep();
124
final java.beans.PropertyEditor editor = PropUtils.getPropertyEditor(p);
126
//Create a new PropertyEnv to carry the values of the Property to the editor
127
PropertyEnv env = null;
129
if (editor instanceof ExPropertyEditor) {
130
if (PropUtils.isLoggable(CustomEditorAction.class)) {
131
PropUtils.log(CustomEditorAction.class, "Editor is an " + "ExPropertyEditor, attaching a PropertyEnv"); //NOI18N
134
env = new PropertyEnv();
135
env.setFeatureDescriptor(fd);
137
if (invoker instanceof SheetTable) {
138
if (PropUtils.isLoggable(CustomEditorAction.class)) {
140
CustomEditorAction.class, "env.setBeans to " + invoker.getReusablePropertyEnv().getBeans()
144
env.setBeans(invoker.getReusablePropertyEnv().getBeans());
147
//Set up the editor with any hints from the property
148
((ExPropertyEditor) editor).attachEnv(env);
151
//if there is no custom property editor...
152
if (!editor.supportsCustomEditor()) {
153
if (PropUtils.isLoggable(CustomEditorAction.class)) {
155
CustomEditorAction.class,
156
"Cant invoke custom " + "editor for editor " + editor + " - it returns false " +
157
"from supportsCustomEditor()."
161
//Somebody invoked it from the keyboard on an editor w/o custom editor
162
Toolkit.getDefaultToolkit().beep();
167
final Component curComp = invoker.getCursorChangeComponent();
169
Cursor cur = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
170
curComp.setCursor(cur);
171
try { //#64007 start - reset cursor in case of a runtime exception
173
// customEditing = true;
174
Object partialValue = invoker.getPartialValue();
176
//Okay, we can display a custom editor.
177
//If the user has already typed something in a text field, pass it to the editor,
178
//even if they haven't updated the value yet
179
if (partialValue != null) {
181
if ((editor.getValue() == null) // Fix #13339
182
||!(partialValue.toString().equals(editor.getAsText()))) {
183
if (!(editor instanceof PropUtils.DifferentValuesEditor)) {
184
editor.setAsText(partialValue.toString());
187
} catch (ProxyNode.DifferentValuesException dve) {
188
// old value will be set back
189
} catch (Exception ite) {
190
// old value will be set back
194
//horrible, I need the nodes anyway
195
final PropertyModel mdl = (refd == null) ? new NodePropertyModel(p, null) : refd;
198
if ((mdl instanceof ExPropertyModel && (((ExPropertyModel) mdl).getFeatureDescriptor() != null))) {
199
fdName = ((ExPropertyModel) mdl).getFeatureDescriptor().getDisplayName();
204
//Support hinting of the title
205
String suppliedTitle = (String) p.getValue("title"); //NOI18N
206
final String title = (suppliedTitle == null)
207
? ((fd.getDisplayName() == null)
208
? //XXX does this ever happen??
209
NbBundle.getMessage(CustomEditorAction.class, "FMT_CUSTOM_DLG_NOPROPNAME_TITLE",
210
fdName == null ? invoker.getBeanName() : fdName
212
: ((fd.getDisplayName().equals(invoker.getBeanName())) ? invoker.getBeanName() :
213
NbBundle.getMessage(CustomEditorAction.class, "FMT_CUSTOM_DLG_TITLE",
214
invoker.getBeanName(), fd.getDisplayName())
215
)) : suppliedTitle; //NOI18N
217
final PropertyDialogManager pdm = new PropertyDialogManager(
219
CustomEditorAction.class, "PS_EditorTitle", //NOI18N
220
(title == null) ? "" : title, // NOI18N
222
), true, editor, mdl, env
225
boolean shouldListen = !(pdm.getComponent() instanceof EnhancedCustomPropertyEditor) &&
226
(p.canWrite() && (invoker.wantAllChanges() || ((env == null) || env.isChangeImmediate())));
228
final PropertyChangeListener pcl = (!shouldListen) ? null
229
: (new PropertyChangeListener() {
230
private boolean updating = false;
232
public void propertyChange(PropertyChangeEvent pce) {
240
boolean success = PropUtils.updateProp(mdl, editor, title);
243
invoker.valueChanged(editor);
244
} else if( !pdm.wasCancelled() ) { //don't bother if custom editor was cancelled
254
editor.addPropertyChangeListener(pcl);
257
final java.awt.Window w = pdm.getDialog();
259
WindowListener wl = new WindowAdapter() {
260
public void windowClosed(WindowEvent e) {
261
if (pdm.getComponent() instanceof EnhancedCustomPropertyEditor) {
262
if (!pdm.wasCancelled() && !closedOption && pdm.wasOK() && !pdm.wasReset()) {
264
invoker.valueChanged(pdm.getEditor());
265
} catch (Exception ex) {
271
invoker.editorClosed();
272
w.removeWindowListener(this);
275
editor.removePropertyChangeListener(pcl);
278
// customEditing=false;
281
public void windowOpened(WindowEvent e) {
282
invoker.editorOpened();
283
curComp.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
287
public void windowClosing(WindowEvent ev) {
288
if (PropUtils.isLoggable(CustomEditorAction.class)) {
289
PropUtils.log(CustomEditorAction.class, "CustomerEditorAction windowClosing event");
296
boolean closedOption = false;
299
//Don't set customEditing for non-dialog custom property editors - another
300
//editor can be opened at the same time
301
if (w instanceof JDialog) {
302
JDialog jd = (JDialog) w;
303
jd.getAccessibleContext().setAccessibleName(title);
305
if (fd.getShortDescription() != null) {
306
jd.getAccessibleContext().setAccessibleDescription(fd.getShortDescription());
309
w.addWindowListener(wl);
310
} else if (w instanceof Frame) {
311
((Frame) w).addWindowListener(wl);
314
invoker.editorOpening();
317
PropUtils.addExternallyEdited(p);
319
PropUtils.removeExternallyEdited(p);
320
} catch (Exception ex) {
321
Exceptions.printStackTrace(ex);
324
} finally { //#64007 end - reset cursor in case of a runtime exception
325
curComp.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
329
static interface Invoker {
330
/** Get the selected object that should be edited */
331
public FeatureDescriptor getSelection();
333
/** If the value displayed has been edited but not committed, get
334
* the partial edit to set in the custom editor */
335
public Object getPartialValue();
337
/** Get the component which is invoking the custom editor, and
338
* should have its cursor changed while the dialog is being opened
339
* (the cursor change will actually happen on its ancestor root pane)*/
340
public Component getCursorChangeComponent();
342
/** Get the name of the bean, if any, that owns the property - this
343
* value is used as part of the window title */
344
public String getBeanName();
346
/** Callback in case the invoker wants to do something before the
347
* window is opened */
348
public void editorOpening();
350
/** Callback to notify the invoker that the window has opened */
351
public void editorOpened();
353
/** Callback to notify the invoker that the window has closed. If
354
* the invoker should behave differently in response to a failed
355
* edit than in response to a successful one, set a flag to false
356
* when editorOpening() is called, and set it to true if failed()
357
* is called, and do your processing here. */
358
public void editorClosed();
360
/** Called if the value is changed successfully */
361
public void valueChanged(PropertyEditor editor);
363
/** Allow an invoker to block invocation of the custom editor if it
364
* is not in an appropriate state */
365
public boolean allowInvoke();
367
/** Called if an update was attempted but the value was illegal */
368
public void failed();
370
/** Should valueUpdated be called even if the editor is not ExPropertyEditor?
371
* PropertyPanel will need this to update inline editors; the property
372
* sheet will repaint anyway and doesn't. */
373
public boolean wantAllChanges();
375
public ReusablePropertyEnv getReusablePropertyEnv();