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.beans.beaninfo;
043:
044: import java.awt.*;
045: import java.awt.event.*;
046: import java.beans.*;
047: import java.net.URL;
048:
049: import javax.swing.*;
050: import javax.swing.border.*;
051:
052: import org.openide.*;
053: import org.openide.loaders.*;
054: import org.openide.nodes.*;
055: import org.openide.util.HelpCtx;
056: import org.openide.explorer.propertysheet.editors.EnhancedCustomPropertyEditor;
057: import java.text.MessageFormat;
058: import java.util.ArrayList;
059: import java.util.Collections;
060: import java.util.Iterator;
061: import java.util.ListIterator;
062: import org.netbeans.api.java.classpath.ClassPath;
063: import org.netbeans.api.java.queries.SourceForBinaryQuery;
064: import org.openide.explorer.ExplorerManager;
065: import org.openide.explorer.view.BeanTreeView;
066: import org.openide.filesystems.FileObject;
067: import org.openide.filesystems.FileUtil;
068:
069: /**
070: * PropertyEditor for Icons. Depends on existing DataObject for images.
071: * Images must be represented by some DataObject which returns itselv
072: * as cookie, and has image file as a primary file. File extensions
073: * for images is specified in isImage method.
074: *
075: * @author Jan Jancura
076: */
077: class BiIconEditor extends PropertyEditorSupport {
078:
079: private static final String BEAN_ICONEDITOR_HELP = "beans.icon"; // NOI18N
080:
081: private FileObject sourceFileObject;
082:
083: /** Standard variable for localization. */
084: static java.util.ResourceBundle bundle = org.openide.util.NbBundle
085: .getBundle(BiIconEditor.class);
086:
087: public static boolean isImage(String s) {
088: s = s.toLowerCase();
089: return s.endsWith(".jpg") || s.endsWith(".gif") || // NOI18N
090: s.endsWith(".jpeg") || s.endsWith(".jpe") || // NOI18N
091: s.equals("jpg") || s.equals("gif") || // NOI18N
092: s.equals("jpeg") || s.equals("jpe"); // NOI18N
093: }
094:
095: // variables .................................................................................
096:
097: //private Icon icon;
098:
099: // init .......................................................................................
100:
101: public BiIconEditor(FileObject sourceFileObject) {
102: this .sourceFileObject = sourceFileObject;
103: }
104:
105: // Special access methods......................................................................
106:
107: /** @return the name of image's source - depending on the type it can be a URL, file name or
108: * resource path to the image on classpath */
109: public String getSourceName() {
110: if (getValue() instanceof BiImageIcon)
111: return ((BiImageIcon) getValue()).getName();
112: else
113: return null;
114: }
115:
116: /**
117: * @return The property value as a human editable string.
118: * <p> Returns null if the value can't be expressed as an editable string.
119: * <p> If a non-null value is returned, then the PropertyEditor should
120: * be prepared to parse that string back in setAsText().
121: */
122: public String getAsText() {
123: Object val = getValue();
124: if (val == null)
125: return "null"; // NOI18N
126:
127: if (val instanceof BiImageIcon) {
128: BiImageIcon ii = (BiImageIcon) val;
129: return ii.getName(); // NOI18N
130: }
131: return null;
132: }
133:
134: /**
135: * Set the property value by parsing a given String. May raise
136: * java.lang.IllegalArgumentException if either the String is
137: * badly formatted or if this kind of property can't be expressed
138: * as text.
139: * @param text The string to be parsed.
140: */
141: public void setAsText(String string)
142: throws IllegalArgumentException {
143: try {
144: setValue(iconFromText(string));
145: } catch (IllegalArgumentException e) {
146: // User inserted incorrect path either report or
147: // do nothing
148: // For now choosing doing nothing
149: }
150: }
151:
152: private BiImageIcon iconFromText(String string)
153: throws IllegalArgumentException {
154: BiImageIcon ii;
155: try {
156: if (string.length() == 0 || string.equals("null")) { // NOI18N
157: ii = null;
158: } else {
159: ClassPath cp = ClassPath.getClassPath(sourceFileObject,
160: ClassPath.SOURCE);
161:
162: URL url = cp.findResource(string.substring(1)).getURL();
163: ii = new BiImageIcon(url, string);
164: }
165: } catch (Throwable e) {
166: if (Boolean.getBoolean("netbeans.debug.exceptions"))
167: e.printStackTrace(); // NOI18N
168: throw new IllegalArgumentException(e.toString());
169: }
170: return ii;
171: }
172:
173: /**
174: * @return True if the class will honor the paintValue method.
175: */
176: public boolean isPaintable() {
177: return false;
178: }
179:
180: /**
181: * @return True if the propertyEditor can provide a custom editor.
182: */
183: public boolean supportsCustomEditor() {
184: return true;
185: }
186:
187: /**
188: * A PropertyEditor may choose to make available a full custom Component
189: * that edits its property value. It is the responsibility of the
190: * PropertyEditor to hook itself up to its editor Component itself and
191: * to report property value changes by firing a PropertyChange event.
192: * <P>
193: * The higher-level code that calls getCustomEditor may either embed
194: * the Component in some larger property sheet, or it may put it in
195: * its own individual dialog, or ...
196: *
197: * @return A java.awt.Component that will allow a human to directly
198: * edit the current property value. May be null if this is
199: * not supported.
200: */
201: public java.awt.Component getCustomEditor() {
202: return new IconPanel();
203: }
204:
205: public static class BiImageIcon extends ImageIcon /* implements Externalizable */{
206: /** generated Serialized Version UID */
207: //static final long serialVersionUID = 7018807466471349466L;
208: private String name;
209:
210: public BiImageIcon() {
211: }
212:
213: BiImageIcon(URL url, String name) {
214: super (url);
215: this .name = name;
216: }
217:
218: BiImageIcon(String file, String name) {
219: super (file);
220: this .name = name;
221: }
222:
223: String getName() {
224: return name;
225: }
226:
227: /*
228: public void writeExternal(ObjectOutput oo) throws IOException {
229: oo.writeObject(name);
230: }
231:
232: public void readExternal(ObjectInput in)
233: throws IOException, ClassNotFoundException {
234: name = (String) in.readObject();
235: ImageIcon ii = null;
236: ii = new ImageIcon(Repository.getDefault().findResource(name).getURL());
237: setImage(ii.getImage());
238: }
239: */
240: }
241:
242: class IconPanel extends JPanel implements
243: EnhancedCustomPropertyEditor {
244: JRadioButton rbClasspath, rbNoPicture;
245: JTextField tfName;
246: JButton bSelect;
247: JScrollPane spImage;
248:
249: static final long serialVersionUID = -6904264999063788703L;
250:
251: IconPanel() {
252: // visual components .............................................
253:
254: JLabel lab;
255: setLayout(new BorderLayout(6, 6));
256: setBorder(new EmptyBorder(6, 6, 6, 6));
257: getAccessibleContext().setAccessibleName(
258: bundle.getString("ACS_IconPanelA11yName")); // NOI18N
259: getAccessibleContext().setAccessibleDescription(
260: bundle.getString("ACS_IconPanelA11yDesc")); // NOI18N
261: JPanel p = new JPanel(new BorderLayout(3, 3));
262: JPanel p1 = new JPanel(new BorderLayout());
263: p1.setBorder(new TitledBorder(new EtchedBorder(), bundle
264: .getString("CTL_ImageSourceType")));
265: JPanel p2 = new JPanel();
266: p2.setBorder(new EmptyBorder(0, 3, 0, 3));
267: GridBagLayout l = new GridBagLayout();
268: GridBagConstraints c = new GridBagConstraints();
269: p2.setLayout(l);
270: c.anchor = GridBagConstraints.WEST;
271:
272: p2.add(rbClasspath = new JRadioButton(bundle
273: .getString("CTL_Classpath")));
274: rbClasspath.setToolTipText(bundle
275: .getString("ACS_ClasspathA11yDesc"));
276: rbClasspath.setMnemonic(bundle.getString(
277: "CTL_Classpath_Mnemonic").charAt(0));
278: c.gridwidth = 1;
279: l.setConstraints(rbClasspath, c);
280:
281: p2.add(lab = new JLabel(bundle
282: .getString("CTL_ClasspathExample")));
283: lab.getAccessibleContext().setAccessibleDescription(
284: bundle.getString("ACS_ClasspathExampleA11yDesc"));
285: c.gridwidth = GridBagConstraints.REMAINDER;
286: l.setConstraints(lab, c);
287:
288: p2.add(rbNoPicture = new JRadioButton(bundle
289: .getString("CTL_NoPicture")));
290: rbNoPicture.setToolTipText(bundle
291: .getString("ACS_NoPictureA11yDesc"));
292: rbNoPicture.setMnemonic(bundle.getString(
293: "CTL_NoPicture_Mnemonic").charAt(0));
294: c.gridwidth = 1;
295: l.setConstraints(rbNoPicture, c);
296:
297: p2.add(lab = new JLabel(bundle.getString("CTL_Null")));
298: lab.getAccessibleContext().setAccessibleDescription(
299: bundle.getString("ACS_NullA11yDesc"));
300: c.gridwidth = GridBagConstraints.REMAINDER;
301: l.setConstraints(lab, c);
302:
303: ButtonGroup bg = new ButtonGroup();
304: bg.add(rbClasspath);
305: bg.add(rbNoPicture);
306: rbClasspath.setSelected(true);
307: p1.add(p2, "West"); // NOI18N
308: p.add(p1, "North"); // NOI18N
309: p1 = new JPanel(new BorderLayout(6, 6));
310: JLabel nameLabel = new JLabel(bundle
311: .getString("CTL_ImageSourceName"));
312: nameLabel.getAccessibleContext().setAccessibleDescription(
313: bundle.getString("ACS_ImageSourceNameA11yDesc"));
314: nameLabel.setDisplayedMnemonic(bundle.getString(
315: "CTL_ImageSourceName_Mnemonic").charAt(0));
316: p1.add(nameLabel, "West"); // NOI18N
317: p1.add(tfName = new JTextField(), "Center"); // NOI18N
318: nameLabel.setLabelFor(tfName);
319: tfName
320: .getAccessibleContext()
321: .setAccessibleName(
322: bundle
323: .getString("ACS_ImageSourceNameTextFieldA11yName"));
324: tfName.setToolTipText(bundle
325: .getString("ACS_ImageSourceNameTextFieldA11yDesc"));
326: p1.add(bSelect = new JButton("..."), "East"); // NOI18N
327: bSelect
328: .getAccessibleContext()
329: .setAccessibleName(
330: bundle
331: .getString("ACS_ImageSourceNameBrowseButtonA11yName"));
332: bSelect
333: .setToolTipText(bundle
334: .getString("ACS_ImageSourceNameBrowseButtonA11yDesc"));
335: bSelect.setEnabled(false);
336: p.add(p1, "South"); // NOI18N
337: add(p, "North"); // NOI18N
338: spImage = new JScrollPane() {
339: public Dimension getPreferredSize() {
340: return new Dimension(60, 60);
341: }
342: };
343: add(spImage, "Center"); // NOI18N
344:
345: // listeners .................................................
346:
347: tfName.addActionListener(new ActionListener() {
348: public void actionPerformed(ActionEvent e) {
349: setValue();
350: }
351: });
352: rbClasspath.addActionListener(new ActionListener() {
353: public void actionPerformed(ActionEvent e) {
354: bSelect.setEnabled(true);
355: tfName.setEnabled(true);
356: setValue();
357: }
358: });
359: rbNoPicture.addActionListener(new ActionListener() {
360: public void actionPerformed(ActionEvent e) {
361: bSelect.setEnabled(false);
362: tfName.setEnabled(false);
363:
364: BiIconEditor.this .setValue(null);
365: updateIcon();
366: }
367: });
368: bSelect.addActionListener(new ActionListener() {
369: public void actionPerformed(ActionEvent e) {
370: if (rbClasspath.isSelected()) {
371: String name = selectResource();
372: if (name != null) {
373: tfName.setText("/" + name); // NOI18N
374: setValue();
375: }
376: }
377: }
378: });
379: // initialization ......................................
380:
381: updateIcon();
382:
383: HelpCtx.setHelpIDString(this , BEAN_ICONEDITOR_HELP);
384:
385: Icon i = (Icon) getValue();
386: if (i == null) {
387: rbNoPicture.setSelected(true);
388: bSelect.setEnabled(false);
389: tfName.setEnabled(false);
390: return;
391: }
392: if (!(i instanceof BiImageIcon))
393: return;
394:
395: rbClasspath.setSelected(true);
396: bSelect.setEnabled(true);
397: tfName.setText(((BiImageIcon) i).getName());
398: }
399:
400: void updateIcon() {
401: Icon i = (Icon) getValue();
402: spImage.setViewportView((i == null) ? new JLabel()
403: : new JLabel(i));
404: // repaint();
405: validate();
406: }
407:
408: void setValue() {
409: String val = tfName.getText();
410: val.trim();
411: if ("".equals(val)) { // NOI18N
412: BiIconEditor.this .setValue(null);
413: return;
414: }
415:
416: try {
417: BiIconEditor.this .setValue(iconFromText(val));
418: } catch (IllegalArgumentException ee) {
419: // Reporting the exception is maybe too much let's do nothing
420: // instead
421: // org.openide.ErrorManager.getDefault().notify(org.openide.ErrorManager.INFORMATIONAL, ee);
422: }
423: updateIcon();
424: }
425:
426: public Object getPropertyValue() throws IllegalStateException {
427: BiImageIcon ii = null;
428: String s = tfName.getText().trim();
429: try {
430: if (rbClasspath.isSelected() && s.length() != 0) {
431: ClassPath cp = ClassPath.getClassPath(
432: sourceFileObject, ClassPath.SOURCE);
433: FileObject f = cp.findResource(s.substring(1));
434: try {
435: ii = new BiImageIcon(f.getURL(), s);
436: } catch (java.lang.Throwable t) {
437: MessageFormat message = new MessageFormat(
438: bundle.getString("CTL_Icon_not_exists")); //NOI18N
439: Object[] form = { s };//CTL_Icon_not_exists=Image class path for {0} is not valid
440: DialogDisplayer
441: .getDefault()
442: .notify(
443: new NotifyDescriptor.Message(
444: message.format(form),
445: NotifyDescriptor.ERROR_MESSAGE));
446: }
447: }
448: } catch (Exception e) {
449: if (Boolean.getBoolean("netbeans.debug.exceptions"))
450: e.printStackTrace(); // NOI18N
451: throw new IllegalStateException(e.toString());
452: }
453: BiIconEditor.this .setValue(ii);
454: return ii;
455: }
456:
457: private java.util.List getRoots(ClassPath cp) {
458: ArrayList list = new ArrayList(cp.entries().size());
459: Iterator eit = cp.entries().iterator();
460: while (eit.hasNext()) {
461: ClassPath.Entry e = (ClassPath.Entry) eit.next();
462:
463: // try to map it to sources
464: URL url = e.getURL();
465: SourceForBinaryQuery.Result r = SourceForBinaryQuery
466: .findSourceRoots(url);
467: FileObject[] fos = r.getRoots();
468: if (fos.length > 0) {
469: for (int i = 0; i < fos.length; i++)
470: list.add(fos[i]);
471: } else {
472: if (e.getRoot() != null)
473: list.add(e.getRoot()); // add the class-path location directly
474: }
475: }
476:
477: return list;
478: }
479:
480: private String rootDisplayName(FileObject fo) {
481: return FileUtil.getFileDisplayName(fo);
482: }
483:
484: /**
485: * Obtains icon resource from the user.
486: *
487: * @returns name of the selected resource or <code>null</code>.
488: */
489: private String selectResource() {
490: ClassPath executionClassPath = ClassPath.getClassPath(
491: sourceFileObject, ClassPath.EXECUTE);
492: java.util.List roots = (executionClassPath == null) ? Collections.EMPTY_LIST
493: : getRoots(executionClassPath);
494: Node nodes[] = new Node[roots.size()];
495: int selRoot = -1;
496: try {
497: ListIterator iter = roots.listIterator();
498: while (iter.hasNext()) {
499: FileObject root = (FileObject) iter.next();
500: DataObject dob = DataObject.find(root);
501: final String displayName = rootDisplayName(root);
502: nodes[iter.previousIndex()] = new RootNode(dob
503: .getNodeDelegate(), displayName);
504: }
505: } catch (DataObjectNotFoundException donfex) {
506: ErrorManager.getDefault().notify(
507: ErrorManager.EXCEPTION, donfex);
508: return null;
509: }
510: Children children = new Children.Array();
511: children.add(nodes);
512: final AbstractNode root = new AbstractNode(children);
513: root
514: .setIconBaseWithExtension("org/netbeans/modules/beans/resources/iconResourceRoot.gif"); // NOI18N
515: root.setDisplayName(bundle.getString("CTL_ClassPathName")); // NOI18N
516:
517: ResourceSelector selector = new ResourceSelector(root);
518: DialogDescriptor dd = new DialogDescriptor(selector, bundle
519: .getString("CTL_OpenDialogName")); // NOI18N
520: Object res = DialogDisplayer.getDefault().notify(dd);
521: nodes = (res == DialogDescriptor.OK_OPTION) ? selector
522: .getNodes() : null;
523: String name = null;
524: if ((nodes != null) && (nodes.length == 1)) {
525: DataObject dob = (DataObject) nodes[0]
526: .getCookie(DataObject.class);
527: if (dob != null) {
528: FileObject fob = dob.getPrimaryFile();
529: if (fob != null) {
530: if (executionClassPath.contains(fob)) {
531: name = executionClassPath
532: .getResourceName(fob);
533: } else {
534: ClassPath srcClassPath = ClassPath
535: .getClassPath(fob, ClassPath.SOURCE);
536: name = srcClassPath.getResourceName(fob);
537: }
538: }
539: }
540: }
541: return name;
542: }
543:
544: } // end of IconPanel
545:
546: private static class RootNode extends FilterNode {
547: RootNode(Node node, String displayName) {
548: super (node);
549: if (displayName != null) {
550: disableDelegation(DELEGATE_GET_DISPLAY_NAME
551: | DELEGATE_SET_DISPLAY_NAME);
552: setDisplayName(displayName);
553: }
554: }
555: } // RootNode
556:
557: private static class ResourceSelector extends JPanel implements
558: ExplorerManager.Provider {
559: /** Manages the tree. */
560: private ExplorerManager manager = new ExplorerManager();
561:
562: public ResourceSelector(Node root) {
563: setLayout(new BorderLayout(0, 5));
564: setBorder(new EmptyBorder(12, 12, 0, 11));
565: getAccessibleContext().setAccessibleDescription(
566: bundle.getString("ACSD_ResourceSelector")); // NOI18N
567: getAccessibleContext().setAccessibleName(
568: bundle.getString("ACSN_ResourceSelector")); // NOI18N
569: manager.setRootContext(root);
570:
571: BeanTreeView tree = new BeanTreeView();
572: tree.setPopupAllowed(false);
573: tree.setDefaultActionAllowed(false);
574: // install proper border for tree
575: tree.setBorder((Border) UIManager
576: .get("Nb.ScrollPane.border")); // NOI18N
577: tree.getAccessibleContext().setAccessibleName(
578: bundle.getString("ACSN_ResourceSelectorView")); // NOI18N
579: tree.getAccessibleContext().setAccessibleDescription(
580: bundle.getString("ACSD_ResourceSelectorView")); // NOI18N
581: add(tree, BorderLayout.CENTER);
582: }
583:
584: /**
585: * Gets preferred size. Overrides superclass method.
586: * Height is adjusted to 1/2 screen.
587: */
588: public Dimension getPreferredSize() {
589: Dimension dim = super .getPreferredSize();
590: dim.height = Math
591: .max(dim.height, org.openide.util.Utilities
592: .getUsableScreenBounds().height / 2);
593: return dim;
594: }
595:
596: /**
597: * @return selected nodes
598: */
599: public Node[] getNodes() {
600: return manager.getSelectedNodes();
601: }
602:
603: public ExplorerManager getExplorerManager() {
604: return manager;
605: }
606:
607: } // ResourceSelector
608:
609: }
|