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:
042: package org.netbeans.modules.vmd.properties;
043:
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.Collections;
047: import java.util.Comparator;
048: import java.util.HashSet;
049: import java.util.List;
050: import java.util.Set;
051: import java.util.WeakHashMap;
052: import javax.swing.SwingUtilities;
053: import org.netbeans.modules.vmd.api.io.ActiveViewSupport;
054: import org.netbeans.modules.vmd.api.io.DataEditorView;
055: import org.netbeans.modules.vmd.api.io.DesignDocumentAwareness;
056: import org.netbeans.modules.vmd.api.model.DesignComponent;
057: import org.netbeans.modules.vmd.api.model.DesignDocument;
058: import org.netbeans.modules.vmd.api.model.DesignEvent;
059: import org.netbeans.modules.vmd.api.model.DesignEventFilter;
060: import org.netbeans.modules.vmd.api.model.DesignListener;
061: import org.netbeans.modules.vmd.api.model.common.ActiveDocumentSupport;
062: import org.netbeans.modules.vmd.api.properties.DesignPropertyDescriptor;
063: import org.netbeans.modules.vmd.api.properties.DesignPropertyEditor;
064: import org.netbeans.modules.vmd.api.properties.GroupPropertyEditor;
065: import org.netbeans.modules.vmd.api.properties.PropertiesPresenter;
066: import org.openide.nodes.Node;
067: import org.openide.nodes.Node.Property;
068: import org.openide.nodes.Sheet;
069: import org.openide.util.WeakSet;
070: import org.openide.util.lookup.InstanceContent;
071:
072: /**
073: * @author Karol Harezlak
074: */
075: //TODO rename it to PropeertiesViewNodesManager or PropeertiesViewNodesController
076: public final class PropertiesNodesManager implements
077: DesignDocumentAwareness, DesignListener,
078: ActiveDocumentSupport.Listener, ActiveViewSupport.Listener {
079:
080: private static final WeakHashMap<DataEditorView, PropertiesNodesManager> INSTANCES = new WeakHashMap<DataEditorView, PropertiesNodesManager>();
081: private static Comparator<DesignPropertyDescriptor> compareByDisplayName = new Comparator<DesignPropertyDescriptor>() {
082: public int compare(DesignPropertyDescriptor descriptor1,
083: DesignPropertyDescriptor descriptor2) {
084: return descriptor1.getPropertyDisplayName().compareTo(
085: descriptor2.getPropertyDisplayName());
086: }
087: };
088:
089: public static synchronized PropertiesNodesManager getInstance(
090: DataEditorView view) {
091: if (INSTANCES.get(view) == null) {
092: PropertiesNodesManager manager = new PropertiesNodesManager(
093: view);
094: INSTANCES.put(view, manager);
095: }
096: return INSTANCES.get(view);
097: }
098:
099: private WeakHashMap<DataEditorView, InstanceContent> icMap;
100: private Collection<InstanceContent> ics;
101: private WeakHashMap<InstanceContent, WeakSet<Node>> nodesToRemoveMap;
102: private DataEditorView view;
103: private DesignDocument document;
104: private WeakHashMap<DesignComponent, WeakSet<DefaultPropertySupport>> propertySupportMap;
105: private WeakHashMap<DesignComponent, Sheet> sheetMap;
106: private WeakHashMap<DesignComponent, PropertiesNode> nodesMap;
107:
108: private PropertiesNodesManager(DataEditorView view) {
109: view.getContext().addDesignDocumentAwareness(this );
110: nodesToRemoveMap = new WeakHashMap<InstanceContent, WeakSet<Node>>();
111: this .view = view;
112: icMap = new WeakHashMap<DataEditorView, InstanceContent>();
113: ics = new HashSet<InstanceContent>();
114: propertySupportMap = new WeakHashMap<DesignComponent, WeakSet<DefaultPropertySupport>>();
115: sheetMap = new WeakHashMap<DesignComponent, Sheet>();
116: nodesMap = new WeakHashMap<DesignComponent, PropertiesNode>();
117: }
118:
119: public void setDesignDocument(DesignDocument document) {
120: if (document != null) {
121: this .document = document;
122: document.getListenerManager().addDesignListener(this ,
123: new DesignEventFilter().setGlobal(true));
124: ActiveDocumentSupport.getDefault()
125: .addActiveDocumentListener(this );
126: ActiveViewSupport.getDefault().addActiveViewListener(this );
127: } else if (this .document != null) {
128: ActiveDocumentSupport.getDefault()
129: .removeActiveDocumentListener(this );
130: ActiveViewSupport.getDefault().removeActiveViewListener(
131: this );
132: this .document.getListenerManager().removeDesignListener(
133: this );
134: this .document = null;
135: view = null;
136: propertySupportMap = null;
137: sheetMap = null;
138: nodesMap = null;
139: }
140: }
141:
142: public void designChanged(DesignEvent event) {
143: final Collection<DesignComponent> selectedComponents = new WeakSet<DesignComponent>(
144: document.getSelectedComponents());
145: if (event.isSelectionChanged()) {
146: repaintPropertiesWindow(selectedComponents);
147: }
148: if (event.isStructureChanged()) {
149: SwingUtilities.invokeLater(new Runnable() {
150: public void run() {
151: PropertiesNodesManager.this
152: .updatePropertyEditorsValues(selectedComponents);
153: PropertiesNodesManager.this
154: .updateSheet(selectedComponents);
155: }
156: });
157: }
158: }
159:
160: public void activeDocumentChanged(
161: DesignDocument deactivatedDocument,
162: DesignDocument activatedDocument) {
163: if (document == null) {
164: return;
165: }
166: if (activatedDocument == null) {
167: document.getTransactionManager().readAccess(new Runnable() {
168:
169: public void run() {
170: repaintPropertiesWindow(Collections.EMPTY_LIST);
171: }
172: });
173: }
174: if (document != activatedDocument) {
175: return;
176: }
177: document.getTransactionManager().readAccess(new Runnable() {
178: public void run() {
179: repaintPropertiesWindow(document
180: .getSelectedComponents());
181: }
182: });
183: }
184:
185: public void activeComponentsChanged(
186: Collection<DesignComponent> activeComponents) {
187: }
188:
189: public void activeViewChanged(final DataEditorView deactivatedView,
190: final DataEditorView activatedView) {
191: if (document == null) {
192: return;
193: }
194: if (activatedView != null
195: && view.getContext() == activatedView.getContext()
196: && activatedView.getKind() == DataEditorView.Kind.MODEL) {
197: document.getTransactionManager().readAccess(new Runnable() {
198:
199: public void run() {
200: repaintPropertiesWindow(document
201: .getSelectedComponents());
202: }
203: });
204: } else if (deactivatedView != null
205: && deactivatedView.getKind() == DataEditorView.Kind.MODEL) {
206: document.getTransactionManager().readAccess(new Runnable() {
207:
208: @SuppressWarnings(value="unchecked")
209: public void run() {
210: repaintPropertiesWindow(Collections.EMPTY_SET);
211: }
212: });
213: }
214: }
215:
216: public void add(InstanceContent ic) {
217: assert (ic != null);
218: this .ics.add(ic);
219: }
220:
221: public void add(DataEditorView view, InstanceContent ic) {
222: assert (ic != null);
223: icMap.put(view, ic);
224: }
225:
226: private void changeLookup(Collection<DesignComponent> components) {
227: if (components == null) {
228: return;
229: }
230: Collection<InstanceContent> tempIcs = new HashSet<InstanceContent>();
231: tempIcs.addAll(ics);
232: if (icMap.get(view) != null) {
233: tempIcs.add(icMap.get(view));
234: }
235: for (InstanceContent ic : tempIcs) {
236: WeakSet<Node> nodesToRemove = nodesToRemoveMap.get(ic);
237: if (nodesToRemove == null) {
238: nodesToRemove = new WeakSet<Node>();
239: nodesToRemoveMap.put(ic, nodesToRemove);
240: }
241: ic
242: .set(Collections.EMPTY_SET,
243: new PropertiesNodeConverter());
244: nodesToRemove.clear();
245: }
246: if (components.isEmpty()) {
247: for (InstanceContent ic : tempIcs) {
248: ic.set(Collections.singleton(Node.EMPTY),
249: new PropertiesNodeConverter());
250: }
251: }
252: for (InstanceContent ic : tempIcs) {
253: Set<Node> nodesToRemove = nodesToRemoveMap.get(ic);
254: for (DesignComponent component : components) {
255: PropertiesNode node = nodesMap.get(component);
256: if (node == null) {
257: node = new PropertiesNode(view, component);
258: nodesMap.put(component, node);
259: }
260: nodesToRemove.add(node);
261: }
262: if (nodesToRemove != null && !nodesToRemove.isEmpty()) {
263: ic.set(nodesToRemove, new PropertiesNodeConverter());
264: }
265: }
266: }
267:
268: public synchronized Sheet getSheet(DesignComponent component) {
269: assert (component != null);
270: if (sheetMap == null) {
271: return null;
272: }
273: if (sheetMap.get(component) == null) {
274: sheetMap.put(component, createSheet(component));
275: }
276: return sheetMap.get(component);
277: }
278:
279: public Sheet createSheet(final DesignComponent component) {
280: final Sheet sheet = new Sheet();
281: component.getDocument().getTransactionManager().readAccess(
282: new Runnable() {
283: public void run() {
284: List<DesignPropertyDescriptor> designerPropertyDescriptors;
285: List<String> categories;
286: designerPropertyDescriptors = new ArrayList<DesignPropertyDescriptor>();
287: categories = new ArrayList<String>();
288: for (PropertiesPresenter propertiesPresenter : component
289: .getPresenters(PropertiesPresenter.class)) {
290: designerPropertyDescriptors
291: .addAll(propertiesPresenter
292: .getDesignPropertyDescriptors());
293: categories.addAll(propertiesPresenter
294: .getPropertiesCategories());
295: }
296: if (designerPropertyDescriptors != null) {
297: Collections.sort(
298: designerPropertyDescriptors,
299: compareByDisplayName);
300: }
301: createCategoriesSet(sheet, categories);
302: for (DesignPropertyDescriptor designerPropertyDescriptor : designerPropertyDescriptors) {
303: DefaultPropertySupport property;
304: DesignPropertyEditor propertyEditor = designerPropertyDescriptor
305: .getPropertyEditor();
306:
307: if (propertyEditor instanceof GroupPropertyEditor
308: && designerPropertyDescriptor
309: .getPropertyNames().size() == 0) {
310: throw new IllegalStateException(
311: "To use AdvancedPropertyEditorSupport you need to specific at least one propertyName"); //NOI18N
312: }
313: if (propertyEditor instanceof GroupPropertyEditor) {
314: property = new AdvancedPropertySupport(
315: designerPropertyDescriptor,
316: designerPropertyDescriptor
317: .getPropertyEditorType());
318: addPropertySupport(component, property);
319: } else if (designerPropertyDescriptor
320: .getPropertyNames().size() <= 1) {
321: property = new PrimitivePropertySupport(
322: designerPropertyDescriptor,
323: designerPropertyDescriptor
324: .getPropertyEditorType());
325: addPropertySupport(component, property);
326: } else {
327: throw new IllegalArgumentException();
328: }
329: if (propertyEditor != null
330: && propertyEditor.canEditAsText() != null) {
331: property.setValue("canEditAsText",
332: propertyEditor.canEditAsText()); //NOI18N
333: }
334: property.setValue("changeImmediate", false); // NOI18
335: sheet.get(
336: designerPropertyDescriptor
337: .getPropertyCategory())
338: .put(property);
339: }
340: }
341: });
342: return sheet;
343: }
344:
345: public void updateSheet(Collection<DesignComponent> components) {
346: if (components == null || components.isEmpty()) {
347: return;
348: }
349: for (DesignComponent component : components) {
350: Sheet sheet = sheetMap.get(component);
351: if (sheet == null) {
352: continue;
353: }
354: for (Node.PropertySet set : sheet.toArray()) {
355: for (Property property : set.getProperties()) {
356: if (!(property instanceof DefaultPropertySupport)) {
357: continue;
358: }
359: ((DefaultPropertySupport) property).update();
360: }
361: }
362: }
363: }
364:
365: private void addPropertySupport(DesignComponent component,
366: DefaultPropertySupport propertySupport) {
367: WeakSet<DefaultPropertySupport> propertySupports = propertySupportMap
368: .get(component);
369: if (propertySupports == null) {
370: propertySupports = new WeakSet<DefaultPropertySupport>();
371: propertySupportMap.put(component, propertySupports);
372: }
373: propertySupports.add(propertySupport);
374: }
375:
376: private void updatePropertyEditorsValues(
377: Collection<DesignComponent> components) {
378: if (components == null) {
379: return;
380: }
381: Collection<InstanceContent> tempIcs = new HashSet<InstanceContent>();
382: tempIcs.addAll(ics);
383: if (icMap.get(view) != null) {
384: tempIcs.add(icMap.get(view));
385: }
386: for (InstanceContent ic : tempIcs) {
387: WeakSet<Node> nodesToRemove = nodesToRemoveMap.get(ic);
388: if (nodesToRemove == null) {
389: continue;
390: }
391: for (Node node : nodesToRemove) {
392: PropertiesNode pn = (PropertiesNode) node;
393: pn.updateNode(getSheet(pn.getComponent()));
394: }
395: }
396: }
397:
398: private void repaintPropertiesWindow(
399: final Collection<DesignComponent> selectedComponents) {
400: SwingUtilities.invokeLater(new Runnable() {
401: public void run() {
402: if (view != null) {
403: PropertiesNodesManager.this
404: .changeLookup(selectedComponents);
405: PropertiesNodesManager.this
406: .updateSheet(selectedComponents);
407: }
408: }
409: });
410: }
411:
412: private synchronized void createCategoriesSet(Sheet sheet,
413: List<String> categories) {
414: for (String propertyCategory : categories) {
415: sheet.put(createPropertiesSet(propertyCategory));
416: }
417: }
418:
419: private synchronized Sheet.Set createPropertiesSet(
420: String categoryName) {
421: Sheet.Set setSheet = new Sheet.Set();
422: setSheet.setName(categoryName);
423: setSheet.setDisplayName(categoryName);
424: return setSheet;
425: }
426:
427: private class PropertiesNodeConverter implements
428: InstanceContent.Convertor {
429:
430: public Object convert(Object object) {
431: return object;
432: }
433:
434: public Class type(Object object) {
435: return object.getClass();
436: }
437:
438: public String id(Object object) {
439: return object.toString();
440: }
441:
442: public String displayName(Object object) {
443: return object.toString();
444: }
445: }
446: }
|