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.
040: */
041: package org.netbeans.modules.vmd.midp.propertyeditors.api.resource;
042:
043: import java.util.ArrayList;
044: import java.util.Collection;
045: import java.util.Collections;
046: import java.util.HashMap;
047: import java.util.List;
048: import java.util.Map;
049: import java.util.Set;
050: import java.util.TreeMap;
051: import javax.swing.JComponent;
052: import javax.swing.JRadioButton;
053: import org.netbeans.modules.vmd.api.model.ComponentProducer;
054: import org.netbeans.modules.vmd.api.model.Debug;
055: import org.netbeans.modules.vmd.api.model.DesignComponent;
056: import org.netbeans.modules.vmd.api.model.DesignDocument;
057: import org.netbeans.modules.vmd.api.model.PropertyValue;
058: import org.netbeans.modules.vmd.api.model.TypeID;
059: import org.netbeans.modules.vmd.api.model.common.AcceptSupport;
060: import org.netbeans.modules.vmd.api.model.common.DocumentSupport;
061: import org.netbeans.modules.vmd.api.model.presenters.actions.DeleteSupport;
062: import org.netbeans.modules.vmd.api.properties.DesignPropertyEditor;
063: import org.netbeans.modules.vmd.midp.codegen.InstanceNameResolver;
064: import org.netbeans.modules.vmd.midp.components.MidpDocumentSupport;
065: import org.netbeans.modules.vmd.midp.components.MidpTypes;
066: import org.netbeans.modules.vmd.midp.components.MidpValueSupport;
067: import org.netbeans.modules.vmd.midp.components.categories.ResourcesCategoryCD;
068: import org.netbeans.modules.vmd.midp.components.general.ClassCD;
069: import org.netbeans.modules.vmd.midp.propertyeditors.resource.elements.FontEditorElement;
070: import org.netbeans.modules.vmd.midp.propertyeditors.resource.elements.ImageEditorElement;
071: import org.netbeans.modules.vmd.midp.propertyeditors.api.resource.element.PropertyEditorResourceElement;
072: import org.netbeans.modules.vmd.midp.propertyeditors.api.resource.element.PropertyEditorResourceElement.DesignComponentWrapper;
073: import org.netbeans.modules.vmd.midp.propertyeditors.api.usercode.PropertyEditorElement;
074: import org.netbeans.modules.vmd.midp.propertyeditors.api.usercode.PropertyEditorUserCode;
075: import org.netbeans.modules.vmd.midp.propertyeditors.resource.elements.TickerEditorElement;
076: import org.openide.awt.Mnemonics;
077: import org.openide.util.NbBundle;
078:
079: /**
080: *
081: * @author Anton Chechel
082: */
083: public class PropertyEditorResource extends PropertyEditorUserCode
084: implements PropertyEditorElement {
085:
086: private Map<String, DesignComponent> createdComponents;
087: private final TypeID componentTypeID;
088: private String noneComponentAsText;
089: private String newComponentAsText;
090: private ResourceEditorPanel rePanel;
091: private JRadioButton radioButton;
092: private PropertyEditorResourceElement perElement;
093:
094: private PropertyEditorResource(
095: PropertyEditorResourceElement perElement,
096: String newComponentAsText, String noneComponentAsText,
097: String userCodeLabel) {
098: super (userCodeLabel);
099:
100: if (newComponentAsText == null || noneComponentAsText == null) {
101: throw Debug.illegalArgument("Argument can not be null"); //NOI18N
102: }
103:
104: if (newComponentAsText.equals(noneComponentAsText)) {
105: throw Debug.illegalArgument("Arguments can not be equal"); //NOI18N
106: }
107:
108: this .componentTypeID = perElement.getTypeID();
109: this .newComponentAsText = newComponentAsText;
110: this .noneComponentAsText = noneComponentAsText;
111: this .perElement = perElement;
112: perElement.setPropertyEditorMessageAwareness(this );
113:
114: createdComponents = new HashMap<String, DesignComponent>();
115:
116: // TODO lazy init
117: radioButton = new JRadioButton();
118: rePanel = new ResourceEditorPanel(perElement,
119: noneComponentAsText, radioButton);
120: Mnemonics.setLocalizedText(radioButton, NbBundle.getMessage(
121: PropertyEditorResource.class, "LBL_RB_RESOURCE")); // NOI18N
122: radioButton.getAccessibleContext().setAccessibleName(
123: NbBundle.getMessage(PropertyEditorResource.class,
124: "ACSN_RB_RESOURCE"));
125: radioButton.getAccessibleContext().setAccessibleDescription(
126: NbBundle.getMessage(PropertyEditorResource.class,
127: "ACSD_RB_RESOURCE"));
128: initElements(Collections
129: .<PropertyEditorElement> singleton(this ));
130: }
131:
132: public static final PropertyEditorResource createInstance(
133: PropertyEditorResourceElement perElement,
134: String newComponentAsText, String noneComponentAsText,
135: String userCodeLabel) {
136: return new PropertyEditorResource(perElement,
137: newComponentAsText, noneComponentAsText, userCodeLabel);
138: }
139:
140: public static final DesignPropertyEditor createFontPropertyEditor() {
141: return new PropertyEditorResource(new FontEditorElement(),
142: NbBundle.getMessage(PropertyEditorResource.class,
143: "LBL_FONTRESOURCEPE_NEW"), NbBundle.getMessage(
144: PropertyEditorResource.class,
145: "LBL_FONTRESOURCEPE_NONE"), NbBundle
146: .getMessage(PropertyEditorResource.class,
147: "LBL_FONTRESOURCEPE_UCLABEL")); //NOI18N
148: }
149:
150: public static final DesignPropertyEditor createTickerPropertyEditor() {
151: return new PropertyEditorResource(new TickerEditorElement(),
152: NbBundle.getMessage(PropertyEditorResource.class,
153: "LBL_TICKERRESOURCEPE_NEW"), NbBundle
154: .getMessage(PropertyEditorResource.class,
155: "LBL_TICKERRESOURCEPE_NONE"), NbBundle
156: .getMessage(PropertyEditorResource.class,
157: "LBL_TICKERRESOURCEPE_UCLABEL")); //NOI18N
158: }
159:
160: public static final DesignPropertyEditor createImagePropertyEditor() {
161: return new PropertyEditorResource(new ImageEditorElement(),
162: NbBundle.getMessage(PropertyEditorResource.class,
163: "LBL_IMAGERESOURCEPE_NEW"), NbBundle
164: .getMessage(PropertyEditorResource.class,
165: "LBL_IMAGERESOURCEPE_NONE"), NbBundle
166: .getMessage(PropertyEditorResource.class,
167: "LBL_IMAGERESOURCEPE_UCLABEL")); //NOI18N
168: }
169:
170: private Map<String, DesignComponent> getComponentsMap() {
171: final Map<String, DesignComponent> componentsMap = new TreeMap<String, DesignComponent>();
172: if (component == null || component.get() == null) {
173: return componentsMap;
174: }
175:
176: final DesignDocument document = component.get().getDocument();
177: document.getTransactionManager().readAccess(new Runnable() {
178:
179: public void run() {
180: Collection<DesignComponent> components = MidpDocumentSupport
181: .getCategoryComponent(document,
182: ResourcesCategoryCD.TYPEID)
183: .getComponents();
184: for (DesignComponent comp : components) {
185: if (comp.getType().equals(componentTypeID)) {
186: componentsMap.put(
187: getComponentDisplayName(comp), comp);
188: }
189: }
190: }
191: });
192: return componentsMap;
193: }
194:
195: private String getComponentDisplayName(DesignComponent component) {
196: if (component == null) {
197: return noneComponentAsText;
198: }
199: // issue 104721 fix
200: // dirty hack to check whether component was detached from document or not
201: if (component.getParentComponent() == null
202: && component.getDocument().getRootComponent() != component) {
203: return noneComponentAsText;
204: }
205:
206: return MidpValueSupport.getHumanReadableString(component);
207: }
208:
209: @Override
210: public String getAsText() {
211: if (isCurrentValueAUserCodeType()) {
212: return USER_CODE_TEXT;
213: }
214:
215: PropertyValue value = (PropertyValue) super .getValue();
216: return getDecodeValue(value);
217: }
218:
219: private void saveValue(String text) {
220: if (text == null || text.length() <= 0) {
221: return;
222: }
223: if (component == null || component.get() == null) {
224: return;
225: }
226:
227: final DesignDocument document = component.get().getDocument();
228: Map<String, DesignComponent> componentsMap = getComponentsMap();
229: if (componentsMap.get(text) != null) {
230: setValue(PropertyValue
231: .createComponentReference(componentsMap.get(text)));
232: } else if (text.equals(noneComponentAsText)) {
233: setValue(NULL_VALUE);
234: } else if (text.equals(newComponentAsText)) {
235: document.getTransactionManager().writeAccess(
236: new Runnable() {
237:
238: public void run() {
239: ComponentProducer producer = DocumentSupport
240: .getComponentProducer(document,
241: componentTypeID.toString());
242: if (producer == null) {
243: throw new IllegalStateException(
244: "No producer for TypeID : "
245: + componentTypeID
246: .toString()); // NOI18N
247: }
248: DesignComponent category = MidpDocumentSupport
249: .getCategoryComponent(document,
250: ResourcesCategoryCD.TYPEID);
251: ComponentProducer.Result result = AcceptSupport
252: .accept(category, producer, null);
253: DesignComponent createdComponent = result != null ? result
254: .getMainComponent()
255: : null;
256:
257: if (createdComponent != null) {
258: initInstanceNameForComponent(createdComponent);
259: PropertyEditorResource.this
260: .setValue(PropertyValue
261: .createComponentReference(createdComponent));
262: }
263: }
264: });
265: } else {
266: Map<String, DesignComponentWrapper> wrappersMap = rePanel
267: .getWrappersMap();
268: for (String key : wrappersMap.keySet()) {
269: if (key.equals(text)) {
270: DesignComponent createdComponent = createdComponents
271: .get(text);
272: setValue(PropertyValue
273: .createComponentReference(createdComponent));
274: createdComponents.clear();
275: break;
276: }
277: }
278: }
279: }
280:
281: private void setValue(PropertyValue value) {
282: super .setValue(value);
283: if (!NULL_VALUE.equals(value)
284: && perElement.isPostSetValueSupported(component.get())) {
285: perElement.postSetValue(component.get(), value
286: .getComponent());
287: }
288: }
289:
290: // invoke in the write transaction
291: private void initInstanceNameForComponent(DesignComponent component) {
292: String nameToBeCreated = perElement.getResourceNameSuggestion();
293: PropertyValue instanceName = InstanceNameResolver
294: .createFromSuggested(component, nameToBeCreated);
295: component.writeProperty(ClassCD.PROP_INSTANCE_NAME,
296: instanceName);
297: }
298:
299: private String getDecodeValue(final PropertyValue value) {
300: if (value == null || value.getKind() == PropertyValue.Kind.NULL) {
301: return noneComponentAsText;
302: }
303: if (component == null || component.get() == null) {
304: return noneComponentAsText;
305: }
306:
307: final String[] decodeValue = new String[1];
308: component.get().getDocument().getTransactionManager()
309: .readAccess(new Runnable() {
310:
311: public void run() {
312: DesignComponent valueComponent = value
313: .getComponent();
314: decodeValue[0] = getComponentDisplayName(valueComponent);
315: }
316: });
317:
318: return decodeValue[0] != null ? decodeValue[0] : "n/a"; //NOI18N
319: }
320:
321: @Override
322: public String[] getTags() {
323: Set<String> components = getComponentsMap().keySet();
324: List<String> tags = new ArrayList<String>(components.size() + 2);
325: if (isCurrentValueAUserCodeType()) {
326: tags.add(PropertyEditorUserCode.USER_CODE_TEXT);
327: } else {
328: tags.add(noneComponentAsText);
329: tags.addAll(components);
330: tags.add(newComponentAsText);
331: }
332: return tags.toArray(new String[tags.size()]);
333: }
334:
335: @Override
336: public Boolean canEditAsText() {
337: return null;
338: }
339:
340: @Override
341: public void customEditorOKButtonPressed() {
342: super .customEditorOKButtonPressed();
343: if (getRadioButton().isSelected()) {
344: saveChanges();
345: saveValue(getTextForPropertyValue());
346: }
347: }
348:
349: public String getTextForPropertyValue() {
350: return rePanel.getTextForPropertyValue();
351: }
352:
353: private void saveChanges() {
354: if (rePanel.wasAnyDesignComponentChanged()) {
355: Map<String, DesignComponent> componentsMap = getComponentsMap();
356: Map<String, DesignComponentWrapper> wrappersMap = rePanel
357: .getWrappersMap();
358:
359: final Collection<DesignComponent> toBeDeleted = new ArrayList<DesignComponent>();
360: for (final String key : wrappersMap.keySet()) {
361: final DesignComponentWrapper wrapper = wrappersMap
362: .get(key);
363:
364: if (wrapper.hasChanges()) {
365: final DesignComponent _component = componentsMap
366: .get(key);
367: if (_component != null) {
368: _component.getDocument()
369: .getTransactionManager().writeAccess(
370: new Runnable() {
371:
372: public void run() {
373: if (wrapper
374: .getComponent() != null) {
375: // component need to be changed
376: Map<String, PropertyValue> changes = wrapper
377: .getChanges();
378: for (String propertyName : changes
379: .keySet()) {
380: final PropertyValue propertyValue = changes
381: .get(propertyName);
382: _component
383: .writeProperty(
384: propertyName,
385: propertyValue);
386: }
387: } else {
388: // component need to be deleted
389: toBeDeleted
390: .add(_component);
391: }
392: }
393: });
394: } else {
395: // component need to be created
396: if (wrapper.isDeleted()) {
397: // do not create
398: continue;
399: }
400:
401: if (component != null
402: && component.get() != null) {
403: final DesignDocument document = component
404: .get().getDocument();
405: document.getTransactionManager()
406: .writeAccess(new Runnable() {
407:
408: public void run() {
409: ComponentProducer producer = DocumentSupport
410: .getComponentProducer(
411: document,
412: componentTypeID
413: .toString());
414: if (producer == null) {
415: throw new IllegalStateException(
416: "No producer for TypeID : "
417: + componentTypeID
418: .toString()); // NOI18N
419: }
420: DesignComponent category = MidpDocumentSupport
421: .getCategoryComponent(
422: document,
423: ResourcesCategoryCD.TYPEID);
424: ComponentProducer.Result result = AcceptSupport
425: .accept(category,
426: producer,
427: null);
428: DesignComponent createdComponent = result != null ? result
429: .getMainComponent()
430: : null;
431: if (createdComponent != null) {
432: createdComponent
433: .writeProperty(
434: ClassCD.PROP_INSTANCE_NAME,
435: MidpTypes
436: .createStringValue(key));
437:
438: Map<String, PropertyValue> changes = wrapper
439: .getChanges();
440: for (String propertyName : changes
441: .keySet()) {
442: final PropertyValue propertyValue = changes
443: .get(propertyName);
444: createdComponent
445: .writeProperty(
446: propertyName,
447: propertyValue);
448: }
449: createdComponents
450: .put(key,
451: createdComponent);
452: }
453: }
454: });
455: }
456: }
457: }
458:
459: if (!toBeDeleted.isEmpty() && component != null
460: && component.get() != null) {
461: final DesignDocument document = component.get()
462: .getDocument();
463: document.getTransactionManager().writeAccess(
464: new Runnable() {
465:
466: public void run() {
467: DeleteSupport
468: .invokeDirectUserDeletion(
469: document,
470: toBeDeleted, false);
471: }
472: });
473: }
474: }
475: }
476: }
477:
478: public JComponent getCustomEditorComponent() {
479: return rePanel;
480: }
481:
482: public JRadioButton getRadioButton() {
483: return radioButton;
484: }
485:
486: public boolean isInitiallySelected() {
487: return true;
488: }
489:
490: public boolean isVerticallyResizable() {
491: return true;
492: }
493:
494: public void updateState(PropertyValue value) {
495: if (rePanel.needsUpdate()) {
496: radioButton.setSelected(!isCurrentValueAUserCodeType());
497: rePanel.update(getComponentsMap(), getDecodeValue(value));
498: }
499: }
500:
501: public void setTextForPropertyValue(String text) {
502: saveValue(text);
503: }
504: }
|