/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.visualvm.core.explorer; import com.sun.tools.visualvm.core.datasource.DataSource; import java.awt.Font; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.Action; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JSeparator; import org.openide.awt.Mnemonics; import org.openide.cookies.InstanceCookie; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataFolder; import org.openide.loaders.DataObject; /** * Class responsible for building explorer context menu. * * @author Jiri Sedlacek */ final class ExplorerContextMenuFactory { private static final Logger LOGGER = Logger.getLogger(ExplorerContextMenuFactory.class.getName()); private static final String SELECTION_ACTIONS_FILE = "VisualVM/ExplorerPopupSelection"; // NOI18N private static final String NOSELECTION_ACTIONS_FILE = "VisualVM/ExplorerPopupNoSelection"; // NOI18N private static ExplorerContextMenuFactory sharedInstance; /** * Returns singleton instance of ExplorerContextMenuFactory. * * @return singleton instance of ExplorerContextMenuFactory. */ static synchronized ExplorerContextMenuFactory instance() { if (sharedInstance == null) sharedInstance = new ExplorerContextMenuFactory(); return sharedInstance; } JPopupMenu createPopupMenu() { // Get actions for the node List<Action>[] actionsArray = getActions(); List<Action> defaultActions = actionsArray[0]; List<Action> actions = actionsArray[1]; // Return if there are no actions to display if (defaultActions.size() == 0 && actions.size() == 0) return null; // Create a popup menu JPopupMenu popupMenu = new JPopupMenu(); // Insert default actions boolean realDefaultAction = true; if (!defaultActions.isEmpty()) { for (Action defaultAction : defaultActions) { JMenuItem defaultItem = new DataSourceItem(defaultAction); if (realDefaultAction) { defaultItem.setFont(defaultItem.getFont().deriveFont(Font.BOLD)); realDefaultAction = false; } popupMenu.add(defaultItem); } } // Insert separator between default action and other actions if (!defaultActions.isEmpty() && !actions.isEmpty()) popupMenu.addSeparator(); // Insert other actions if (!actions.isEmpty()) { for (Action action : actions) { if (action == null) popupMenu.addSeparator(); else popupMenu.add(new DataSourceItem(action)); } } return popupMenu; } Action getDefaultActionFor(Set<DataSource> dataSources) { if (dataSources.isEmpty()) return null; List<Action> defaultActions = getActions()[0]; return defaultActions.isEmpty() ? null : defaultActions.get(0); } private List<Action>[] getActions() { if (ExplorerSupport.sharedInstance().getSelectedDataSources().isEmpty()) return getNoSelectionActions(); else return getSelectionActions(); } private List<Action>[] getSelectionActions() { // Find entrypoint into layer FileObject actionsFO = FileUtil.getConfigFile(SELECTION_ACTIONS_FILE); return getActions(actionsFO, true); } private List<Action>[] getNoSelectionActions() { // Find entrypoint into layer FileObject actionsFO = FileUtil.getConfigFile(NOSELECTION_ACTIONS_FILE); return getActions(actionsFO, false); } private List<Action>[] getActions(FileObject actionsFO, boolean allowDefaultActions) { // Init caches for default and regular context menu actions List<Action> defaultActions = new ArrayList(); List<Action> actions = new ArrayList(); if (actionsFO != null) { DataFolder actionsDF = DataFolder.findFolder(actionsFO); DataObject[] menuItems = actionsDF.getChildren(); for (DataObject menuItemDO : menuItems) { FileObject fobj = menuItemDO.getPrimaryFile(); if (fobj.isFolder()) { LOGGER.log(Level.WARNING, "Nested menus not supported for Applications context menu: " + fobj, fobj); // NOI18N } else { InstanceCookie menuItemCookie = (InstanceCookie)menuItemDO.getCookie(InstanceCookie.class); try { Object menuItem = menuItemCookie.instanceCreate(); boolean isDefaultAction = false; Object isDefaultActionObj = fobj.getAttribute("default"); // NOI18N if (isDefaultActionObj != null) try { isDefaultAction = (Boolean)isDefaultActionObj; if (!allowDefaultActions && isDefaultAction) LOGGER.log(Level.WARNING, "Default actions not supported for " + actionsFO.getPath() + ": " + menuItem, menuItem); // NOI18N } catch (Exception e) { LOGGER.log(Level.WARNING, "Cannot determine whether context menu action is default: " + isDefaultActionObj, isDefaultActionObj); // NOI18N } List<Action> actionsList = isDefaultAction ? defaultActions : actions; if (menuItem instanceof Action) { Action action = (Action)menuItem; if (action.isEnabled()) actionsList.add(action); } else if (menuItem instanceof JSeparator) { if (isDefaultAction) { LOGGER.log(Level.WARNING, "Separator cannot be added to default actions " + menuItem, menuItem); // NOI18N } else { actionsList.add(null); } } else { LOGGER.log(Level.WARNING, "Unsupported context menu item: " + menuItem, menuItem); // NOI18N } } catch (Exception ex) { LOGGER.log(Level.SEVERE, "Unable to resolve context menu action: " + menuItemDO, menuItemDO); // NOI18N } } } } // Return actions return new List[] { cleanupActions(defaultActions), cleanupActions(actions) }; } private List<Action> cleanupActions(List<Action> actions) { boolean leadingNull = true; Action lastAction = null; List<Action> cleanActions = new ArrayList(); for (Action action : actions) { if (action == null) { if (!leadingNull && lastAction != null) cleanActions.add(null); } else { cleanActions.add(action); leadingNull = false; } lastAction = action; } if (!cleanActions.isEmpty()) { int lastItemIndex = cleanActions.size() - 1; Action lastCleanAction = cleanActions.get(lastItemIndex); if (lastCleanAction == null) cleanActions.remove(lastItemIndex); } return cleanActions; } private static class DataSourceItem extends JMenuItem { public DataSourceItem(Action action) { super(action); setIcon(null); setToolTipText(null); String name = (String)action.getValue(Action.NAME); if (name != null) Mnemonics.setLocalizedText(this, name); // NOI18N } } }