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.awt.CardLayout;
044: import java.awt.Component;
045: import java.awt.Font;
046: import java.awt.event.ActionEvent;
047: import java.util.Collections;
048: import java.util.List;
049: import javax.swing.JComponent;
050: import javax.swing.event.ListSelectionEvent;
051: import javax.swing.event.ListSelectionListener;
052: import java.awt.GridBagConstraints;
053: import java.awt.GridBagLayout;
054: import java.awt.Insets;
055: import java.awt.event.ActionListener;
056: import java.util.HashMap;
057: import java.util.HashSet;
058: import java.util.Map;
059: import java.util.Set;
060: import java.util.SortedSet;
061: import java.util.TreeSet;
062: import javax.swing.DefaultListCellRenderer;
063: import javax.swing.DefaultListModel;
064: import javax.swing.Icon;
065: import javax.swing.ImageIcon;
066: import javax.swing.JButton;
067: import javax.swing.JLabel;
068: import javax.swing.JList;
069: import javax.swing.JPanel;
070: import javax.swing.JRadioButton;
071: import javax.swing.JScrollPane;
072: import javax.swing.ListSelectionModel;
073: import org.netbeans.modules.vmd.api.model.DesignComponent;
074: import org.netbeans.modules.vmd.api.model.PropertyValue;
075: import org.netbeans.modules.vmd.midp.actions.GoToSourceSupport;
076: import org.netbeans.modules.vmd.midp.propertyeditors.api.resource.element.PropertyEditorResourceElement;
077: import org.netbeans.modules.vmd.midp.propertyeditors.api.resource.element.PropertyEditorResourceElement.DesignComponentWrapper;
078: import org.netbeans.modules.vmd.midp.propertyeditors.api.resource.element.PropertyEditorResourceElementEvent;
079: import org.netbeans.modules.vmd.midp.propertyeditors.api.resource.element.PropertyEditorResourceElementListener;
080: import org.openide.awt.Mnemonics;
081: import org.openide.util.NbBundle;
082: import org.openide.util.Utilities;
083:
084: /**
085: *
086: * @author Anton Chechel
087: */
088: class ResourceEditorPanel extends JPanel implements
089: PropertyEditorResourceElementListener, ListSelectionListener,
090: ActionListener, UserCodeAwareness {
091:
092: private static final String ACTION_ADD_RESOURCE = "addResource"; // NOI18N
093: private static final String ACTION_REMOVE_RESOURCE = "removeResource"; // NOI18N
094: private static final String COMPONENT_CARD = "componentCard"; // NOI18N
095: private static final String USER_CODE_CARD = "userCodeCard"; // NOI18N
096: private static long componentIDCounter = -10000L;
097:
098: private PropertyEditorResourceElement element;
099: private Map<String, DesignComponentWrapper> wrappersMap;
100: private Set<String> changedComponents;
101:
102: private String noneComponentAsText;
103: private JList componentsList;
104: private JRadioButton radioButton;
105: private Icon icon;
106: private JPanel ucPanel;
107: private CardLayout ucCardLayout;
108:
109: ResourceEditorPanel(PropertyEditorResourceElement element,
110: String noneComponentAsText, JRadioButton radioButton) {
111: if (noneComponentAsText == null
112: || noneComponentAsText.length() == 0) {
113: throw new IllegalArgumentException(
114: "Incorrect value of noneComponentAsText: "
115: + noneComponentAsText); // NOI18N
116: }
117:
118: if (element == null) {
119: throw new IllegalArgumentException(
120: "PropertyEditorResourceElement shouls not be null"); // NOI18N
121: }
122:
123: this .element = element;
124: this .noneComponentAsText = noneComponentAsText;
125: this .radioButton = radioButton;
126: changedComponents = new HashSet<String>();
127:
128: initComponents(element.getJComponent());
129: element.addPropertyEditorResourceElementListener(this );
130: }
131:
132: private void initComponents(JComponent component) {
133: setLayout(new GridBagLayout());
134: GridBagConstraints constraints = new GridBagConstraints();
135:
136: componentsList = new JList(new DefaultListModel());
137: componentsList
138: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
139: componentsList.addListSelectionListener(this );
140: componentsList.setCellRenderer(new ComponentsListRenderer());
141: // componentsList.setPreferredSize(new Dimension(120, 140));
142: JScrollPane scrollPane = new JScrollPane();
143: scrollPane.setViewportView(componentsList);
144: constraints.insets = new Insets(0, 0, 0, 12);
145: constraints.anchor = GridBagConstraints.NORTHWEST;
146: constraints.gridx = 0;
147: constraints.gridy = 0;
148: constraints.weightx = 0.0;
149: constraints.weighty = 1.0;
150: constraints.gridwidth = 2;
151: constraints.fill = GridBagConstraints.BOTH;
152: add(scrollPane, constraints);
153:
154: JButton addButton = new JButton();
155: Mnemonics.setLocalizedText(addButton, NbBundle.getMessage(
156: ResourceEditorPanel.class, "LBL_ADD_COMPONENT")); // NOI18N
157: addButton.getAccessibleContext().setAccessibleName(
158: NbBundle.getMessage(ResourceEditorPanel.class,
159: "ACSN_ADD_COMPONENT")); // NOI18N
160: addButton.getAccessibleContext().setAccessibleDescription(
161: NbBundle.getMessage(ResourceEditorPanel.class,
162: "ACSD_ADD_COMPONENT")); // NOI18N
163: addButton.setActionCommand(ACTION_ADD_RESOURCE);
164: addButton.addActionListener(this );
165: constraints.insets = new Insets(6, 0, 0, 12);
166: constraints.anchor = GridBagConstraints.NORTHWEST;
167: constraints.gridx = 0;
168: constraints.gridy = 1;
169: constraints.weightx = 0.0;
170: constraints.weighty = 0.0;
171: constraints.gridwidth = 1;
172: constraints.fill = GridBagConstraints.NONE;
173: add(addButton, constraints);
174:
175: JButton removeButton = new JButton();
176: Mnemonics.setLocalizedText(removeButton, NbBundle.getMessage(
177: ResourceEditorPanel.class, "LBL_REMOVE_COMPONENT")); // NOI18N
178: removeButton.getAccessibleContext().setAccessibleName(
179: NbBundle.getMessage(ResourceEditorPanel.class,
180: "ACSN_REMOVE_COMPONENT")); // NOI18N
181: removeButton.getAccessibleContext().setAccessibleDescription(
182: NbBundle.getMessage(ResourceEditorPanel.class,
183: "ACSD_REMOVE_COMPONENT")); // NOI18N
184: removeButton.setActionCommand(ACTION_REMOVE_RESOURCE);
185: removeButton.addActionListener(this );
186: constraints.insets = new Insets(6, 0, 0, 12);
187: constraints.anchor = GridBagConstraints.NORTHWEST;
188: constraints.gridx = 1;
189: constraints.gridy = 1;
190: constraints.weightx = 0.0;
191: constraints.weighty = 0.0;
192: constraints.fill = GridBagConstraints.NONE;
193: add(removeButton, constraints);
194:
195: constraints.insets = new Insets(0, 0, 0, 0);
196: constraints.anchor = GridBagConstraints.NORTHWEST;
197: constraints.gridx = 2;
198: constraints.gridy = 0;
199: constraints.weightx = 1.0;
200: constraints.weighty = 1.0;
201: constraints.fill = GridBagConstraints.BOTH;
202: add(createUCAwarePanel(component), constraints);
203:
204: icon = new ImageIcon(Utilities.loadImage(element.getIconPath()));
205: }
206:
207: private JComponent createUCAwarePanel(JComponent component) {
208: JPanel userCodeCardPanel = new UserCodeCardPanel(this );
209: ucCardLayout = new CardLayout();
210: ucPanel = new JPanel(ucCardLayout);
211: ucPanel.add(component, COMPONENT_CARD);
212: ucPanel.add(userCodeCardPanel, USER_CODE_CARD);
213: return ucPanel;
214: }
215:
216: private void setUserCode(boolean isUserCode) {
217: ucCardLayout.show(ucPanel, isUserCode ? USER_CODE_CARD
218: : COMPONENT_CARD);
219: }
220:
221: private boolean isUserCodeInside(DesignComponentWrapper wrapper) {
222: if (wrapper == null) {
223: return false;
224: }
225:
226: final DesignComponent component = wrapper.getComponent();
227: if (component == null) {
228: return false;
229: }
230:
231: final List<String> propertyNames = element
232: .getPropertyValueNames();
233: final boolean[] isUC = new boolean[1];
234: component.getDocument().getTransactionManager().readAccess(
235: new Runnable() {
236:
237: public void run() {
238: for (String propertyName : propertyNames) {
239: PropertyValue pv = component
240: .readProperty(propertyName);
241: if (PropertyValue.Kind.USERCODE.equals(pv
242: .getKind())) {
243: isUC[0] = true;
244: break;
245: }
246: }
247: }
248: });
249: return isUC[0];
250: }
251:
252: void update(Map<String, DesignComponent> componentsMap,
253: String selectedComponentName) {
254: this .wrappersMap = new HashMap<String, DesignComponentWrapper>(
255: componentsMap.size());
256: for (String key : componentsMap.keySet()) {
257: DesignComponent _component = componentsMap.get(key);
258: wrappersMap
259: .put(key, new DesignComponentWrapper(_component));
260: }
261: changedComponents.clear();
262: sortAndSelect(selectedComponentName);
263: }
264:
265: // issue# 112658
266: private void sortAndSelect(String selectedComponentName) {
267: DefaultListModel listModel = (DefaultListModel) componentsList
268: .getModel();
269: listModel.removeAllElements();
270: listModel.addElement(noneComponentAsText);
271:
272: SortedSet<String> set = new TreeSet<String>();
273: for (String componentName : wrappersMap.keySet()) {
274: set.add(componentName);
275: }
276: for (String str : set) {
277: listModel.addElement(str);
278: }
279:
280: selectComponent(selectedComponentName);
281: }
282:
283: private void selectComponent(String selectedComponentName) {
284: // select value in the components list
285: componentsList.setSelectedValue(selectedComponentName, false);
286:
287: // select according values in the element component
288: if (noneComponentAsText.equals(selectedComponentName)) {
289: element.setDesignComponentWrapper(null);
290: } else {
291: element.setDesignComponentWrapper(wrappersMap
292: .get(selectedComponentName));
293: }
294: }
295:
296: public void valueChanged(ListSelectionEvent e) {
297: Object selectedName = getSelectedComponentName();
298: if (selectedName == null
299: || noneComponentAsText.equals(selectedName)) {
300: element.setDesignComponentWrapper(null);
301: setUserCode(false);
302: } else {
303: DesignComponentWrapper wrapper = wrappersMap
304: .get(selectedName);
305: element.setDesignComponentWrapper(wrapper);
306: setUserCode(isUserCodeInside(wrapper));
307: }
308: radioButton.setSelected(true);
309: }
310:
311: public void elementChanged(PropertyEditorResourceElementEvent event) {
312: long componentID = event.getComponentID();
313: String propertyName = event.getPropertyName();
314: PropertyValue propertyValue = event.getPropertyValue();
315:
316: DesignComponentWrapper wrapper = null;
317: for (String key : wrappersMap.keySet()) {
318: wrapper = wrappersMap.get(key);
319: if (wrapper.getComponentID() == componentID) {
320: wrapper.setChangeRecord(propertyName, propertyValue);
321: changedComponents.add(key);
322:
323: // UI stuff
324: // need to refresh list cell renderer
325: componentsList.invalidate();
326: componentsList.validate();
327: componentsList.repaint();
328: break;
329: }
330: }
331: }
332:
333: public void actionPerformed(ActionEvent e) {
334: Object selectedName = getSelectedComponentName();
335: DefaultListModel model = (DefaultListModel) componentsList
336: .getModel();
337: if (ACTION_ADD_RESOURCE.equals(e.getActionCommand())) {
338: String name = getNewComponentName();
339: DesignComponentWrapper virtualComponent = new DesignComponentWrapper(
340: componentIDCounter++, element.getTypeID());
341: wrappersMap.put(name, virtualComponent);
342: model.addElement(name);
343: sortAndSelect(name);
344: changedComponents.add(name);
345: } else if (ACTION_REMOVE_RESOURCE.equals(e.getActionCommand())) {
346: if (selectedName != null
347: && !noneComponentAsText.equals(selectedName)) {
348: wrappersMap.get(selectedName).deleteComponent();
349: int index = model.indexOf(selectedName);
350: model.removeElement(selectedName);
351: componentsList.setSelectedIndex(index - 1);
352: }
353: }
354: }
355:
356: private String getNewComponentName() {
357: String base = element.getResourceNameSuggestion();
358: Set<String> existingKeys = wrappersMap.keySet();
359: Set<String> toBeDeleted = new HashSet<String>();
360: for (String key : wrappersMap.keySet()) {
361: if (wrappersMap.get(key).isDeleted()) {
362: toBeDeleted.add(key);
363: }
364: }
365: for (String key : toBeDeleted) {
366: wrappersMap.remove(key);
367: }
368:
369: String name;
370: for (int i = 1;; i++) {
371: name = base + i;
372: if (!existingKeys.contains(name)) {
373: break;
374: }
375: }
376: return name;
377: }
378:
379: private Object getSelectedComponentName() {
380: return componentsList.getSelectedValue();
381: }
382:
383: String getTextForPropertyValue() {
384: String str = (String) componentsList.getSelectedValue();
385: return str != null ? str : ""; // NOI18N
386: }
387:
388: boolean wasAnyDesignComponentChanged() {
389: for (String key : wrappersMap.keySet()) {
390: if (wrappersMap.get(key).hasChanges()) {
391: return true;
392: }
393: }
394: return false;
395: }
396:
397: Map<String, DesignComponentWrapper> getWrappersMap() {
398: return Collections.unmodifiableMap(wrappersMap);
399: }
400:
401: // do not update state of editor is shown
402: boolean needsUpdate() {
403: // return !isShowing();
404: return true;
405: }
406:
407: public void goToSource() {
408: DesignComponentWrapper wrapper = wrappersMap
409: .get(getSelectedComponentName());
410: if (wrapper != null && wrapper.getComponent() != null) {
411: GoToSourceSupport.goToSourceOfComponent(wrapper
412: .getComponent());
413: }
414: }
415:
416: public void resetUserCode() {
417: DesignComponentWrapper wrapper = wrappersMap
418: .get(getSelectedComponentName());
419: if (wrapper != null && wrapper.getComponent() != null) {
420: final DesignComponent component = wrapper.getComponent();
421: component.getDocument().getTransactionManager()
422: .writeAccess(new Runnable() {
423:
424: public void run() {
425: List<String> propertyNames = element
426: .getPropertyValueNames();
427: for (String propertyName : propertyNames) {
428: PropertyValue pv = component
429: .readProperty(propertyName);
430: if (PropertyValue.Kind.USERCODE
431: .equals(pv.getKind())) {
432: component
433: .writeProperty(
434: propertyName,
435: component
436: .getComponentDescriptor()
437: .getPropertyDescriptor(
438: propertyName)
439: .getDefaultValue());
440: }
441: }
442: }
443: });
444: setUserCode(isUserCodeInside(wrapper));
445: }
446: }
447:
448: private class ComponentsListRenderer extends
449: DefaultListCellRenderer {
450:
451: private Font changedFont;
452: private Font defaultFont;
453:
454: @Override
455: public Component getListCellRendererComponent(JList list,
456: Object value, int index, boolean isSelected,
457: boolean cellHasFocus) {
458: JLabel renderer = (JLabel) super
459: .getListCellRendererComponent(list, value, index,
460: isSelected, cellHasFocus);
461: if (defaultFont == null) {
462: defaultFont = renderer.getFont();
463: changedFont = defaultFont.deriveFont(Font.BOLD);
464: }
465:
466: if (!noneComponentAsText.equals(value) && icon != null) {
467: renderer.setIcon(icon);
468: }
469:
470: renderer
471: .setFont(changedComponents.contains(value) ? changedFont
472: : defaultFont);
473: return renderer;
474: }
475: }
476: }
|