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.palette;
043:
044: import java.awt.event.ActionListener;
045: import java.beans.BeanInfo;
046: import java.util.ResourceBundle;
047: import java.text.MessageFormat;
048: import java.awt.event.ActionEvent;
049: import java.awt.datatransfer.*;
050: import java.io.IOException;
051: import java.util.Arrays;
052: import java.util.Comparator;
053: import java.util.concurrent.Callable;
054: import java.util.logging.Level;
055: import java.util.logging.Logger;
056: import javax.swing.*;
057: import org.netbeans.spi.palette.PaletteController;
058: import org.netbeans.modules.palette.ui.PalettePanel;
059: import org.netbeans.spi.palette.PaletteActions;
060:
061: import org.openide.*;
062: import org.openide.loaders.DataObject;
063: import org.openide.nodes.*;
064: import org.openide.filesystems.*;
065: import org.openide.util.*;
066: import org.openide.util.datatransfer.PasteType;
067: import org.openide.util.datatransfer.NewType;
068: import org.openide.util.datatransfer.ExClipboard;
069: import org.openide.windows.TopComponent;
070:
071: /**
072: * Class providing various useful methods for palette classes.
073: *
074: * @author S Aubrecht
075: */
076: public final class Utils {
077:
078: private static final Logger ERR = Logger
079: .getLogger("org.netbeans.modules.palette"); // NOI18N
080:
081: private Utils() {
082: }
083:
084: // -----------
085:
086: public static ResourceBundle getBundle() {
087: return NbBundle.getBundle(Utils.class);
088: }
089:
090: public static String getBundleString(String key) {
091: return getBundle().getString(key);
092: }
093:
094: public static Action[] mergeActions(Action[] first, Action[] second) {
095: if (null == first)
096: return second;
097: if (null == second)
098: return first;
099:
100: Action[] res = new Action[first.length + second.length + 1];
101: System.arraycopy(first, 0, res, 0, first.length);
102: res[first.length] = null;
103: System.arraycopy(second, 0, res, first.length + 1,
104: second.length);
105: return res;
106: }
107:
108: public static boolean isReadonly(Node node) {
109: Object val = node.getValue(PaletteController.ATTR_IS_READONLY);
110: if (null == val) {
111: DataObject dobj = (DataObject) node
112: .getCookie(DataObject.class);
113: if (null != dobj) {
114: val = dobj.getPrimaryFile().getAttribute(
115: PaletteController.ATTR_IS_READONLY);
116: }
117: }
118: if (null != val) {
119: return Boolean.valueOf(val.toString()).booleanValue();
120: } else {
121: return !node.canDestroy();
122: }
123: }
124:
125: public static HelpCtx getHelpCtx(Node node, HelpCtx defaultHelp) {
126: HelpCtx retValue = defaultHelp;
127: if (null == retValue || HelpCtx.DEFAULT_HELP.equals(retValue)) {
128: Object val = node.getValue(PaletteController.ATTR_HELP_ID);
129: if (null == val) {
130: DataObject dobj = (DataObject) node
131: .getCookie(DataObject.class);
132: if (null != dobj) {
133: val = dobj.getPrimaryFile().getAttribute(
134: PaletteController.ATTR_HELP_ID);
135: }
136: }
137:
138: if (null != val)
139: retValue = new HelpCtx(val.toString());
140: }
141: return retValue;
142: }
143:
144: public static void addCustomizationMenuItems(JPopupMenu popup,
145: PaletteController controller, Settings settings) {
146: popup.addSeparator();
147: popup.add(new ShowNamesAction(settings));
148: popup.add(new ChangeIconSizeAction(settings));
149: addResetMenuItem(popup, controller, settings);
150: popup.addSeparator();
151: popup.add(new ShowCustomizerAction(controller));
152: }
153:
154: static void addResetMenuItem(JPopupMenu popup,
155: final PaletteController controller, final Settings settings) {
156: JMenuItem item = new JMenuItem(
157: getBundleString("CTL_ResetPalettePopup"));
158: item.addActionListener(new ActionListener() {
159: public void actionPerformed(ActionEvent e) {
160: resetPalette(controller, settings);
161: }
162: });
163: popup.add(item);
164: }
165:
166: /**
167: * Find a Node representing the given category.
168: *
169: * @param root Palette's root node.
170: * @param categoryName Name of the category to search for.
171: * @return Category with the given name or null.
172: */
173: public static Node findCategoryNode(Node root, String categoryName) {
174: return root.getChildren().findChild(categoryName);
175: }
176:
177: public static void resetPalette(final PaletteController controller,
178: final Settings settings) {
179: Node rootNode = (Node) controller.getRoot().lookup(Node.class);
180: if (null != rootNode) {
181: PaletteActions customActions = rootNode.getLookup().lookup(
182: PaletteActions.class);
183: Action resetAction = customActions.getResetAction();
184: if (null != resetAction) {
185: settings.reset();
186: resetAction.actionPerformed(new ActionEvent(controller,
187: 0, "reset")); //NOI18N
188: controller.refresh();
189: } else {
190: resetPalette(rootNode, controller, settings);
191: }
192: }
193: }
194:
195: public static void resetPalette(Node rootNode,
196: PaletteController controller, Settings settings) {
197: // first user confirmation...
198: NotifyDescriptor desc = new NotifyDescriptor.Confirmation(
199: getBundleString("MSG_ConfirmPaletteReset"), // NOI18N
200: getBundleString("CTL_ConfirmResetTitle"), // NOI18N
201: NotifyDescriptor.YES_NO_OPTION);
202:
203: if (NotifyDescriptor.YES_OPTION.equals(DialogDisplayer
204: .getDefault().notify(desc))) {
205:
206: settings.reset();
207: DataObject dob = (DataObject) rootNode.getLookup().lookup(
208: DataObject.class);
209: if (null != dob) {
210: FileObject primaryFile = dob.getPrimaryFile();
211: if (null != primaryFile && primaryFile.isFolder()) {
212: final Object cleaner = primaryFile
213: .getAttribute("removeWritables"); //NOI18N
214: if (null != cleaner
215: && (cleaner instanceof Callable)) {
216: try {
217: ((Callable) cleaner).call();
218: } catch (Exception ex) {
219: ERR.log(Level.INFO, ex
220: .getLocalizedMessage(), ex);
221: }
222: }
223: }
224: }
225: controller.refresh();
226: }
227: }
228:
229: public static void setOpenedByUser(TopComponent tc,
230: boolean userOpened) {
231: tc.putClientProperty("userOpened", Boolean.valueOf(userOpened)); //NOI18N
232: }
233:
234: public static boolean isOpenedByUser(TopComponent tc) {
235: Object val = tc.getClientProperty("userOpened");
236: tc.putClientProperty("userOpened", null);
237: return null != val && val instanceof Boolean
238: && ((Boolean) val).booleanValue();
239: }
240:
241: /**
242: * An action to create a new palette category.
243: */
244: public static class NewCategoryAction extends AbstractAction {
245: private Node paletteNode;
246:
247: /**
248: * @param paletteRootNode Palette's root node.
249: */
250: public NewCategoryAction(Node paletteRootNode) {
251: putValue(Action.NAME, getBundleString("CTL_CreateCategory")); // NOI18N
252: this .paletteNode = paletteRootNode;
253: }
254:
255: public void actionPerformed(ActionEvent event) {
256: NewType[] newTypes = paletteNode.getNewTypes();
257: try {
258: if (null != newTypes && newTypes.length > 0) {
259: newTypes[0].create();
260: }
261: } catch (IOException ioE) {
262: ERR.log(Level.INFO, ioE.getLocalizedMessage(), ioE);
263: }
264: }
265: }
266:
267: /**
268: * An action to sort categories alphabetically.
269: */
270: static class SortCategoriesAction extends AbstractAction {
271: private Node paletteNode;
272:
273: public SortCategoriesAction(Node paletteNode) {
274: putValue(Action.NAME, getBundleString("CTL_SortCategories")); // NOI18N
275: this .paletteNode = paletteNode;
276: }
277:
278: public void actionPerformed(ActionEvent event) {
279: Index order = (Index) paletteNode.getCookie(Index.class);
280: if (order != null) {
281: final Node[] nodes = paletteNode.getChildren()
282: .getNodes(DefaultModel.canBlock());
283: Arrays.sort(nodes, new Comparator<Node>() {
284: public int compare(Node n1, Node n2) {
285: return n1.getDisplayName().compareTo(
286: n2.getDisplayName());
287: }
288: });
289: int[] perm = new int[nodes.length];
290: for (int i = 0; i < perm.length; i++) {
291: perm[i] = order.indexOf(nodes[i]);
292: }
293: order.reorder(perm);
294: }
295: }
296:
297: public boolean isEnabled() {
298: return (paletteNode.getCookie(Index.class) != null);
299: }
300: }
301:
302: /**
303: * An action to show/hide palette item names.
304: */
305: private static class ShowNamesAction extends AbstractAction {
306:
307: private Settings settings;
308:
309: public ShowNamesAction(Settings settings) {
310: this .settings = settings;
311: }
312:
313: public void actionPerformed(ActionEvent event) {
314: settings.setShowItemNames(!settings.getShowItemNames());
315: }
316:
317: public Object getValue(String key) {
318: if (Action.NAME.equals(key)) {
319: boolean showNames = settings.getShowItemNames();
320: return getBundleString(showNames ? "CTL_HideNames"
321: : "CTL_ShowNames"); // NOI18N
322: } else {
323: return super .getValue(key);
324: }
325: }
326: }
327:
328: /**
329: * An action to change the size of palette icons.
330: */
331: private static class ChangeIconSizeAction extends AbstractAction {
332:
333: private Settings settings;
334:
335: public ChangeIconSizeAction(Settings settings) {
336: this .settings = settings;
337: }
338:
339: public void actionPerformed(ActionEvent event) {
340: int oldSize = settings.getIconSize();
341: int newSize = (oldSize == BeanInfo.ICON_COLOR_16x16) ? BeanInfo.ICON_COLOR_32x32
342: : BeanInfo.ICON_COLOR_16x16;
343: settings.setIconSize(newSize);
344: }
345:
346: public Object getValue(String key) {
347: if (Action.NAME.equals(key)) {
348: String namePattern = getBundleString("CTL_IconSize"); // NOI18N
349: return MessageFormat.format(namePattern,
350: new Object[] { Integer.valueOf(settings
351: .getIconSize()) });
352: } else {
353: return super .getValue(key);
354: }
355: }
356: }
357:
358: /**
359: * An action to restore palette's default state.
360: */
361: static class RefreshPaletteAction extends AbstractAction {
362:
363: public RefreshPaletteAction() {
364: putValue(Action.NAME, getBundleString("CTL_RefreshPalette")); // NOI18N
365: }
366:
367: public void actionPerformed(ActionEvent event) {
368: PalettePanel.getDefault().doRefresh();
369: }
370:
371: }
372:
373: /**
374: * An action to remove a category and all items in it.
375: */
376: static class DeleteCategoryAction extends AbstractAction {
377: private Node categoryNode;
378:
379: public DeleteCategoryAction(Node categoryNode) {
380: this .categoryNode = categoryNode;
381: putValue(Action.NAME, getBundleString("CTL_DeleteCategory")); // NOI18N
382: }
383:
384: public void actionPerformed(ActionEvent event) {
385: // first user confirmation...
386: String message = MessageFormat.format(
387: getBundleString("FMT_ConfirmCategoryDelete"), // NOI18N
388: new Object[] { categoryNode.getName() });
389:
390: NotifyDescriptor desc = new NotifyDescriptor.Confirmation(
391: message,
392: getBundleString("CTL_ConfirmCategoryTitle"), // NOI18N
393: NotifyDescriptor.YES_NO_OPTION);
394:
395: if (NotifyDescriptor.YES_OPTION.equals(DialogDisplayer
396: .getDefault().notify(desc))) {
397: try {
398: categoryNode.destroy();
399: } catch (java.io.IOException e) {
400: ERR.log(Level.INFO, e.getLocalizedMessage(), e);
401: }
402: }
403: }
404:
405: public boolean isEnabled() {
406: return categoryNode.canDestroy();
407: }
408: }
409:
410: /**
411: * An action to rename a category.
412: */
413: static class RenameCategoryAction extends AbstractAction {
414: private Node categoryNode;
415:
416: public RenameCategoryAction(Node categoryNode) {
417: this .categoryNode = categoryNode;
418: putValue(Action.NAME, getBundleString("CTL_RenameCategory")); // NOI18N
419: }
420:
421: public void actionPerformed(ActionEvent event) {
422: NotifyDescriptor.InputLine desc = new NotifyDescriptor.InputLine(
423: getBundleString("CTL_NewName"), // NOI18N
424: getBundleString("CTL_Rename")); // NOI18N
425: desc.setInputText(categoryNode.getDisplayName());
426:
427: if (NotifyDescriptor.OK_OPTION.equals(DialogDisplayer
428: .getDefault().notify(desc))) {
429: String newName;
430: try {
431: newName = desc.getInputText();
432: if (!"".equals(newName)) // NOI18N
433: categoryNode.setDisplayName(newName);
434: } catch (IllegalArgumentException e) {
435: ERR.log(Level.INFO, e.getLocalizedMessage(), e);
436: }
437: }
438: }
439:
440: public boolean isEnabled() {
441: return categoryNode.canRename();
442: }
443: }
444:
445: /**
446: * An action to sort categories alphabetically.
447: */
448: static class SortItemsAction extends AbstractAction {
449: private Node categoryNode;
450:
451: public SortItemsAction(Node categoryNode) {
452: putValue(Action.NAME, getBundleString("CTL_SortItems")); // NOI18N
453: this .categoryNode = categoryNode;
454: }
455:
456: public void actionPerformed(ActionEvent event) {
457: Index order = (Index) categoryNode.getCookie(Index.class);
458: if (order != null) {
459: final Node[] nodes = categoryNode.getChildren()
460: .getNodes(DefaultModel.canBlock());
461: Arrays.sort(nodes, new Comparator<Node>() {
462: public int compare(Node n1, Node n2) {
463: return n1.getDisplayName().compareTo(
464: n2.getDisplayName());
465: }
466: });
467: int[] perm = new int[nodes.length];
468: for (int i = 0; i < perm.length; i++) {
469: perm[i] = order.indexOf(nodes[i]);
470: }
471: order.reorder(perm);
472: }
473: }
474:
475: public boolean isEnabled() {
476: return (categoryNode.getCookie(Index.class) != null);
477: }
478: }
479:
480: /**
481: * An action to create a new palette item from clipboard contents.
482: */
483: public static class PasteItemAction extends AbstractAction {
484: private Node categoryNode;
485:
486: public PasteItemAction(Node categoryNode) {
487: this .categoryNode = categoryNode;
488: putValue(Action.NAME, getBundleString("CTL_Paste")); // NOI18N
489: }
490:
491: public void actionPerformed(ActionEvent event) {
492: PasteType type = getPasteType();
493: if (type != null) {
494: try {
495: Transferable trans = type.paste();
496: if (trans != null) {
497: ClipboardOwner owner = trans instanceof ClipboardOwner ? (ClipboardOwner) trans
498: : new StringSelection(""); // NOI18N
499: Clipboard clipboard = (Clipboard) Lookup
500: .getDefault().lookup(ExClipboard.class);
501: clipboard.setContents(trans, owner);
502: }
503: } catch (java.io.IOException e) {
504: ERR.log(Level.INFO, e.getLocalizedMessage(), e);
505: }
506: }
507: }
508:
509: public boolean isEnabled() {
510: return (getPasteType() != null);
511: }
512:
513: private PasteType getPasteType() {
514: Clipboard clipboard = (Clipboard) Lookup.getDefault()
515: .lookup(ExClipboard.class);
516: Transferable trans = clipboard.getContents(this );
517: if (trans != null) {
518: PasteType[] pasteTypes = categoryNode
519: .getPasteTypes(trans);
520: if (pasteTypes != null && pasteTypes.length != 0)
521: return pasteTypes[0];
522: }
523: return null;
524: }
525:
526: }
527:
528: /**
529: * An action to cut a palette item to clipboard.
530: */
531: public static class CutItemAction extends AbstractAction {
532: private Node itemNode;
533:
534: public CutItemAction(Node itemNode) {
535: this .itemNode = itemNode;
536: putValue(Action.NAME, getBundleString("CTL_Cut")); // NOI18N
537: }
538:
539: public void actionPerformed(ActionEvent event) {
540: try {
541: Transferable trans = itemNode.clipboardCut();
542: if (trans != null) {
543: Clipboard clipboard = (Clipboard) Lookup
544: .getDefault().lookup(ExClipboard.class);
545: clipboard.setContents(trans,
546: new StringSelection("")); // NOI18N
547: }
548: } catch (java.io.IOException e) {
549: ERR.log(Level.INFO, e.getLocalizedMessage(), e);
550: }
551: }
552:
553: public boolean isEnabled() {
554: return itemNode.canCut();
555: }
556: }
557:
558: /**
559: * An action to copy palette item to clipboard.
560: */
561: public static class CopyItemAction extends AbstractAction {
562: private Node itemNode;
563:
564: public CopyItemAction(Node itemNode) {
565: this .itemNode = itemNode;
566: putValue(Action.NAME, getBundleString("CTL_Copy")); // NOI18N
567: }
568:
569: public void actionPerformed(ActionEvent event) {
570: try {
571: Transferable trans = itemNode.clipboardCopy();
572: if (trans != null) {
573: Clipboard clipboard = (Clipboard) Lookup
574: .getDefault().lookup(ExClipboard.class);
575: clipboard.setContents(trans,
576: new StringSelection("")); // NOI18N
577: }
578: } catch (java.io.IOException e) {
579: ERR.log(Level.INFO, e.getLocalizedMessage(), e);
580: }
581: }
582:
583: public boolean isEnabled() {
584: return itemNode.canCopy();
585: }
586: }
587:
588: /**
589: * An action to remove an item from palette.
590: */
591: static class RemoveItemAction extends AbstractAction {
592: private Node itemNode;
593:
594: public RemoveItemAction(Node itemNode) {
595: this .itemNode = itemNode;
596: putValue(Action.NAME, getBundleString("CTL_Delete")); // NOI18N
597: }
598:
599: public void actionPerformed(ActionEvent event) {
600: // first user confirmation...
601: String message = MessageFormat.format(
602: getBundleString("FMT_ConfirmBeanDelete"), // NOI18N
603: new Object[] { itemNode.getDisplayName() });
604:
605: NotifyDescriptor desc = new NotifyDescriptor.Confirmation(
606: message, getBundleString("CTL_ConfirmBeanTitle"), // NOI18N
607: NotifyDescriptor.YES_NO_OPTION);
608:
609: if (NotifyDescriptor.YES_OPTION.equals(DialogDisplayer
610: .getDefault().notify(desc))) {
611: try {
612: itemNode.destroy();
613: } catch (java.io.IOException e) {
614: ERR.log(Level.INFO, e.getLocalizedMessage(), e);
615: }
616: }
617: }
618:
619: public boolean isEnabled() {
620: return itemNode.canDestroy();
621: }
622: }
623:
624: /**
625: * An action to remove an item from palette.
626: */
627: private static class ShowCustomizerAction extends AbstractAction {
628: private PaletteController palette;
629:
630: public ShowCustomizerAction(PaletteController palette) {
631: this .palette = palette;
632: putValue(Action.NAME, getBundleString("CTL_ShowCustomizer")); // NOI18N
633: }
634:
635: public void actionPerformed(ActionEvent event) {
636: palette.showCustomizer();
637: }
638: }
639: }
|