001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.OOOO
040: */
041:
042: package org.netbeans.modules.vmd.api.properties;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyEditorSupport;
046: import java.beans.PropertyVetoException;
047: import java.beans.VetoableChangeListener;
048: import java.lang.ref.WeakReference;
049: import java.lang.reflect.InvocationTargetException;
050: import java.util.Collection;
051: import java.util.List;
052:
053: import org.netbeans.modules.vmd.api.model.DesignComponent;
054: import org.netbeans.modules.vmd.api.model.DesignDocument;
055: import org.netbeans.modules.vmd.api.model.DesignEvent;
056: import org.netbeans.modules.vmd.api.model.PropertyDescriptor;
057: import org.netbeans.modules.vmd.api.model.PropertyValue;
058: import org.netbeans.modules.vmd.api.model.common.ActiveDocumentSupport;
059: import org.netbeans.modules.vmd.api.model.presenters.InfoPresenter;
060: import org.openide.explorer.propertysheet.ExPropertyEditor;
061: import org.openide.explorer.propertysheet.InplaceEditor;
062: import org.openide.explorer.propertysheet.InplaceEditor.Factory;
063: import org.openide.explorer.propertysheet.PropertyEnv;
064: import org.openide.nodes.PropertySupport;
065:
066: /**
067: *
068: * @author Karol Harezlak
069: */
070:
071: /**
072: *
073: * This class represents custom property editor in the Properties Window.
074: */
075: public abstract class DesignPropertyEditor extends
076: PropertyEditorSupport implements ExPropertyEditor, Factory {
077:
078: private List<String> propertyNames;
079: private WeakReference<DesignComponent> component;
080: private Object tempValue;
081: private PropertyValue propertyValue;
082: private PropertySupport propertySupport;
083: private InplaceEditor inplaceEditor;
084: private String propertyDisplayName;
085: private String customEditorTitle;
086:
087: /**
088: * Useful especially when passing value Boolean.FALSE - in this case disable
089: * the possibility of editing the the value as text but allows getAsText to
090: * return non null. Also used by passing Boolean.TRUE to indicate
091: * that a editor should allow the user to type their own text into the combo box.
092: * @return the state of property in-place editor in the advanced property sheet
093: */
094: public Boolean canEditAsText() {
095: return null;
096: }
097:
098: /**
099: * Test whether the property is writable.
100: * @return true if the write of the value is supported
101: */
102: public boolean canWrite() {
103: return true;
104: }
105:
106: /**
107: * This method indicates whether the current value is the same as the value
108: * that would otherwise be restored by calling restoreDefaultValue()
109: * (if supportsDefaultValue() returns true). The default implementation
110: * returns true and it is recommended to also return true when supportsDefaultValue()
111: * returns false (if we do not support default value any value can be considered
112: * as the default). If supportsDefaultValue() returns false this method will
113: * not be called by the default implementation of property sheet.
114: * @return on default checks
115: */
116: public boolean isDefaultValue() {
117: if (propertyNames == null || propertyNames.isEmpty()) {
118: return true;
119: }
120:
121: final boolean[] isDefaultValue = new boolean[] { true };
122: component.get().getDocument().getTransactionManager()
123: .readAccess(new Runnable() {
124: public void run() {
125: for (String propertyName : propertyNames) {
126: if (!component.get().isDefaultValue(
127: propertyName)) {
128: isDefaultValue[0] = false;
129: break;
130: }
131: }
132: }
133: });
134:
135: return isDefaultValue[0];
136: }
137:
138: @Override
139: public boolean supportsCustomEditor() {
140: Collection components = ActiveDocumentSupport.getDefault()
141: .getActiveComponents();
142: if (components != null && components.size() == 1)
143: return true;
144:
145: return false;
146: }
147:
148: /**
149: * Test whether the property had a default value.
150: * @return true if it does (true by default)
151: */
152: public boolean supportsDefaultValue() {
153: if (propertyNames == null || propertyNames.isEmpty()) {
154: return false;
155: }
156:
157: final boolean[] supportsDefaultValue = new boolean[] { true };
158: component.get().getDocument().getTransactionManager()
159: .readAccess(new Runnable() {
160: public void run() {
161: for (String propertyName : propertyNames) {
162: PropertyDescriptor propertyDescriptor = component
163: .get()
164: .getComponentDescriptor()
165: .getPropertyDescriptor(propertyName);
166: if (!(propertyDescriptor.getDefaultValue()
167: .getKind() != PropertyValue.Kind.NULL || propertyDescriptor
168: .isAllowNull())) {
169: supportsDefaultValue[0] = false;
170: break;
171: }
172: }
173: }
174: });
175:
176: return supportsDefaultValue[0];
177: }
178:
179: /**
180: * Returns value used to restore this property to its default value, if supported. In the default
181: * implementation, returns defaultValue of PropertyValue.
182: * @return default value specifed in the model for this property
183: */
184: public Object getDefaultValue() {
185: if (propertyNames == null || propertyNames.isEmpty())
186: throw new IllegalStateException(
187: "Unable to obtain default value for this property without property name"); //NOI18N
188:
189: if (!(tempValue instanceof GroupValue))
190: return readDefaultPropertyValue(propertyNames.iterator()
191: .next());
192:
193: GroupValue newAdvancedValue = ((GroupValue) tempValue);
194: for (String propertyName : newAdvancedValue.getPropertyNames()) {
195: ((GroupValue) tempValue).putValue(propertyName,
196: readDefaultPropertyValue(propertyName));
197: }
198:
199: return newAdvancedValue;
200: }
201:
202: /**
203: * This method is called by the property sheet to pass the environment to the property editor.
204: * The typical use case is for the ExPropertyEditor to call env.getFeatureDescriptor().getValue
205: * (String key) to retrieve any hints the Property object may supply regarding how
206: * the property editor should behave(such as providing alternate text representations of
207: * "true" and "false" for a Boolean property editor).
208: * @param allows an object (suchDef as a Node.Property instance) to communicate hints to
209: * property editor instances
210: */
211: public void attachEnv(PropertyEnv env) {
212: env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
213: env.addVetoableChangeListener(new VetoableChangeListener() {
214: public void vetoableChange(PropertyChangeEvent evt)
215: throws PropertyVetoException {
216: customEditorOKButtonPressed();
217: }
218: });
219: }
220:
221: public final void resolve(DesignComponent component,
222: List<String> propertyNames, Object value,
223: PropertySupport propertySupport, String propertyDisplayName) {
224:
225: this .component = new WeakReference<DesignComponent>(component);
226: this .propertyNames = propertyNames;
227: this .tempValue = value;
228: super .setValue(value);
229: this .propertySupport = propertySupport;
230: this .propertyDisplayName = propertyDisplayName;
231: }
232:
233: public final void resolveInplaceEditor(InplaceEditor inplaceEditor) {
234: this .inplaceEditor = inplaceEditor;
235: }
236:
237: public InplaceEditor getInplaceEditor() {
238: return inplaceEditor;
239: }
240:
241: /**
242: * Title of the custom property editor dialog.
243: * @return custom property editor title
244: */
245: public String getCustomEditorTitle() {
246: if (component == null)
247: return null;
248:
249: component.get().getDocument().getTransactionManager()
250: .readAccess((new Runnable() {
251: public void run() {
252: DesignDocument document = component.get()
253: .getDocument();
254: if (component.get().getParentComponent() == null
255: && document.getRootComponent() != component
256: .get()) {
257: customEditorTitle = null;
258: return;
259: }
260: customEditorTitle = InfoPresenter
261: .getDisplayName(component.get())
262: + " - " + propertyDisplayName; //NOI18N
263: }
264: }));
265: return customEditorTitle;
266: }
267:
268: /**
269: * It's called when PropertyEditor is attached to the DesignComponent using PropertiesPresenter (notifyAttached)
270: * @param DesignComponent attached to this PropertyEditor
271: */
272: public void init(DesignComponent component) {
273: }
274:
275: /**
276: * Its called according to the DesignEventFilterResolver(DesignEventFilter) passed into the PropertiesPresenter
277: *
278: */
279: public void notifyDesignChanged(DesignEvent event) {
280: }
281:
282: /**
283: * Describes if executeInsideWriteTransaction() will be executed inside write
284: * transaction responsible for saving data from property editor into the model.
285: * @return Boolean.TRUE will be executed.
286: */
287: public boolean isExecuteInsideWriteTransactionUsed() {
288: return true;
289: }
290:
291: /**
292: * This method is executed at the beginning of write transaction when data from property
293: * editor is writes into the model
294: * @return Boolean.FALSE ONLY this method will be executed without any other
295: * support it's means that value of property editor is not saved
296: */
297: public boolean executeInsideWriteTransaction() {
298: return true;
299: }
300:
301: /**
302: * Method is invoked after OK button is pressed in the custom editor.
303: */
304: public void customEditorOKButtonPressed() {
305: }
306:
307: /**
308: * This method gives support to chose how to handle reset To Defualt event inside of custom property editor.
309: * When returns Boolean.True (on default) reset to default is handle automatically
310: * by the method getDefaultValue and all values connected with this
311: * property editor are restored to the default based on the getDefaultValue method.
312: * When return Boolean.False, restoring values to the default state
313: * has to be resolve completely inside of method customEditorResetToDefaultButtonPressed
314: * @return boolean value
315: */
316: public boolean isResetToDefaultAutomatically() {
317: return true;
318: }
319:
320: /**
321: * Method is invoked after Reset To Defaulat button is pressed in the custom property editor.
322: * This method is executed only when isResetToDefaultAutomatically return Boolean.False.
323: * NOTE: This method is executed inside of write transaction.
324: */
325: public void customEditorResetToDefaultButtonPressed() {
326: }
327:
328: @SuppressWarnings("unchecked")
329: // NOI18N
330: public final void invokeSaveToModel() {
331: try {
332: propertySupport.setValue(getValue());
333: } catch (IllegalArgumentException ex) {
334: ex.printStackTrace();
335: } catch (InvocationTargetException ex) {
336: ex.printStackTrace();
337: } catch (IllegalAccessException ex) {
338: ex.printStackTrace();
339: }
340: }
341:
342: private PropertyValue readDefaultPropertyValue(
343: final String propertyName) {
344: component.get().getDocument().getTransactionManager()
345: .readAccess(new Runnable() {
346: public void run() {
347: propertyValue = component.get()
348: .getComponentDescriptor()
349: .getPropertyDescriptor(propertyName)
350: .getDefaultValue();
351: }
352: });
353:
354: return propertyValue;
355: }
356:
357: public String getPropertyDisplayName() {
358: return propertyDisplayName;
359: }
360: }
|