//----------------------------------------------------------------------------//
// //
// A c t i o n M a n a g e r //
// //
//----------------------------------------------------------------------------//
// <editor-fold defaultstate="collapsed" desc="hdr"> //
// Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. //
// This software is released under the GNU General Public License. //
// Goto http://kenai.com/projects/audiveris to report bugs or suggestions. //
//----------------------------------------------------------------------------//
// </editor-fold>
package omr.action;
import omr.WellKnowns;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import omr.ui.MainGui;
import omr.ui.util.SeparableMenu;
import omr.ui.util.UIUtil;
import omr.util.UriUtil;
import org.jdesktop.application.ApplicationAction;
import org.jdesktop.application.ResourceMap;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.Box;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JToolBar;
import javax.xml.bind.JAXBException;
/**
* Class {@code ActionManager} handles the instantiation and dressing
* of actions, their organization in the menus and the tool bar, and
* their enabling.
*
* @author Hervé Bitteur
*/
public class ActionManager
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(ActionManager.class);
/** Class loader */
private static final ClassLoader classLoader = ActionManager.class.
getClassLoader();
/** Singleton */
private static volatile ActionManager INSTANCE;
//~ Instance fields --------------------------------------------------------
//
/** The map of all menus, so that we can directly provide some. */
private final Map<String, JMenu> menuMap = new HashMap<>();
/** Collection of actions enabled only when a sheet is selected. */
private final Collection<Action> sheetDependentActions = new ArrayList<>();
/** Collection of actions enabled only when current score is available. */
private final Collection<Action> scoreDependentActions = new ArrayList<>();
/** The tool bar that hosts some actions. */
private final JToolBar toolBar = new JToolBar();
/** The menu bar for all actions. */
private final JMenuBar menuBar = new JMenuBar();
//~ Constructors -----------------------------------------------------------
//
//---------------//
// ActionManager //
//---------------//
/**
* Meant to be instantiated at most once.
*/
private ActionManager ()
{
}
//~ Methods ----------------------------------------------------------------
//-------------//
// getInstance //
//-------------//
/**
* Report the single action manager instance.
*
* @return the unique instance of this class
*/
public static ActionManager getInstance ()
{
if (INSTANCE == null) {
INSTANCE = new ActionManager();
}
return INSTANCE;
}
//-------------------//
// getActionInstance //
//-------------------//
/**
* Retrieve an action knowing its methodName.
*
* @param instance the instance of the hosting class
* @param methodName the method name
* @return the action found, or null if none
*/
public ApplicationAction getActionInstance (Object instance,
String methodName)
{
ActionMap actionMap = MainGui.getInstance().getContext().getActionMap(
instance);
return (ApplicationAction) actionMap.get(methodName);
}
//---------//
// getMenu //
//---------//
/**
* Report the menu built for a given key.
*
* @param key the given menu key
* @return the related menu
*/
public JMenu getMenu (String key)
{
return menuMap.get(key);
}
//------------//
// getMenuBar //
//------------//
/**
* Report the bar containing all generated pull-down menus.
*
* @return the menu bar
*/
public JMenuBar getMenuBar ()
{
return menuBar;
}
//---------//
// getName //
//---------//
/**
* Report a describing name.
*
* @return a describing name
*/
public String getName ()
{
return "ActionManager";
}
//------------//
// getToolBar //
//------------//
/**
* Report the tool bar containing all generated buttons.
*
* @return the tool bar
*/
public JToolBar getToolBar ()
{
return toolBar;
}
//------------//
// injectMenu //
//------------//
/**
* Insert a predefined menu, either partly or fully built.
*
* @param key the menu unique name
* @param menu the menu to inject
*/
public void injectMenu (String key,
JMenu menu)
{
menuMap.put(key, menu);
}
//--------------------//
// loadAllDescriptors //
//--------------------//
/**
* Load all descriptors as found in system and user configuration
* files.
*/
public void loadAllDescriptors ()
{
// Load classes first for system actions, then for user actions
URI[] uris = new URI[]{
UriUtil.toURI(WellKnowns.RES_URI, "system-actions.xml"),
new File(WellKnowns.CONFIG_FOLDER, "user-actions.xml").toURI().normalize()};
for (int i = 0; i < uris.length; i++) {
URI uri = uris[i];
try {
URL url = uri.toURL();
InputStream input = url.openStream();
Actions.loadActionsFrom(input);
} catch (IOException ex) {
// Item does not exist
if (i == 0) {
// Only the first item (system) is mandatory
logger.error("Mandatory file not found {}", uri);
}
} catch (JAXBException ex) {
logger.warn("Error loading actions from " + uri, ex);
}
}
}
//--------------------//
// registerAllActions //
//--------------------//
/**
* Register all actions as listed in the descriptor files, and
* organize them according to the various domains defined.
* There is one pull-down menu generated for each domain found.
*/
public void registerAllActions ()
{
// Insert an initial separator, to let user easily grab the toolBar
toolBar.addSeparator();
for (String domain : Actions.getDomainNames()) {
// Create dedicated menu for this range, if not already existing
JMenu menu = menuMap.get(domain);
if (menu == null) {
logger.debug("Creating menu:{}", domain);
menu = new SeparableMenu(domain);
menuMap.put(domain, menu);
} else {
logger.debug("Augmenting menu:{}", domain);
}
// Proper menu decoration
ResourceMap resource = MainGui.getInstance().getContext().
getResourceMap(Actions.class);
menu.setText(domain); // As default
menu.setName(domain);
// Register all actions in the given domain
registerDomainActions(domain, menu);
resource.injectComponents(menu);
toolBar.addSeparator();
// Smart insertion of the menu into the menu bar
if (menu.getItemCount() > 0) {
if (domain.equalsIgnoreCase("help")) {
menuBar.add(Box.createHorizontalStrut(50));
}
SeparableMenu.trimSeparator(menu); // No separator at end
menuBar.add(menu);
}
}
}
//----------------//
// registerAction //
//----------------//
/**
* Allocate and dress an instance of the provided class, then
* register the action in the UI structure (menus and buttons)
* according to the action descriptor parameters.
*
* @param action the provided action class
* @return the registered and decorated instance of the action class
*/
@SuppressWarnings("unchecked")
private ApplicationAction registerAction (ActionDescriptor desc)
{
///logger.info("registerAction. " + desc);
ApplicationAction action = null;
try {
// Retrieve proper class instance
Class<?> classe = classLoader.loadClass(desc.className);
Object instance = null;
// Reuse existing instance through a 'getInstance()' method if any
try {
Method getInstance = classe.getDeclaredMethod(
"getInstance",
(Class[]) null);
if (Modifier.isStatic(getInstance.getModifiers())) {
instance = getInstance.invoke(null);
}
} catch (NoSuchMethodException ignored) {
}
if (instance == null) {
// Fall back to allocate a new class instance
///logger.warn("instantiating instance of " + classe);
instance = classe.newInstance();
}
// Retrieve the action instance
action = getActionInstance(instance, desc.methodName);
if (action != null) {
// Insertion of a button on Tool Bar?
if (desc.buttonClassName != null) {
Class<? extends AbstractButton> buttonClass =
(Class<? extends AbstractButton>) classLoader.
loadClass(desc.buttonClassName);
AbstractButton button = buttonClass.newInstance();
button.setAction(action);
toolBar.add(button);
button.setBorder(UIUtil.getToolBorder());
button.setText("");
}
} else {
logger.error("Unknown action {} in class {}",
desc.methodName, desc.className);
}
} catch (ClassNotFoundException | SecurityException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException | InstantiationException ex) {
logger.warn("Error while registering " + desc, ex);
}
return action;
}
//-----------------------//
// registerDomainActions //
//-----------------------//
@SuppressWarnings("unchecked")
private void registerDomainActions (String domain,
JMenu menu)
{
// Create all type sections for this menu
for (int section : Actions.getSections()) {
logger.debug("Starting section: {}", section);
// Use a separator between sections
menu.addSeparator();
for (ActionDescriptor desc : Actions.getAllDescriptors()) {
if (desc.domain.equalsIgnoreCase(domain)
&& (desc.section == section)) {
logger.debug("Registering {}", desc);
try {
Class<? extends JMenuItem> itemClass;
if (desc.itemClassName != null) {
itemClass = (Class<? extends JMenuItem>) classLoader.
loadClass(
desc.itemClassName);
} else {
itemClass = JMenuItem.class;
}
JMenuItem item = itemClass.newInstance();
item.setText(desc.methodName);
ApplicationAction action = registerAction(desc);
if (action != null) {
action.setSelected(action.isSelected());
item.setAction(action);
menu.add(item);
} else {
logger.warn("Could not register {}", desc);
}
} catch (ClassNotFoundException | InstantiationException |
IllegalAccessException ex) {
logger.warn("Error with " + desc.itemClassName, ex);
}
}
}
}
}
}