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-2007 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.apisupport.project.ui;
043:
044: import java.awt.Component;
045: import java.awt.Dialog;
046: import java.awt.Dimension;
047: import java.awt.FlowLayout;
048: import java.awt.GridLayout;
049: import java.awt.Image;
050: import java.awt.event.InputEvent;
051: import java.awt.event.KeyEvent;
052: import java.io.File;
053: import java.io.IOException;
054: import java.lang.ref.Reference;
055: import java.lang.ref.WeakReference;
056: import java.net.MalformedURLException;
057: import java.text.Collator;
058: import java.text.MessageFormat;
059: import java.util.ArrayList;
060: import java.util.Collection;
061: import java.util.Collections;
062: import java.util.HashSet;
063: import java.util.List;
064: import java.util.Locale;
065: import java.util.Map;
066: import java.util.SortedSet;
067: import java.util.Stack;
068: import java.util.StringTokenizer;
069: import java.util.TreeSet;
070: import javax.swing.ComboBoxModel;
071: import javax.swing.DefaultComboBoxModel;
072: import javax.swing.Icon;
073: import javax.swing.ImageIcon;
074: import javax.swing.JButton;
075: import javax.swing.JComboBox;
076: import javax.swing.JFileChooser;
077: import javax.swing.JLabel;
078: import javax.swing.JList;
079: import javax.swing.JPanel;
080: import javax.swing.JTextField;
081: import javax.swing.KeyStroke;
082: import javax.swing.ListCellRenderer;
083: import javax.swing.UIManager;
084: import javax.swing.event.DocumentEvent;
085: import javax.swing.event.DocumentListener;
086: import javax.swing.filechooser.FileFilter;
087: import javax.swing.filechooser.FileView;
088: import javax.swing.plaf.UIResource;
089: import javax.swing.text.JTextComponent;
090: import org.netbeans.api.project.Project;
091: import org.netbeans.api.project.ProjectInformation;
092: import org.netbeans.api.project.ProjectManager;
093: import org.netbeans.api.project.ProjectUtils;
094: import org.netbeans.api.project.SourceGroup;
095: import org.netbeans.api.project.ui.OpenProjects;
096: import org.netbeans.modules.apisupport.project.NbModuleProject;
097: import org.netbeans.modules.apisupport.project.spi.NbModuleProvider;
098: import org.netbeans.modules.apisupport.project.Util;
099: import org.netbeans.modules.apisupport.project.layers.LayerUtils;
100: import org.netbeans.modules.apisupport.project.suite.SuiteProject;
101: import org.netbeans.modules.apisupport.project.ui.customizer.SuiteUtils;
102: import org.netbeans.modules.apisupport.project.ui.wizard.NewNbModuleWizardIterator;
103: import org.netbeans.spi.java.project.support.ui.PackageView;
104: import org.netbeans.spi.project.ui.support.ProjectChooser;
105: import org.openide.DialogDescriptor;
106: import org.openide.DialogDisplayer;
107: import org.openide.ErrorManager;
108: import org.openide.NotifyDescriptor;
109: import org.openide.WizardDescriptor;
110: import org.openide.awt.Mnemonics;
111: import org.openide.filesystems.FileObject;
112: import org.openide.filesystems.FileStateInvalidException;
113: import org.openide.filesystems.FileSystem;
114: import org.openide.filesystems.FileUtil;
115: import org.openide.util.NbBundle;
116: import org.openide.util.NbCollections;
117: import org.openide.util.Utilities;
118:
119: /**
120: * UI related utility methods for the module.
121: *
122: * @author Martin Krauskopf
123: */
124: public final class UIUtil {
125:
126: private static final String ICON_KEY_UIMANAGER = "Tree.closedIcon"; // NOI18N
127: private static final String OPENED_ICON_KEY_UIMANAGER = "Tree.openIcon"; // NOI18N
128: private static final String ICON_KEY_UIMANAGER_NB = "Nb.Explorer.Folder.icon"; // NOI18N
129: private static final String OPENED_ICON_KEY_UIMANAGER_NB = "Nb.Explorer.Folder.openedIcon"; // NOI18N
130: private static final String ICON_PATH = "org/netbeans/modules/apisupport/project/resources/defaultFolder.gif"; // NOI18N
131: private static final String OPENED_ICON_PATH = "org/netbeans/modules/apisupport/project/resources/defaultFolderOpen.gif"; // NOI18N
132:
133: private UIUtil() {
134: }
135:
136: public static String keyToLogicalString(KeyStroke keyStroke) {
137: String keyDesc = Utilities.keyToString(keyStroke);
138: int dash = keyDesc.indexOf('-');
139: return dash == -1 ? keyDesc : keyDesc.substring(0, dash)
140: .replace('C', 'D').replace('A', 'O')
141: + keyDesc.substring(dash);
142: }
143:
144: public static String keyStrokeToString(KeyStroke keyStroke) {
145: int modifiers = keyStroke.getModifiers();
146: StringBuffer sb = new StringBuffer();
147: if ((modifiers & InputEvent.CTRL_DOWN_MASK) > 0) {
148: sb.append("Ctrl+"); // NOI18N
149: }
150: if ((modifiers & InputEvent.ALT_DOWN_MASK) > 0) {
151: sb.append("Alt+"); // NOI18N
152: }
153: if ((modifiers & InputEvent.SHIFT_DOWN_MASK) > 0) {
154: sb.append("Shift+"); // NOI18N
155: }
156: if ((modifiers & InputEvent.META_DOWN_MASK) > 0) {
157: sb.append("Meta+"); // NOI18N
158: }
159: if (keyStroke.getKeyCode() != KeyEvent.VK_SHIFT
160: && keyStroke.getKeyCode() != KeyEvent.VK_CONTROL
161: && keyStroke.getKeyCode() != KeyEvent.VK_META
162: && keyStroke.getKeyCode() != KeyEvent.VK_ALT
163: && keyStroke.getKeyCode() != KeyEvent.VK_ALT_GRAPH) {
164: sb.append(Utilities.keyToString(KeyStroke.getKeyStroke(
165: keyStroke.getKeyCode(), 0)));
166: }
167: return sb.toString();
168: }
169:
170: public static KeyStroke stringToKeyStroke(String keyStroke) {
171: int modifiers = 0;
172: if (keyStroke.startsWith("Ctrl+")) { // NOI18N
173: modifiers |= InputEvent.CTRL_DOWN_MASK;
174: keyStroke = keyStroke.substring(5);
175: }
176: if (keyStroke.startsWith("Alt+")) { // NOI18N
177: modifiers |= InputEvent.ALT_DOWN_MASK;
178: keyStroke = keyStroke.substring(4);
179: }
180: if (keyStroke.startsWith("Shift+")) { // NOI18N
181: modifiers |= InputEvent.SHIFT_DOWN_MASK;
182: keyStroke = keyStroke.substring(6);
183: }
184: if (keyStroke.startsWith("Meta+")) { // NOI18N
185: modifiers |= InputEvent.META_DOWN_MASK;
186: keyStroke = keyStroke.substring(5);
187: }
188: KeyStroke ks = Utilities.stringToKey(keyStroke);
189: if (ks == null) {
190: return null;
191: }
192: KeyStroke result = KeyStroke.getKeyStroke(ks.getKeyCode(),
193: modifiers);
194: return result;
195: }
196:
197: /**
198: * Returns multi keystroke for given text representation of shortcuts
199: * (like Alt+A B). Returns null if text is not parsable, and empty array
200: * for empty string.
201: */
202: public static KeyStroke[] stringToKeyStrokes(String keyStrokes) {
203: String delim = " "; // NOI18N
204: if (keyStrokes.length() == 0) {
205: return new KeyStroke[0];
206: }
207: StringTokenizer st = new StringTokenizer(keyStrokes, delim);
208: List<KeyStroke> result = new ArrayList<KeyStroke>();
209: while (st.hasMoreTokens()) {
210: String ks = st.nextToken().trim();
211: KeyStroke keyStroke = stringToKeyStroke(ks);
212: if (keyStroke == null) { // text is not parsable
213: return null;
214: }
215: result.add(keyStroke);
216: }
217: return result.toArray(new KeyStroke[result.size()]);
218: }
219:
220: public static String keyStrokesToString(final KeyStroke[] keyStrokes) {
221: StringBuffer sb = new StringBuffer(UIUtil
222: .keyStrokeToString(keyStrokes[0]));
223: int i, k = keyStrokes.length;
224: for (i = 1; i < k; i++) {
225: sb.append(' ').append(
226: UIUtil.keyStrokeToString(keyStrokes[i]));
227: }
228: String newShortcut = sb.toString();
229: return newShortcut;
230: }
231:
232: public static String keyStrokesToLogicalString(
233: final KeyStroke[] keyStrokes) {
234: StringBuffer sb = new StringBuffer(UIUtil
235: .keyToLogicalString(keyStrokes[0]));
236: int i, k = keyStrokes.length;
237: for (i = 1; i < k; i++) {
238: sb.append(' ').append(
239: UIUtil.keyToLogicalString((keyStrokes[i])));
240: }
241: String newShortcut = sb.toString();
242: return newShortcut;
243: }
244:
245: /**
246: * Calls in turn {@link ProjectChooser#setProjectsFolder} if the
247: * <code>folder</code> is not <code>null</code> and is a directory.
248: */
249: public static void setProjectChooserDir(File folder) {
250: if (folder == null || !folder.isDirectory()) {
251: return;
252: }
253: ProjectChooser.setProjectsFolder(folder);
254: }
255:
256: /**
257: * Calls {@link #setProjectChooserDir} with the <code>fileOrFolder</code>'s
258: * parent if it isn't <code>null</code>. Otherwise fallbacks to
259: * <code>fileOrFolder</code> itself if it is a directory.
260: */
261: public static void setProjectChooserDirParent(File fileOrFolder) {
262: if (fileOrFolder == null) {
263: return;
264: }
265: File parent = fileOrFolder.getParentFile();
266: setProjectChooserDir(parent != null ? parent : (fileOrFolder
267: .isDirectory() ? fileOrFolder : null));
268: }
269:
270: /**
271: * Set the <code>text</code> for the <code>textComp</code> and set its
272: * caret position to the end of the text.
273: */
274: public static void setText(JTextComponent textComp, String text) {
275: textComp.setText(text);
276: textComp.setCaretPosition(text == null ? 0 : text.length());
277: }
278:
279: /**
280: * Convenient class for listening on document changes. Use it if you do not
281: * care what exact change really happened. {@link #removeUpdate} and {@link
282: * #changedUpdate} just delegate to {@link #insertUpdate}. So everything
283: * what is needed in order to be notified about document changes is to
284: * override {@link #insertUpdate} method.
285: */
286: public abstract static class DocumentAdapter implements
287: DocumentListener {
288: public void removeUpdate(DocumentEvent e) {
289: insertUpdate(null);
290: }
291:
292: public void changedUpdate(DocumentEvent e) {
293: insertUpdate(null);
294: }
295: }
296:
297: private static Reference<JFileChooser> iconChooser;
298:
299: /**
300: * @param icon file representing icon
301: * @param expectedWidth expected width
302: * @param expectedHeight expected height
303: * @return warning or empty <code>String</code>
304: */
305: public static String getIconDimensionWarning(File icon,
306: int expectedWidth, int expectedHeight) {
307: Dimension real = new Dimension(UIUtil.getIconDimension(icon));
308: if (real.height == expectedHeight
309: && real.width == expectedWidth) {
310: return "";
311: }
312: return NbBundle.getMessage(UIUtil.class, "MSG_WrongIconSize",
313: new Object[] { real.width, real.height, expectedWidth,
314: expectedHeight });
315: }
316:
317: /**
318: * @param expectedWidth expected width
319: * @param expectedHeight expected height
320: * @return warning
321: */
322: public static String getNoIconSelectedWarning(int expectedWidth,
323: int expectedHeight) {
324: return NbBundle.getMessage(UIUtil.class, "MSG_NoIconSelected",
325: expectedWidth, expectedHeight);
326: }
327:
328: /**
329: * @param icon file representing icon
330: * @param expectedWidth expected width
331: * @param expectedHeight expected height
332: * @return true if icon corresponds to expected dimension
333: */
334: public static boolean isValidIcon(final File icon,
335: int expectedWidth, int expectedHeight) {
336: Dimension iconDimension = UIUtil.getIconDimension(icon);
337: return (expectedWidth == iconDimension.getWidth() && expectedHeight == iconDimension
338: .getHeight());
339: }
340:
341: /**
342: * @param icon file representing icon
343: * @return width and height of icon encapsulated into {@link java.awt.Dimension}
344: */
345: public static Dimension getIconDimension(final File icon) {
346: try {
347: ImageIcon imc = new ImageIcon(icon.toURI().toURL());
348: return new Dimension(imc.getIconWidth(), imc
349: .getIconHeight());
350: } catch (MalformedURLException ex) {
351: ErrorManager.getDefault().notify(ex);
352: }
353: return new Dimension(-1, -1);
354: }
355:
356: /**
357: * Returns an instance of {@link javax.swing.JFileChooser} permitting
358: * selection only a regular <em>icon</em>.
359: */
360: public static JFileChooser getIconFileChooser() {
361: if (iconChooser != null) {
362: JFileChooser choose = iconChooser.get();
363: if (choose != null) {
364: return choose;
365: }
366: }
367: final JFileChooser chooser = new IconFileChooser();
368: iconChooser = new WeakReference<JFileChooser>(chooser);
369: return chooser;
370: }
371:
372: /**
373: * tries to set the selected file according to currently existing data.
374: * Will se it only if the String represents a file path that exists.
375: */
376: public static JFileChooser getIconFileChooser(String oldValue) {
377: JFileChooser chooser = getIconFileChooser();
378: String iconText = oldValue.trim();
379: if (iconText.length() > 0) {
380: File fil = new File(iconText);
381: if (fil.exists()) {
382: chooser.setSelectedFile(fil);
383: }
384: }
385: return chooser;
386: }
387:
388: /**
389: * Create combobox containing packages from the given {@link SourceGroup}.
390: */
391: public static JComboBox createPackageComboBox(SourceGroup srcRoot) {
392: JComboBox packagesComboBox = new JComboBox(PackageView
393: .createListView(srcRoot));
394: packagesComboBox.setRenderer(PackageView.listRenderer());
395: return packagesComboBox;
396: }
397:
398: /**
399: * Returns true for valid package name.
400: */
401: public static boolean isValidPackageName(String str) {
402: if (str.length() > 0 && str.charAt(0) == '.') {
403: return false;
404: }
405: StringTokenizer tukac = new StringTokenizer(str, "."); // NOI18N
406: while (tukac.hasMoreTokens()) {
407: String token = tukac.nextToken();
408: if ("".equals(token)) {
409: return false;
410: }
411: if (!Utilities.isJavaIdentifier(token)) {
412: return false;
413: }
414: }
415: return true;
416: }
417:
418: /**
419: * Returns a string suitable for text areas respresenting content of {@link
420: * CreatedModifiedFiles} <em>paths</em>.
421: *
422: * @param relPaths should be either
423: * {@link CreatedModifiedFiles#getCreatedPaths()} or
424: * {@link CreatedModifiedFiles#getModifiedPaths()}.
425: */
426: public static String generateTextAreaContent(String[] relPaths) {
427: StringBuffer sb = new StringBuffer();
428: if (relPaths.length > 0) {
429: for (int i = 0; i < relPaths.length; i++) {
430: if (i > 0) {
431: sb.append('\n');
432: }
433: sb.append(relPaths[i]);
434: }
435: }
436: return sb.toString();
437: }
438:
439: /**
440: * Calls in turn {@link #createLayerPresenterComboModel(Project, String,
441: * Map)} with {@link Collections#EMPTY_MAP} as a third parameter.
442: */
443: public static ComboBoxModel createLayerPresenterComboModel(
444: final Project project, final String sfsRoot) {
445: return createLayerPresenterComboModel(project, sfsRoot,
446: Collections.<String, Object> emptyMap());
447: }
448:
449: /**
450: * Returns {@link ComboBoxModel} containing {@link #LayerItemPresenter}s
451: * wrapping all folders under the given <code>sfsRoot</code>.
452: *
453: * @param excludeAttrs {@link Map} of pairs String - Object used to filter
454: * out folders which have one or more attribute(key)
455: * with a corresponding value.
456: */
457: public static ComboBoxModel createLayerPresenterComboModel(
458: final Project project, final String sfsRoot,
459: final Map<String, Object> excludeAttrs) {
460: DefaultComboBoxModel model = new DefaultComboBoxModel();
461: try {
462: FileSystem sfs = LayerUtils
463: .getEffectiveSystemFilesystem(project);
464: FileObject root = sfs.getRoot().getFileObject(sfsRoot);
465: if (root != null) {
466: SortedSet<LayerItemPresenter> presenters = new TreeSet<LayerItemPresenter>();
467: for (FileObject subFolder : getFolders(root,
468: excludeAttrs)) {
469: presenters.add(new LayerItemPresenter(subFolder,
470: root));
471: }
472: for (LayerItemPresenter presenter : presenters) {
473: model.addElement(presenter);
474: }
475: }
476: } catch (IOException exc) {
477: Util.err.notify(exc);
478: }
479: return model;
480: }
481:
482: public static class LayerItemPresenter implements
483: Comparable<LayerItemPresenter> {
484:
485: private String displayName;
486: private final FileObject item;
487: private final FileObject root;
488: private final boolean contentType;
489:
490: public LayerItemPresenter(final FileObject item,
491: final FileObject root, final boolean contentType) {
492: this .item = item;
493: this .root = root;
494: this .contentType = contentType;
495: }
496:
497: public LayerItemPresenter(final FileObject item,
498: final FileObject root) {
499: this (item, root, false);
500: }
501:
502: public FileObject getFileObject() {
503: return item;
504: }
505:
506: public String getFullPath() {
507: return item.getPath();
508: }
509:
510: public String getDisplayName() {
511: if (displayName == null) {
512: displayName = computeDisplayName();
513: }
514: return displayName;
515: }
516:
517: public @Override
518: String toString() {
519: return getDisplayName();
520: }
521:
522: public int compareTo(LayerItemPresenter o) {
523: int res = Collator.getInstance().compare(getDisplayName(),
524: o.getDisplayName());
525: if (res != 0) {
526: return res;
527: } else {
528: return getFullPath().compareTo(o.getFullPath());
529: }
530: }
531:
532: private static String getFileObjectName(FileObject fo) {
533: String name = null;
534: try {
535: name = fo.getFileSystem().getStatus().annotateName(
536: fo.getNameExt(), Collections.singleton(fo));
537: } catch (FileStateInvalidException ex) {
538: name = fo.getName();
539: }
540: return name;
541: }
542:
543: private String computeDisplayName() {
544: FileObject displayItem = contentType ? item.getParent()
545: : item;
546: String displaySeparator = contentType ? "/" : " | "; // NOI18N
547: Stack<String> s = new Stack<String>();
548: s.push(getFileObjectName(displayItem));
549: FileObject parent = displayItem.getParent();
550: while (!root.getPath().equals(parent.getPath())) {
551: s.push(getFileObjectName(parent));
552: parent = parent.getParent();
553: }
554: StringBuffer sb = new StringBuffer();
555: sb.append(s.pop());
556: while (!s.empty()) {
557: sb.append(displaySeparator).append(s.pop());
558: }
559: return sb.toString();
560: }
561:
562: }
563:
564: /**
565: * Returns path relative to the root of the SFS. May return
566: * <code>null</code> for empty String or user's custom non-string items.
567: * Also see {@link Util#isValidSFSPath(String)}.
568: */
569: public static String getSFSPath(final JComboBox lpCombo,
570: final String supposedRoot) {
571: Object editorItem = lpCombo.getEditor().getItem();
572: String path = null;
573: if (editorItem instanceof LayerItemPresenter) {
574: path = ((LayerItemPresenter) editorItem).getFullPath();
575: } else if (editorItem instanceof String) {
576: String editorItemS = ((String) editorItem).trim();
577: if (editorItemS.length() > 0) {
578: path = searchLIPCategoryCombo(lpCombo, editorItemS);
579: if (path == null) {
580: // entered by user - absolute and relative are supported...
581: path = editorItemS.startsWith(supposedRoot) ? editorItemS
582: : supposedRoot + '/' + editorItemS;
583: }
584: }
585: }
586: return path;
587: }
588:
589: public static NbModuleProject chooseSuiteComponent(
590: Component parent, SuiteProject suite) {
591: NbModuleProject suiteComponent = null;
592: Project project = chooseProject(parent);
593: if (project != null) {
594: NbModuleProvider nmtp = project.getLookup().lookup(
595: NbModuleProvider.class);
596: if (nmtp == null) { // not netbeans module
597: DialogDisplayer.getDefault().notify(
598: new NotifyDescriptor.Message(NbBundle
599: .getMessage(UIUtil.class,
600: "MSG_TryingToAddNonNBModule",
601: ProjectUtils.getInformation(
602: project)
603: .getDisplayName())));
604: } else if (SuiteUtils.getSubProjects(suite).contains(
605: project)) {
606: DialogDisplayer
607: .getDefault()
608: .notify(
609: new NotifyDescriptor.Message(
610: NbBundle
611: .getMessage(
612: UIUtil.class,
613: "MSG_SuiteAlreadyContainsProject",
614: ProjectUtils
615: .getInformation(
616: suite)
617: .getDisplayName(),
618: ProjectUtils
619: .getInformation(
620: project)
621: .getDisplayName())));
622: } else if (nmtp.getModuleType() == NbModuleProvider.SUITE_COMPONENT) {
623: Object[] params = new Object[] {
624: ProjectUtils.getInformation(project)
625: .getDisplayName(),
626: getSuiteProjectName(project),
627: getSuiteProjectDirectory(project),
628: ProjectUtils.getInformation(suite)
629: .getDisplayName(), };
630: NotifyDescriptor.Confirmation confirmation = new NotifyDescriptor.Confirmation(
631: NbBundle.getMessage(UIUtil.class,
632: "MSG_MoveFromSuiteToSuite", params),
633: NotifyDescriptor.OK_CANCEL_OPTION);
634: DialogDisplayer.getDefault().notify(confirmation);
635: if (confirmation.getValue() == NotifyDescriptor.OK_OPTION) {
636: suiteComponent = (NbModuleProject) project;
637: }
638: } else if (nmtp.getModuleType() == NbModuleProvider.STANDALONE) {
639: suiteComponent = (NbModuleProject) project;
640: } else if (nmtp.getModuleType() == NbModuleProvider.NETBEANS_ORG) {
641: DialogDisplayer.getDefault().notify(
642: new NotifyDescriptor.Message(NbBundle
643: .getMessage(UIUtil.class,
644: "MSG_TryingToAddNBORGModule",
645: ProjectUtils.getInformation(
646: project)
647: .getDisplayName())));
648: }
649: }
650: return suiteComponent;
651: }
652:
653: /**
654: * Appropriately renders {@link Project}s. For others instances delegates
655: * to {@link DefaultListCellRenderer}.
656: */
657: public static ListCellRenderer createProjectRenderer() {
658: return new ProjectRenderer();
659: }
660:
661: private static class ProjectRenderer extends JLabel implements
662: ListCellRenderer, UIResource {
663:
664: public ProjectRenderer() {
665: setOpaque(true);
666: }
667:
668: public Component getListCellRendererComponent(JList list,
669: Object value, int index, boolean isSelected,
670: boolean cellHasFocus) {
671: // #93658: GTK needs name to render cell renderer "natively"
672: setName("ComboBox.listRenderer"); // NOI18N
673:
674: String text = null;
675: if (!(value instanceof Project)) {
676: text = value.toString();
677: } else {
678: ProjectInformation pi = ProjectUtils
679: .getInformation((Project) value);
680: text = pi.getDisplayName();
681: setIcon(pi.getIcon());
682: }
683: setText(text);
684:
685: if (isSelected) {
686: setBackground(list.getSelectionBackground());
687: setForeground(list.getSelectionForeground());
688: } else {
689: setBackground(list.getBackground());
690: setForeground(list.getForeground());
691: }
692:
693: return this ;
694: }
695:
696: // #93658: GTK needs name to render cell renderer "natively"
697: public @Override
698: String getName() {
699: String name = super .getName();
700: return name == null ? "ComboBox.renderer" : name; // NOI18N
701: }
702:
703: }
704:
705: /**
706: * Returns default folder icon as {@link java.awt.Image}. Never returns
707: * <code>null</code>.
708: *
709: * @param opened wheter closed or opened icon should be returned.
710: */
711: public static Image getTreeFolderIcon(boolean opened) {
712: Image base = null;
713: Icon baseIcon = UIManager
714: .getIcon(opened ? OPENED_ICON_KEY_UIMANAGER
715: : ICON_KEY_UIMANAGER); // #70263
716: if (baseIcon != null) {
717: base = Utilities.icon2Image(baseIcon);
718: } else {
719: base = (Image) UIManager
720: .get(opened ? OPENED_ICON_KEY_UIMANAGER_NB
721: : ICON_KEY_UIMANAGER_NB); // #70263
722: if (base == null) { // fallback to our owns
723: base = Utilities.loadImage(opened ? OPENED_ICON_PATH
724: : ICON_PATH, true);
725: }
726: }
727: assert base != null;
728: return base;
729: }
730:
731: public static NbModuleProject runLibraryWrapperWizard(
732: final Project suiteProvider) {
733: NewNbModuleWizardIterator iterator = NewNbModuleWizardIterator
734: .createLibraryModuleIterator(suiteProvider);
735: return UIUtil.runProjectWizard(iterator,
736: "CTL_NewLibraryWrapperProject"); // NOI18N
737: }
738:
739: public static NbModuleProject runProjectWizard(
740: final NewNbModuleWizardIterator iterator,
741: final String titleBundleKey) {
742: WizardDescriptor wd = new WizardDescriptor(iterator);
743: wd.setTitleFormat(new MessageFormat("{0}")); // NOI18N
744: wd.setTitle(NbBundle.getMessage(UIUtil.class, titleBundleKey));
745: Dialog dialog = DialogDisplayer.getDefault().createDialog(wd);
746: dialog.setVisible(true);
747: dialog.toFront();
748: NbModuleProject project = null;
749: boolean cancelled = wd.getValue() != WizardDescriptor.FINISH_OPTION;
750: if (!cancelled) {
751: FileObject folder = iterator.getCreateProjectFolder();
752: try {
753: project = (NbModuleProject) ProjectManager.getDefault()
754: .findProject(folder);
755: OpenProjects.getDefault().open(
756: new Project[] { project }, false);
757: if (wd.getProperty("setAsMain") == Boolean.TRUE) { // NOI18N
758: OpenProjects.getDefault().setMainProject(project);
759: }
760: } catch (IOException e) {
761: ErrorManager.getDefault().notify(
762: ErrorManager.INFORMATIONAL, e);
763: }
764: }
765: return project;
766: }
767:
768: /**
769: * Searches LayerItemPresenter combobox by the item's display name.
770: */
771: private static String searchLIPCategoryCombo(
772: final JComboBox lpCombo, final String displayName) {
773: String path = null;
774: for (int i = 0; i < lpCombo.getItemCount(); i++) {
775: Object item = lpCombo.getItemAt(i);
776: if (!(item instanceof LayerItemPresenter)) {
777: continue;
778: }
779: LayerItemPresenter presenter = (LayerItemPresenter) lpCombo
780: .getItemAt(i);
781: if (displayName.equals(presenter.getDisplayName())) {
782: path = presenter.getFullPath();
783: break;
784: }
785: }
786: return path;
787: }
788:
789: private static Project chooseProject(Component parent) {
790: JFileChooser chooser = ProjectChooser.projectChooser();
791: int option = chooser.showOpenDialog(parent);
792: Project project = null;
793: if (option == JFileChooser.APPROVE_OPTION) {
794: File projectDir = chooser.getSelectedFile();
795: UIUtil.setProjectChooserDirParent(projectDir);
796: try {
797: project = ProjectManager.getDefault().findProject(
798: FileUtil.toFileObject(projectDir));
799: } catch (IOException e) {
800: ErrorManager.getDefault().notify(ErrorManager.WARNING,
801: e);
802: }
803: }
804: return project;
805: }
806:
807: private static File getSuiteDirectory(Project suiteComp) {
808: File suiteDir = SuiteUtils.getSuiteDirectory(suiteComp);
809: assert suiteDir != null : "Invalid suite provider for: "
810: + suiteComp.getProjectDirectory();
811: return suiteDir;
812: }
813:
814: private static String getSuiteProjectDirectory(Project suiteComp) {
815: return getSuiteDirectory(suiteComp).getAbsolutePath();
816: }
817:
818: private static String getSuiteProjectName(Project suiteComp) {
819: FileObject suiteDir = FileUtil
820: .toFileObject(getSuiteDirectory(suiteComp));
821: if (suiteDir == null) {
822: // #94915
823: return "???"; // NOI18N
824: }
825: return Util.getDisplayName(suiteDir);
826: }
827:
828: private static Collection<FileObject> getFolders(
829: final FileObject root,
830: final Map<String, Object> excludeAttrs) {
831: Collection<FileObject> folders = new HashSet<FileObject>();
832: SUBFOLDERS: for (FileObject subFolder : NbCollections
833: .iterable(root.getFolders(false))) {
834: for (Map.Entry<String, Object> entry : excludeAttrs
835: .entrySet()) {
836: if (entry.getValue().equals(
837: subFolder.getAttribute(entry.getKey()))) {
838: continue SUBFOLDERS;
839: }
840: }
841: folders.add(subFolder);
842: folders.addAll(getFolders(subFolder, excludeAttrs));
843: }
844: return folders;
845: }
846:
847: private static final class IconFilter extends FileFilter {
848: public boolean accept(File pathname) {
849: return pathname.isDirectory()
850: || pathname.getName().toLowerCase(Locale.ENGLISH)
851: .endsWith("gif") || // NOI18N
852: pathname.getName().toLowerCase(Locale.ENGLISH)
853: .endsWith("png"); // NOI18N
854: }
855:
856: public String getDescription() {
857: return "*.gif, *.png"; // NOI18N
858: }
859: }
860:
861: /**
862: * Show an OK/cancel-type dialog with customized button texts.
863: * Only a separate method because it is otherwise cumbersome to replace
864: * the OK button with a button that is set as the default.
865: * @param title the dialog title
866: * @param message the body of the message (usually HTML text)
867: * @param acceptButton a label for the default accept button; should not use mnemonics
868: * @param cancelButton a label for the cancel button (or null for default); should not use mnemonics
869: * @param messageType {@link NotifyDescriptor#WARNING_MESSAGE} or similar
870: * @return true if user accepted the dialog
871: */
872: public static boolean showAcceptCancelDialog(String title,
873: String message, String acceptButton, String cancelButton,
874: int messageType) {
875: return showAcceptCancelDialog(title, message, acceptButton,
876: null, cancelButton, messageType);
877: }
878:
879: /**
880: * Show an OK/cancel-type dialog with customized button texts.
881: * Only a separate method because it is otherwise cumbersome to replace
882: * the OK button with a button that is set as the default.
883: * @param title the dialog title
884: * @param message the body of the message (usually HTML text)
885: * @param acceptButton a label for the default accept button; should not use mnemonics
886: * @param accDescrAcceptButton a accessible description for acceptButton
887: * @param cancelButton a label for the cancel button (or null for default); should not use mnemonics
888: * @param messageType {@link NotifyDescriptor#WARNING_MESSAGE} or similar
889: * @return true if user accepted the dialog
890: */
891: public static boolean showAcceptCancelDialog(String title,
892: String message, String acceptButton,
893: String accDescrAcceptButton, String cancelButton,
894: int messageType) {
895: DialogDescriptor d = new DialogDescriptor(message, title);
896: d.setModal(true);
897: JButton accept = new JButton(acceptButton);
898: accept.setDefaultCapable(true);
899: if (accDescrAcceptButton != null) {
900: accept.getAccessibleContext().setAccessibleDescription(
901: accDescrAcceptButton);
902: }
903: d.setOptions(new Object[] {
904: accept,
905: cancelButton != null ? new JButton(cancelButton)
906: : NotifyDescriptor.CANCEL_OPTION, });
907: d.setMessageType(messageType);
908: return DialogDisplayer.getDefault().notify(d).equals(accept);
909: }
910:
911: private static class IconFileChooser extends JFileChooser {
912: private final JTextField iconInfo = new JTextField();
913:
914: private IconFileChooser() {
915: JPanel accessoryPanel = getAccesoryPanel(iconInfo);
916: setDialogTitle(NbBundle.getMessage(UIUtil.class,
917: "TITLE_IconDialog"));//NOI18N
918: setAccessory(accessoryPanel);
919: setAcceptAllFileFilterUsed(false);
920: setFileSelectionMode(JFileChooser.FILES_ONLY);
921: setMultiSelectionEnabled(false);
922: addChoosableFileFilter(new IconFilter());
923: setFileView(new FileView() {
924: public @Override
925: Icon getIcon(File f) {
926: // Show icons right in the chooser, to make it easier to find
927: // the right one.
928: if (f.getName().endsWith(".gif")
929: || f.getName().endsWith(".png")) { // NOI18N
930: Icon icon = new ImageIcon(f.getAbsolutePath());
931: if (icon.getIconWidth() == 16
932: && icon.getIconHeight() == 16) {
933: return icon;
934: }
935: }
936: return null;
937: }
938:
939: public @Override
940: String getName(File f) {
941: File f2 = getSelectedFile();
942: if (f2 != null
943: && (f2.getName().endsWith(".gif") || f2
944: .getName().endsWith(".png"))) { // NOI18N
945: Icon icon = new ImageIcon(f2.getAbsolutePath());
946: StringBuffer sb = new StringBuffer();
947: sb.append(f2.getName()).append(" [");//NOI18N
948: sb.append(icon.getIconWidth()).append('x')
949: .append(icon.getIconHeight());
950: sb.append(']');
951: setApproveButtonToolTipText(sb.toString());
952: iconInfo.setText(sb.toString());
953: } else {
954: iconInfo.setText("");
955: }
956: return super .getName(f);
957: }
958:
959: });
960: }
961:
962: private static JPanel getAccesoryPanel(final JTextField iconInfo) {
963: iconInfo.setColumns(15);
964: iconInfo.setEditable(false);
965:
966: JPanel accessoryPanel = new JPanel();
967: JPanel inner = new JPanel();
968: JLabel iconInfoLabel = new JLabel();
969: accessoryPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 6,
970: 0));
971:
972: inner.setLayout(new GridLayout(2, 1, 0, 6));
973:
974: iconInfoLabel.setLabelFor(iconInfo);
975: Mnemonics.setLocalizedText(iconInfoLabel, NbBundle
976: .getMessage(UIUtil.class, "LBL_IconInfo"));
977: inner.add(iconInfoLabel);
978:
979: inner.add(iconInfo);
980:
981: accessoryPanel.add(inner);
982: return accessoryPanel;
983: }
984: }
985: }
|