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.beaninfo.editors;
043:
044: import java.awt.Component;
045: import java.awt.Font;
046: import java.awt.GridBagConstraints;
047: import java.awt.GridBagLayout;
048: import java.awt.Insets;
049: import java.awt.event.ActionEvent;
050: import java.awt.event.ActionListener;
051: import java.beans.IntrospectionException;
052: import java.beans.Introspector;
053: import java.beans.PropertyEditorSupport;
054: import java.text.MessageFormat;
055: import java.util.ArrayList;
056: import java.util.Collection;
057: import java.util.Date;
058: import java.util.Iterator;
059: import javax.swing.ButtonGroup;
060: import javax.swing.JLabel;
061: import javax.swing.JPanel;
062: import javax.swing.JRadioButton;
063: import org.netbeans.core.UIExceptions;
064: import org.openide.explorer.propertysheet.ExPropertyEditor;
065: import org.openide.explorer.propertysheet.PropertyEnv;
066: import org.openide.util.NbBundle;
067: import org.openide.util.Lookup;
068: import org.openide.util.Utilities;
069:
070: /**
071: * Defines editor for choosing of any object using lookup.
072: *
073: * @author Jaroslav Tulach
074: */
075: public final class ObjectEditor extends PropertyEditorSupport implements
076: ExPropertyEditor {
077: /** Name of the custom property that can be passed in PropertyEnv.
078: * Should contain superclass that is allowed to be
079: */
080: private static final String PROP_SUPERCLASS = "superClass"; // NOI18N
081: /** Name of the custom property that can be passed in PropertyEnv.
082: * Either Boolean.TRUE or a String, in such case the string represents
083: * human readable name of the value.
084: */
085: private static final String PROP_NULL = "nullValue"; // NOI18N
086: /** Name of the custom property that can be passed in PropertyEnv.
087: * A lookup to use to query for results.
088: */
089: private static final String PROP_LOOKUP = "lookup"; // NOI18N
090:
091: /** custom editor */
092: private ObjectPanel customEditor;
093:
094: /** super class to search for */
095: private Lookup.Template<Object> template;
096:
097: /** null or name to use for null value */
098: private String nullValue;
099:
100: /** a special lookup to use or null */
101: private Lookup lookup;
102:
103: /** Creates new ObjectEditor */
104: public ObjectEditor() {
105: }
106:
107: /**
108: * This method is called by the IDE to pass
109: * the environment to the property editor.
110: */
111: public synchronized void attachEnv(PropertyEnv env) {
112: Object obj = env.getFeatureDescriptor().getValue(
113: PROP_SUPERCLASS);
114: if (obj instanceof Class) {
115: @SuppressWarnings("unchecked")
116: Class<Object> clz = (Class<Object>) obj;
117: template = new Lookup.Template<Object>(clz);
118: } else {
119: template = null;
120: }
121:
122: obj = env.getFeatureDescriptor().getValue(PROP_NULL);
123: if (Boolean.TRUE.equals(obj)) {
124: nullValue = NbBundle.getMessage(ObjectEditor.class,
125: "CTL_NullValue");
126: } else {
127: if (obj instanceof String) {
128: nullValue = (String) obj;
129: } else {
130: nullValue = null;
131: }
132: }
133:
134: obj = env.getFeatureDescriptor().getValue(PROP_LOOKUP);
135: lookup = obj instanceof Lookup ? (Lookup) obj : null;
136: //Don't allow editing in the case only one item and tags are null
137: if (getTags() == null || getTags().length <= 1) {
138: env.getFeatureDescriptor().setValue("canEditAsText",
139: Boolean.FALSE); //NOI18N
140: }
141: }
142:
143: /** A lookup to work on.
144: * @return a lookup.
145: */
146: protected Lookup lookup() {
147: Lookup l = lookup;
148: return l == null ? Lookup.getDefault() : l;
149: }
150:
151: /** A template to use.
152: */
153: protected Lookup.Template<Object> template() {
154: if (template == null) {
155: template = new Lookup.Template<Object>(Object.class);
156: }
157:
158: return template;
159: }
160:
161: @Override
162: public String getAsText() {
163: Object value = getValue();
164: if (value == null) {
165: return nullValue == null ? NbBundle.getMessage(
166: ObjectEditor.class, "CTL_NullValue") : nullValue;
167: }
168:
169: Lookup.Template<Object> t = new Lookup.Template<Object>(
170: template().getType(), template().getId(), value // instance to search for
171: );
172: Lookup.Item item = lookup().lookupItem(t);
173:
174: if (item == null) {
175: return NbBundle.getMessage(ObjectEditor.class,
176: "CTL_NullItem");
177: }
178:
179: return item.getDisplayName();
180: }
181:
182: /** Searches between items whether there is one with the same display name.
183: * @param str item name
184: */
185: @Override
186: public void setAsText(java.lang.String str)
187: throws java.lang.IllegalArgumentException {
188: if (nullValue != null && nullValue.equals(str)) {
189: setValue(null);
190: return;
191: }
192:
193: Collection allItems = lookup().lookup(template()).allItems();
194:
195: Iterator it = allItems.iterator();
196: while (it.hasNext()) {
197: Lookup.Item item = (Lookup.Item) it.next();
198:
199: if (item.getDisplayName().equals(str)) {
200: setValue(item.getInstance());
201: firePropertyChange();
202: return;
203: }
204: }
205: IllegalArgumentException iae = new IllegalArgumentException(str);
206: String msg = MessageFormat.format(NbBundle.getMessage(
207: ObjectEditor.class, "FMT_EXC_GENERIC_BAD_VALUE"), //NOI18N
208: new Object[] { str });
209: UIExceptions.annotateUser(iae, str, msg, null, new Date());
210: throw iae;
211: }
212:
213: /** List of all display names for items.
214: * @return array of strings
215: */
216: @Override
217: public java.lang.String[] getTags() {
218: Collection<? extends Lookup.Item<Object>> allItems = lookup()
219: .lookup(template()).allItems();
220: if (allItems.size() <= 1) {
221: return null;
222: }
223:
224: ArrayList<String> list = new ArrayList<String>(
225: allItems.size() + 1);
226: if (nullValue != null) {
227: list.add(nullValue);
228: }
229:
230: for (Lookup.Item<Object> item : allItems) {
231: list.add(item.getDisplayName());
232: }
233:
234: String[] retValue = new String[list.size()];
235: list.toArray(retValue);
236: return retValue;
237: }
238:
239: /** Yes we have custom editor.
240: */
241: @Override
242: public boolean supportsCustomEditor() {
243: //Don't allow custom editor if there will be nothing to show
244: return getTags() != null && getTags().length > 1;
245: }
246:
247: @Override
248: public synchronized Component getCustomEditor() {
249: if (!supportsCustomEditor()) {
250: return null;
251: }
252: if (customEditor != null) {
253: return customEditor;
254: }
255: Lookup.Result<Object> contents = lookup().lookup(template());
256: ObjectPanel panel = new ObjectPanel(contents);
257: return customEditor = panel;
258: }
259:
260: private class ObjectPanel extends JPanel implements ActionListener {
261: static final long serialVersionUID = 1L;
262:
263: public ObjectPanel(Lookup.Result<Object> res) {
264: getAccessibleContext().setAccessibleName(
265: NbBundle.getMessage(ObjectEditor.class,
266: "ACSN_ObjectTree")); //NOI18N
267: getAccessibleContext().setAccessibleDescription(
268: NbBundle.getMessage(ObjectEditor.class,
269: "ACSD_ObjectTree")); //NOI18N
270:
271: setLayout(new GridBagLayout());
272: GridBagConstraints gbc = new GridBagConstraints();
273: int row = 0;
274: ButtonGroup bg = new ButtonGroup();
275: Font bold;
276: Font plain;
277: if (Utilities.isMac()) {
278: // don't use deriveFont() - see #49973 for details
279: bold = new Font(getFont().getName(), Font.BOLD,
280: getFont().getSize());
281: //For default metal L&F where labels are by default bold
282: // don't use deriveFont() - see #49973 for details
283: plain = new Font(getFont().getName(), Font.PLAIN,
284: getFont().getSize());
285: } else {
286: bold = getFont().deriveFont(Font.BOLD);
287: plain = getFont().deriveFont(Font.PLAIN);
288: }
289:
290: Collection<? extends Lookup.Item<Object>> c = res
291: .allItems();
292: Lookup.Item[] items = new Lookup.Item[c.size()];
293: items = c.toArray(items);
294:
295: int BASE_LEFT_INSET = 7;
296: for (int i = 0; i < items.length; i++) {
297: JRadioButton rb = new ItemRadioButton(items[i], bold);
298: Object inst = items[i].getInstance();
299: if (inst != null && inst.equals(getValue())) {
300: rb.setSelected(true);
301: }
302: rb.addActionListener(this );
303: bg.add(rb);
304: String description = getDescription(items[i]);
305:
306: gbc.gridx = 0;
307: gbc.gridy = row;
308: gbc.insets = new Insets(i == 0 ? 7 : 0,
309: BASE_LEFT_INSET, description != null ? 1
310: : i == items.length - 1 ? 7 : 4,
311: BASE_LEFT_INSET);
312: gbc.fill = GridBagConstraints.HORIZONTAL;
313: add(rb, gbc);
314: row++;
315: if (description != null) {
316: JLabel lbl = new JLabel(description);
317: lbl.setLabelFor(rb);
318: lbl.setFont(plain);
319: int left = rb.getIcon() != null ? rb.getIcon()
320: .getIconWidth() : 20;
321: gbc.insets = new Insets(0, BASE_LEFT_INSET + left,
322: 4, BASE_LEFT_INSET + left);
323: gbc.gridx = 0;
324: gbc.gridy = row;
325: add(lbl, gbc);
326: row++;
327: }
328: }
329: }
330:
331: private String getDescription(Lookup.Item item) {
332: String id = item.getId();
333: String result = null;
334: try {
335: result = Introspector.getBeanInfo(
336: item.getInstance().getClass())
337: .getBeanDescriptor().getShortDescription();
338: } catch (IntrospectionException ie) {
339: //do nothing
340: }
341: String toCheck = item.getInstance().getClass().getName();
342: toCheck = toCheck.lastIndexOf('.') != -1 ? toCheck
343: .substring(toCheck.lastIndexOf('.') + 1) : toCheck; //NOI18N
344: if (toCheck.equals(result)) {
345: result = null;
346: }
347: return result;
348: }
349:
350: public void actionPerformed(ActionEvent ae) {
351: Lookup.Item item = ((ItemRadioButton) ae.getSource()).item;
352: Object o = item.getInstance();
353: setValue(item.getInstance());
354: ObjectEditor.this .firePropertyChange();
355: }
356: }
357:
358: private static class ItemRadioButton extends JRadioButton {
359: static final long serialVersionUID = 3L;
360:
361: Lookup.Item item;
362:
363: public ItemRadioButton(Lookup.Item item, Font font) {
364: this.item = item;
365: setName(item.getId());
366: setText(item.getDisplayName());
367: setFont(font);
368: getAccessibleContext().setAccessibleName(getName());
369: getAccessibleContext().setAccessibleDescription(getText());
370: }
371: }
372: }
|