~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to openide/explorer/src/org/openide/explorer/propertysheet/CustomEditorAction.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 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
 * Contributor(s):
 
25
 *
 
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.
 
29
 *
 
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.
 
40
 */
 
41
 
 
42
package org.openide.explorer.propertysheet;
 
43
 
 
44
import org.openide.explorer.propertysheet.editors.EnhancedCustomPropertyEditor;
 
45
import org.openide.nodes.Node.*;
 
46
import org.openide.util.NbBundle;
 
47
 
 
48
import java.awt.*;
 
49
import java.awt.event.ActionEvent;
 
50
import java.awt.event.WindowAdapter;
 
51
import java.awt.event.WindowEvent;
 
52
import java.awt.event.WindowListener;
 
53
 
 
54
import java.beans.*;
 
55
 
 
56
import java.lang.ref.WeakReference;
 
57
 
 
58
import javax.swing.AbstractAction;
 
59
import javax.swing.JDialog;
 
60
import org.openide.util.Exceptions;
 
61
 
 
62
 
 
63
/** Action to invoke the custom editor.
 
64
 *
 
65
 * @author  Tim Boudreau
 
66
 */
 
67
class CustomEditorAction extends AbstractAction {
 
68
    private Invoker invoker;
 
69
    private WeakReference<PropertyModel> modelRef = null;
 
70
 
 
71
    /** Creates a new instance of CustomEditorAction */
 
72
    public CustomEditorAction(Invoker invoker) {
 
73
        this.invoker = invoker;
 
74
        putValue(SMALL_ICON, PropUtils.getCustomButtonIcon());
 
75
    }
 
76
 
 
77
    public CustomEditorAction(Invoker invoker, PropertyModel mdl) {
 
78
        this(invoker);
 
79
 
 
80
        if (mdl != null) {
 
81
            //            System.err.println("Creating custom editor action for model " + mdl);
 
82
            modelRef = new WeakReference<PropertyModel>(mdl);
 
83
        }
 
84
    }
 
85
 
 
86
    public void actionPerformed(ActionEvent ae) {
 
87
        if (PropUtils.isLoggable(CustomEditorAction.class)) {
 
88
            PropUtils.log(CustomEditorAction.class, "CustomEditorAction invoked " + ae); //NOI18N
 
89
        }
 
90
 
 
91
        if (!invoker.allowInvoke()) {
 
92
            if (PropUtils.isLoggable(CustomEditorAction.class)) {
 
93
                PropUtils.log(
 
94
                    CustomEditorAction.class,
 
95
                    "Invoker (" + invoker.getClass() + " allowInvoke() returned false.  Aborting."
 
96
                ); //NOI18N
 
97
            }
 
98
 
 
99
            return;
 
100
        }
 
101
 
 
102
        PropertyModel refd = (modelRef != null) ? modelRef.get() : null;
 
103
 
 
104
        //get the feature descriptor in question
 
105
        FeatureDescriptor fd = invoker.getSelection();
 
106
 
 
107
        final Property p = (fd instanceof Property) ? (Property) fd : null;
 
108
 
 
109
        //if it's not a property...
 
110
        if (p == null) {
 
111
            if (PropUtils.isLoggable(CustomEditorAction.class)) {
 
112
                PropUtils.log(
 
113
                    CustomEditorAction.class,
 
114
                    "Cant invoke custom " + "editor on " + fd + " it is null or not a Property." + "Aborting."
 
115
                ); //NOI18N
 
116
            }
 
117
 
 
118
            //Somebody invoked it from the keyboard on an expandable set
 
119
            Toolkit.getDefaultToolkit().beep();
 
120
 
 
121
            return;
 
122
        }
 
123
 
 
124
        final java.beans.PropertyEditor editor = PropUtils.getPropertyEditor(p);
 
125
 
 
126
        //Create a new PropertyEnv to carry the values of the Property to the editor
 
127
        PropertyEnv env = null;
 
128
 
 
129
        if (editor instanceof ExPropertyEditor) {
 
130
            if (PropUtils.isLoggable(CustomEditorAction.class)) {
 
131
                PropUtils.log(CustomEditorAction.class, "Editor is an " + "ExPropertyEditor, attaching a PropertyEnv"); //NOI18N
 
132
            }
 
133
 
 
134
            env = new PropertyEnv();
 
135
            env.setFeatureDescriptor(fd);
 
136
 
 
137
            if (invoker instanceof SheetTable) {
 
138
                if (PropUtils.isLoggable(CustomEditorAction.class)) {
 
139
                    PropUtils.log(
 
140
                        CustomEditorAction.class, "env.setBeans to " + invoker.getReusablePropertyEnv().getBeans()
 
141
                    ); //NOI18N
 
142
                }
 
143
 
 
144
                env.setBeans(invoker.getReusablePropertyEnv().getBeans());
 
145
            }
 
146
 
 
147
            //Set up the editor with any hints from the property
 
148
            ((ExPropertyEditor) editor).attachEnv(env);
 
149
        }
 
150
 
 
151
        //if there is no custom property editor...
 
152
        if (!editor.supportsCustomEditor()) {
 
153
            if (PropUtils.isLoggable(CustomEditorAction.class)) {
 
154
                PropUtils.log(
 
155
                    CustomEditorAction.class,
 
156
                    "Cant invoke custom " + "editor for editor " + editor + " - it returns false " +
 
157
                    "from supportsCustomEditor()."
 
158
                ); //NOI18N
 
159
            }
 
160
 
 
161
            //Somebody invoked it from the keyboard on an editor w/o custom editor
 
162
            Toolkit.getDefaultToolkit().beep();
 
163
 
 
164
            return;
 
165
        }
 
166
 
 
167
        final Component curComp = invoker.getCursorChangeComponent();
 
168
 
 
169
        Cursor cur = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
 
170
        curComp.setCursor(cur);
 
171
        try { //#64007 start - reset cursor in case of a runtime exception
 
172
 
 
173
        //            customEditing = true;
 
174
        Object partialValue = invoker.getPartialValue();
 
175
 
 
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) {
 
180
            try {
 
181
                if ((editor.getValue() == null) // Fix #13339
 
182
                         ||!(partialValue.toString().equals(editor.getAsText()))) {
 
183
                    if (!(editor instanceof PropUtils.DifferentValuesEditor)) {
 
184
                        editor.setAsText(partialValue.toString());
 
185
                    }
 
186
                }
 
187
            } catch (ProxyNode.DifferentValuesException dve) {
 
188
                // old value will be set back
 
189
            } catch (Exception ite) {
 
190
                // old value will be set back
 
191
            }
 
192
        }
 
193
 
 
194
        //horrible, I need the nodes anyway
 
195
        final PropertyModel mdl = (refd == null) ? new NodePropertyModel(p, null) : refd;
 
196
        String fdName;
 
197
 
 
198
        if ((mdl instanceof ExPropertyModel && (((ExPropertyModel) mdl).getFeatureDescriptor() != null))) {
 
199
            fdName = ((ExPropertyModel) mdl).getFeatureDescriptor().getDisplayName();
 
200
        } else {
 
201
            fdName = null;
 
202
        }
 
203
 
 
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
 
211
            )
 
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
 
216
 
 
217
        final PropertyDialogManager pdm = new PropertyDialogManager(
 
218
                NbBundle.getMessage(
 
219
                    CustomEditorAction.class, "PS_EditorTitle", //NOI18N
 
220
                    (title == null) ? "" : title, // NOI18N
 
221
                    p.getValueType()
 
222
                ), true, editor, mdl, env
 
223
            );
 
224
 
 
225
        boolean shouldListen = !(pdm.getComponent() instanceof EnhancedCustomPropertyEditor) &&
 
226
            (p.canWrite() && (invoker.wantAllChanges() || ((env == null) || env.isChangeImmediate())));
 
227
 
 
228
        final PropertyChangeListener pcl = (!shouldListen) ? null
 
229
                                                           : (new PropertyChangeListener() {
 
230
                    private boolean updating = false;
 
231
 
 
232
                    public void propertyChange(PropertyChangeEvent pce) {
 
233
                        if (updating) {
 
234
                            return;
 
235
                        }
 
236
 
 
237
                        updating = true;
 
238
 
 
239
                        try {
 
240
                            boolean success = PropUtils.updateProp(mdl, editor, title);
 
241
 
 
242
                            if (success) {
 
243
                                invoker.valueChanged(editor);
 
244
                            } else if( !pdm.wasCancelled() ) { //don't bother if custom editor was cancelled
 
245
                                invoker.failed();
 
246
                            }
 
247
                        } finally {
 
248
                            updating = false;
 
249
                        }
 
250
                    }
 
251
                });
 
252
 
 
253
        if (pcl != null) {
 
254
            editor.addPropertyChangeListener(pcl);
 
255
        }
 
256
 
 
257
        final java.awt.Window w = pdm.getDialog();
 
258
 
 
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()) {
 
263
                            try {
 
264
                                invoker.valueChanged(pdm.getEditor());
 
265
                            } catch (Exception ex) {
 
266
                                //do nothing
 
267
                            }
 
268
                        }
 
269
                    }
 
270
 
 
271
                    invoker.editorClosed();
 
272
                    w.removeWindowListener(this);
 
273
 
 
274
                    if (pcl != null) {
 
275
                        editor.removePropertyChangeListener(pcl);
 
276
                    }
 
277
 
 
278
                    //                        customEditing=false;
 
279
                }
 
280
 
 
281
                public void windowOpened(WindowEvent e) {
 
282
                    invoker.editorOpened();
 
283
                    curComp.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 
284
                }
 
285
 
 
286
                // MCF ISSUE 44366 
 
287
                public void windowClosing(WindowEvent ev) {
 
288
                    if (PropUtils.isLoggable(CustomEditorAction.class)) {
 
289
                        PropUtils.log(CustomEditorAction.class, "CustomerEditorAction windowClosing event");
 
290
                    }
 
291
 
 
292
                    closedOption = true;
 
293
                }
 
294
 
 
295
                //  MCF ISSUE 44366 
 
296
                boolean closedOption = false;
 
297
            };
 
298
 
 
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);
 
304
 
 
305
            if (fd.getShortDescription() != null) {
 
306
                jd.getAccessibleContext().setAccessibleDescription(fd.getShortDescription());
 
307
            }
 
308
 
 
309
            w.addWindowListener(wl);
 
310
        } else if (w instanceof Frame) {
 
311
            ((Frame) w).addWindowListener(wl);
 
312
        }
 
313
 
 
314
        invoker.editorOpening();
 
315
 
 
316
        try {
 
317
            PropUtils.addExternallyEdited(p);
 
318
            w.setVisible(true);
 
319
            PropUtils.removeExternallyEdited(p);
 
320
        } catch (Exception ex) {
 
321
            Exceptions.printStackTrace(ex);
 
322
        }
 
323
        
 
324
        } finally { //#64007 end - reset cursor in case of a runtime exception
 
325
            curComp.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 
326
        }
 
327
    }
 
328
 
 
329
    static interface Invoker {
 
330
        /** Get the selected object that should be edited  */
 
331
        public FeatureDescriptor getSelection();
 
332
 
 
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();
 
336
 
 
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();
 
341
 
 
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();
 
345
 
 
346
        /** Callback in case the invoker wants to do something before the
 
347
         * window is opened */
 
348
        public void editorOpening();
 
349
 
 
350
        /** Callback to notify the invoker that the window has opened */
 
351
        public void editorOpened();
 
352
 
 
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();
 
359
 
 
360
        /** Called if the value is changed successfully */
 
361
        public void valueChanged(PropertyEditor editor);
 
362
 
 
363
        /** Allow an invoker to block invocation of the custom editor if it
 
364
         * is not in an appropriate state */
 
365
        public boolean allowInvoke();
 
366
 
 
367
        /** Called if an update was attempted but the value was illegal */
 
368
        public void failed();
 
369
 
 
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();
 
374
 
 
375
        public ReusablePropertyEnv getReusablePropertyEnv();
 
376
    }
 
377
}