/*
* Freeplane - mind map editor
* Copyright (C) 2008 Dimitry Polivaev
*
* This file author is Dimitry Polivaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.freeplane.core.ui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.tree.DefaultMutableTreeNode;
import org.freeplane.core.io.IElementHandler;
import org.freeplane.core.io.ReadManager;
import org.freeplane.core.io.xml.TreeXmlReader;
import org.freeplane.core.resources.ResourceController;
import org.freeplane.core.resources.SetBooleanPropertyAction;
import org.freeplane.core.ui.components.JAutoCheckBoxMenuItem;
import org.freeplane.core.ui.components.JAutoRadioButtonMenuItem;
import org.freeplane.core.ui.components.JAutoToggleButton;
import org.freeplane.core.ui.components.JFreeplaneMenuItem;
import org.freeplane.core.ui.components.UITools;
import org.freeplane.core.util.Compat;
import org.freeplane.core.util.FileUtils;
import org.freeplane.core.util.LogUtils;
import org.freeplane.core.util.TextUtils;
import org.freeplane.features.map.MapModel;
import org.freeplane.features.mode.Controller;
import org.freeplane.features.mode.ModeController;
import org.freeplane.n3.nanoxml.XMLElement;
import org.pushingpixels.flamingo.api.common.AsynchronousLoadListener;
import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon;
public class MenuBuilder extends UIBuilder implements IKeyStrokeProcessor {
//RIBBONS old MenuBuilder
private static class ActionHolder implements INameMnemonicHolder {
final private Action action;
public ActionHolder(final Action action) {
super();
this.action = action;
}
/*
* (non-Javadoc)
* @see freeplane.main.Tools.IAbstractButton#getText()
*/
public String getText() {
return (String) action.getValue(Action.NAME);
}
/*
* (non-Javadoc)
* @see
* freeplane.main.Tools.IAbstractButton#setDisplayedMnemonicIndex(int)
*/
public void setDisplayedMnemonicIndex(final int mnemoSignIndex) {
action.putValue("SwingDisplayedMnemonicIndexKey", mnemoSignIndex);
}
/*
* (non-Javadoc)
* @see freeplane.main.Tools.IAbstractButton#setMnemonic(char)
*/
public void setMnemonic(final char charAfterMnemoSign) {
int vk = charAfterMnemoSign;
if (vk >= 'a' && vk <= 'z') {
vk -= ('a' - 'A');
}
action.putValue(Action.MNEMONIC_KEY, new Integer(vk));
}
/*
* (non-Javadoc)
* @see freeplane.main.Tools.IAbstractButton#setText(java.lang.String)
*/
public void setText(final String text) {
action.putValue(Action.NAME, text);
}
}
public static class ButtonHolder implements INameMnemonicHolder {
final private AbstractButton btn;
public ButtonHolder(final AbstractButton btn) {
super();
this.btn = btn;
}
/*
* (non-Javadoc)
* @see freeplane.main.Tools.IAbstractButton#getText()
*/
public String getText() {
return btn.getText();
}
/*
* (non-Javadoc)
* @see
* freeplane.main.Tools.IAbstractButton#setDisplayedMnemonicIndex(int)
*/
public void setDisplayedMnemonicIndex(final int mnemoSignIndex) {
btn.setDisplayedMnemonicIndex(mnemoSignIndex);
}
/*
* (non-Javadoc)
* @see freeplane.main.Tools.IAbstractButton#setMnemonic(char)
*/
public void setMnemonic(final char charAfterMnemoSign) {
btn.setMnemonic(charAfterMnemoSign);
}
/*
* (non-Javadoc)
* @see freeplane.main.Tools.IAbstractButton#setText(java.lang.String)
*/
public void setText(final String text) {
btn.setText(text);
}
}
static private class DelegatingPopupMenuListener implements PopupMenuListener {
final private PopupMenuListener listener;
final private Object source;
public DelegatingPopupMenuListener(final PopupMenuListener listener, final Object source) {
super();
this.listener = listener;
this.source = source;
}
public Object getSource() {
return source;
}
private PopupMenuEvent newEvent() {
return new PopupMenuEvent(source);
}
public void popupMenuCanceled(final PopupMenuEvent e) {
listener.popupMenuCanceled(newEvent());
}
public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) {
listener.popupMenuWillBecomeInvisible(newEvent());
}
public void popupMenuWillBecomeVisible(final PopupMenuEvent e) {
listener.popupMenuWillBecomeVisible(newEvent());
}
}
static private class Enabler implements PropertyChangeListener {
final private WeakReference<Component> comp;
public Enabler(final Component comp) {
this.comp = new WeakReference<Component>(comp);
}
public void propertyChange(final PropertyChangeEvent evt) {
final Component component = comp.get();
if (component == null) {
final Action action = (Action) evt.getSource();
action.removePropertyChangeListener(this);
}
else if (evt.getPropertyName().equals("enabled")) {
final Action action = (Action) evt.getSource();
component.setEnabled(action.isEnabled());
}
}
}
interface INameMnemonicHolder {
/**
*/
String getText();
/**
*/
void setDisplayedMnemonicIndex(int mnemoSignIndex);
/**
*/
void setMnemonic(char charAfterMnemoSign);
/**
*/
void setText(String replaceAll);
}
private static class MenuPath {
static MenuPath emptyPath() {
final MenuPath menuPath = new MenuPath("");
menuPath.key = "";
return menuPath;
}
String parentKey;
String key;
MenuPath(final String key) {
parentKey = key;
}
void setKey(final String name) {
key = name;
}
void setLastKeySection(final String name) {
key = parentKey + '/' + name;
}
@Override
public String toString() {
return key;
}
}
//RIBBONS MenuStructureReader --> xml reader
private class MenuStructureReader {
private final class ActionCreator implements IElementHandler {
public Object createElement(final Object parent, final String tag, final XMLElement attributes) {
if (attributes == null) {
return null;
}
final MenuPath menuPath = new MenuPath(parent.toString());
final String action = attributes.getAttribute("action", null);
final String plugin = attributes.getAttribute("plugin", null);
if(plugin != null && ! plugins.contains(plugin))
return null;
try {
AFreeplaneAction theAction = modeController.getAction(action);
if (theAction == null) {
if(action.startsWith("SetBooleanPropertyAction.")){
String propertyName = action.substring("SetBooleanPropertyAction.".length());
theAction = new SetBooleanPropertyAction(propertyName);
modeController.addAction(theAction);
}
else{
LogUtils.severe("action " + action + " not found");
return null;
}
}
String menuKey = attributes.getAttribute("menu_key", null);
if(menuKey == null)
menuKey = getMenuKey(menuPath.parentKey, theAction.getKey());
menuPath.setKey(menuKey);
String accelerator = attributes.getAttribute("accelerator", null);
if (accelerator != null) {
if (Compat.isMacOsX()) {
accelerator = accelerator.replaceFirst("CONTROL", "META").replaceFirst("control", "meta");
}
setDefaultAccelerator(menuPath.key, accelerator);
}
if (tag.equals("menu_radio_action")) {
final JRadioButtonMenuItem item = (JRadioButtonMenuItem)
addRadioItem(menuPath.parentKey, menuPath.key, theAction, "true".equals(attributes.getAttribute("selected", "false")));
if (buttonGroup == null) {
buttonGroup = new ButtonGroup();
}
buttonGroup.add(item);
}
else {
addAction(menuPath.parentKey, menuPath.key, theAction, MenuBuilder.AS_CHILD);
}
}
catch (final Exception e) {
LogUtils.severe(e);
}
return menuPath;
}
}
private final class CategoryCreator implements IElementHandler {
public Object createElement(final Object parent, final String tag, final XMLElement attributes) {
if (attributes == null) {
return null;
}
final String plugin = attributes.getAttribute("plugin", null);
if(plugin != null && ! plugins.contains(plugin))
return null;
buttonGroup = null;
final MenuPath menuPath = new MenuPath(parent.toString());
final String menuKey = attributes.getAttribute("menu_key", null);
if(menuKey == null)
menuPath.setLastKeySection(attributes.getAttribute("name", null));
else
menuPath.setKey(menuKey);
if (!contains(menuPath.key)) {
if (tag.equals("menu_submenu")) {
final JMenu menuItem = new JMenu();
String nameRef = attributes.getAttribute("name_ref", null);
if(nameRef == null)
nameRef = attributes.getAttribute("name", null);
final String iconResource = ResourceController.getResourceController().getProperty(nameRef + ".icon", null);
MenuBuilder.setLabelAndMnemonic(menuItem, TextUtils.getRawText(nameRef));
if(iconResource != null){
final URL url = ResourceController.getResourceController().getResource(iconResource);
menuItem.setIcon(new ImageIcon(url));
}
addMenuItem(menuPath.parentKey, menuItem, menuPath.key, MenuBuilder.AS_CHILD);
}
else {
if (!(menuPath.parentKey.equals(""))) {
addMenuItemGroup(menuPath.parentKey, menuPath.key, MenuBuilder.AS_CHILD);
}
}
}
return menuPath;
}
}
private final class SeparatorCreator implements IElementHandler {
public Object createElement(final Object parent, final String tag, final XMLElement attributes) {
addSeparator(parent.toString(), MenuBuilder.AS_CHILD);
return parent;
}
}
private final class StructureCreator implements IElementHandler {
public Object createElement(final Object parent, final String tag, final XMLElement attributes) {
return MenuPath.emptyPath();
}
}
private ButtonGroup buttonGroup;
final private ReadManager readManager;
MenuStructureReader() {
readManager = new ReadManager();
readManager.addElementHandler("menu_structure", new StructureCreator());
readManager.addElementHandler("menu_category", new CategoryCreator());
readManager.addElementHandler("menu_submenu", new CategoryCreator());
readManager.addElementHandler("menu_action", new ActionCreator());
readManager.addElementHandler("menu_radio_action", new ActionCreator());
readManager.addElementHandler("menu_separator", new SeparatorCreator());
}
public void processMenu(final URL menu) {
InputStreamReader streamReader = null;
try {
streamReader = new InputStreamReader(new BufferedInputStream(menu.openStream()));
final TreeXmlReader reader = new TreeXmlReader(readManager);
reader.load(streamReader);
}
catch (final Exception e) {
throw new RuntimeException(e);
}
finally {
FileUtils.silentlyClose(streamReader);
}
}
}
private static Insets nullInsets = new Insets(0, 0, 0, 0);
private static final String SHORTCUT_PROPERTY_PREFIX = "acceleratorFor";
static final int MAX_HEIGHT = Toolkit.getDefaultToolkit().getScreenSize().height - 100;
static public JMenu createMenu(final String name) {
final JMenu menu = new JMenu();
final String text = TextUtils.getRawText(name);
MenuBuilder.setLabelAndMnemonic(menu, text);
return menu;
}
static public JMenuItem createMenuItem(final String name) {
final JMenuItem menu = new JFreeplaneMenuItem();
final String text = TextUtils.getRawText(name);
MenuBuilder.setLabelAndMnemonic(menu, text);
return menu;
}
public static void loadAcceleratorPresets(final InputStream in) {
final Properties prop = new Properties();
try {
prop.load(in);
for (final Entry<Object, Object> property : prop.entrySet()) {
final String shortcutKey = (String) property.getKey();
final String keystrokeString = (String) property.getValue();
if (!shortcutKey.startsWith(SHORTCUT_PROPERTY_PREFIX)) {
LogUtils.warn("wrong property key " + shortcutKey);
continue;
}
final int pos = shortcutKey.indexOf("/", SHORTCUT_PROPERTY_PREFIX.length());
if (pos <= 0) {
LogUtils.warn("wrong property key " + shortcutKey);
continue;
}
final String modeName = shortcutKey.substring(SHORTCUT_PROPERTY_PREFIX.length(), pos);
final String itemKey = shortcutKey.substring(pos + 1);
Controller controller = Controller.getCurrentController();
final ModeController modeController = controller.getModeController(modeName);
if (modeController == null) {
LogUtils.warn("unknown mode name in " + shortcutKey);
continue;
}
final MenuBuilder menuBuilder = modeController.getUserInputListenerFactory().getMenuBuilder();
final Node node = (Node) menuBuilder.get(itemKey);
if (node == null) {
LogUtils.warn("wrong key in " + shortcutKey);
continue;
}
final Object obj = node.getUserObject();
if (!(obj instanceof JMenuItem)) {
LogUtils.warn("wrong key in " + shortcutKey);
continue;
}
final KeyStroke keyStroke;
if (!keystrokeString.equals("")) {
keyStroke = UITools.getKeyStroke(keystrokeString);
final Node oldNode = menuBuilder.getMenuItemForKeystroke(keyStroke);
if (oldNode != null) {
menuBuilder.setAccelerator(oldNode, null);
final Object key = oldNode.getKey();
final String oldShortcutKey = menuBuilder.getShortcutKey(key.toString());
ResourceController.getResourceController().setProperty(oldShortcutKey, "");
}
}
else {
keyStroke = null;
}
menuBuilder.setAccelerator(node, keyStroke);
ResourceController.getResourceController().setProperty(shortcutKey, keystrokeString);
}
}
catch (final IOException e) {
e.printStackTrace();
}
}
/**
* Ampersand indicates that the character after it is a mnemo, unless the
* character is a space. In "Find & Replace", ampersand does not label
* mnemo, while in "&About", mnemo is "Alt + A".
*/
public static void setLabelAndMnemonic(final AbstractButton btn, final String inLabel) {
MenuBuilder.setLabelAndMnemonic(new ButtonHolder(btn), inLabel);
}
/**
* Ampersand indicates that the character after it is a mnemo, unless the
* character is a space. In "Find & Replace", ampersand does not label
* mnemo, while in "&About", mnemo is "Alt + A".
*/
public static void setLabelAndMnemonic(final Action action, final String inLabel) {
MenuBuilder.setLabelAndMnemonic(new ActionHolder(action), inLabel);
}
private static void setLabelAndMnemonic(final INameMnemonicHolder item, final String inLabel) {
String rawLabel = inLabel;
if (rawLabel == null) {
rawLabel = item.getText();
}
if (rawLabel == null) {
return;
}
item.setText(TextUtils.removeMnemonic(rawLabel));
final int mnemoSignIndex = rawLabel.indexOf('&');
if (mnemoSignIndex >= 0 && mnemoSignIndex + 1 < rawLabel.length()) {
final char charAfterMnemoSign = rawLabel.charAt(mnemoSignIndex + 1);
if (charAfterMnemoSign != ' ') {
if (!Compat.isMacOsX()) {
item.setMnemonic(charAfterMnemoSign);
item.setDisplayedMnemonicIndex(mnemoSignIndex);
}
}
}
}
private IAcceleratorChangeListener acceleratorChangeListener;
private final Map<KeyStroke, Node> accelerators = new HashMap<KeyStroke, Node>();
final private ModeController modeController;
final MenuStructureReader reader;
private Set<String> plugins;
public MenuBuilder(ModeController modeController) {
super(null);
this.modeController = modeController;
reader = new MenuStructureReader();
}
private void setDefaultAccelerator(final String itemKey, final String accelerator) {
final String shortcutKey = getShortcutKey(itemKey);
if (null == ResourceController.getResourceController().getProperty(shortcutKey, null)) {
ResourceController.getResourceController().setDefaultProperty(shortcutKey, accelerator);
}
}
/**
* @return returns the new JMenuItem.
*/
public void addAction(final String category, final AFreeplaneAction action, final int position) {
final String menuKey = getMenuKey(category, action.getKey());
addAction(category, menuKey, action, position);
}
public String getMenuKey(final String category, String actionKey) {
actionKey = "$" + actionKey + '$';
for (int i = 0; i < 1000; i++) {
final String key = actionKey + i;
if (null == get(key)) {
return key;
}
}
return category + '/' + actionKey;
}
public void addAction(final String category, final String key, final AFreeplaneAction action, final int position) {
assert action != null;
assert key != null;
if (getContainer(get(category), Container.class) instanceof JToolBar) {
addButton(category, action, key, position);
return;
}
final JMenuItem item;
if (action.getClass().getAnnotation(SelectableAction.class) != null) {
item = new JAutoCheckBoxMenuItem(decorateAction(category, action));
}
else {
item = new JFreeplaneMenuItem(decorateAction(category, action));
}
addMenuItem(category, item, key, position);
addListeners(key, action);
return;
}
private void addListeners(final String key, final AFreeplaneAction action) {
if (AFreeplaneAction.checkSelectionOnPopup(action)) {
addPopupMenuListener(key, new PopupMenuListener() {
public void popupMenuCanceled(final PopupMenuEvent e) {
}
public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) {
}
public void popupMenuWillBecomeVisible(final PopupMenuEvent e) {
if(action.isEnabled())
action.setSelected();
}
});
}
}
private void addButton(final String category, final Action action, final String key, final int position) {
final AbstractButton button;
assert action != null;
if (action.getClass().getAnnotation(SelectableAction.class) != null) {
button = new JAutoToggleButton(action);
}
else {
button = new JButton(action);
}
addElement(category, button, key, position);
}
@Override
protected void addComponent(final Container container, final Component component, final int index) {
if (container instanceof JMenu) {
new MenuSplitter().addMenuComponent((JMenu) container, component, index);
return;
}
if (container instanceof JToolBar && component instanceof AbstractButton) {
{
((AbstractButton) component).setMargin(MenuBuilder.nullInsets);
}
}
super.addComponent(container, component, index);
}
public void addComponent(final String parent, final Container item, final Action action, final int position) {
action.addPropertyChangeListener(new Enabler(item));
addElement(parent, item, position);
}
/**
* Add new first level menu bar.
*/
public void addMenuBar(final JMenuBar menubar, final String key) {
addElement(this, menubar, key, UIBuilder.AS_CHILD);
}
public void addMenuItem(final String relativeKey, final JMenuItem item, final String key, final int position) {
final String shortcutKey = getShortcutKey(key);
final String keyStrokeString = ResourceController.getResourceController().getProperty(shortcutKey);
//RIBBONS - to set the right icon size
if(item.getIcon() != null && item.getIcon() instanceof ImageIcon) {
ImageIcon ico = (ImageIcon)item.getIcon();
ImageWrapperResizableIcon nuIco = ImageWrapperResizableIcon.getIcon(ico.getImage(), new Dimension(ico.getIconWidth(), ico.getIconHeight()));
nuIco.setPreferredSize(new Dimension(16, 16));
nuIco.addAsynchronousLoadListener(new AsynchronousLoadListener() {
public void completed(boolean success) {
item.repaint();
}
});
item.setIcon(nuIco);
}
final Node element = (Node) addElement(relativeKey, item, key, position);
if (null == getMenuBar(element)) {
return;
}
if (keyStrokeString != null && !keyStrokeString.equals("")) {
final KeyStroke keyStroke = UITools.getKeyStroke(keyStrokeString);
setAccelerator(element, keyStroke);
}
}
public void addMenuItemGroup(final String key, final int position) {
addElement(this, key, key, position);
}
public void addMenuItemGroup(final String relativeKey, final String key, final int position) {
addElement(relativeKey, key, key, position);
}
public void addPopupMenu(final JPopupMenu menu, final String key) {
addElement(this, menu, key, UIBuilder.AS_CHILD);
}
public void addPopupMenuListener(final Object key, final PopupMenuListener listener) {
final DefaultMutableTreeNode node = get(key);
assert (node != null);
final JPopupMenu popup;
if (node.getUserObject() instanceof JMenu) {
popup = ((JMenu) node.getUserObject()).getPopupMenu();
}
else if (node.getUserObject() instanceof JPopupMenu) {
popup = (JPopupMenu) node.getUserObject();
}
else {
final Container container = getContainer(((DefaultMutableTreeNode) node.getParent()), Container.class);
if (container instanceof JPopupMenu) {
popup = (JPopupMenu) container;
}
else if (container instanceof JMenu) {
popup = ((JMenu) container).getPopupMenu();
}
else {
throw new RuntimeException("no popup menu found!");
}
}
final Object userObject = node.getUserObject();
popup.addPopupMenuListener(new DelegatingPopupMenuListener(listener, userObject));
}
public JMenuItem addRadioItem(final String category, final AFreeplaneAction action, final boolean isSelected) {
assert action != null;
final String actionKey = "$" + action.getKey() + '$';
for(int i = 0; i < 1000; i++){
String key = actionKey + i;
if (null == get(key)){
return addRadioItem(category, key, action, isSelected);
}
}
return addRadioItem(category, category + '/' + actionKey, action, isSelected);
}
public JMenuItem addRadioItem(final String category, final String key,
final AFreeplaneAction action, final boolean isSelected) {
assert key != null;
final JRadioButtonMenuItem item;
if (action.getClass().getAnnotation(SelectableAction.class) != null) {
item = new JAutoRadioButtonMenuItem(decorateAction(category, action));
}
else {
item = new JRadioButtonMenuItem(decorateAction(category, action));
}
addMenuItem(category, item, key, MenuBuilder.AS_CHILD);
item.setSelected(isSelected);
addListeners(key, action);
return item;
}
public JMenuItem addCheckboxItem(final String category, final AFreeplaneAction action, final boolean isSelected) {
assert action != null;
final String actionKey = "$" + action.getKey() + '$';
for(int i = 0; i < 1000; i++){
String key = actionKey + i;
if (null == get(key)){
return addCheckboxItem(category, key, action, isSelected);
}
}
return addCheckboxItem(category, category + '/' + actionKey, action, isSelected);
}
public JMenuItem addCheckboxItem(final String category, final String key,
final AFreeplaneAction action, final boolean isSelected) {
assert key != null;
final JCheckBoxMenuItem item;
if (action.getClass().getAnnotation(SelectableAction.class) != null) {
item = new JAutoCheckBoxMenuItem(decorateAction(category, action));
}
else {
item = new JCheckBoxMenuItem(decorateAction(category, action));
}
addMenuItem(category, item, key, MenuBuilder.AS_CHILD);
item.setSelected(isSelected);
addListeners(key, action);
return item;
}
public void addSeparator() {
addElement(this, new JPopupMenu.Separator(), UIBuilder.AS_CHILD);
}
public void addSeparator(final String parentKey, final int position) {
final Container parent = getContainer(get(parentKey), Container.class);
if (parent instanceof JMenu || parent instanceof JPopupMenu) {
addElement(parentKey, new JPopupMenu.Separator(), position);
return;
}
if (parent instanceof JToolBar) {
final JToolBar t = (JToolBar) parent;
final JToolBar.Separator s = new JToolBar.Separator();
addElement(parentKey, s, position);
if (t.getOrientation() == SwingConstants.VERTICAL) {
s.setOrientation(SwingConstants.HORIZONTAL);
}
else {
s.setOrientation(SwingConstants.VERTICAL);
}
return;
}
}
public void addToolbar(final JToolBar toolbar, final String key) {
addElement(this, toolbar, key, UIBuilder.AS_CHILD);
}
public void afterMapChange(final MapModel newMap) {
final Iterator<Object> iterator = newObjectIterator();
while (iterator.hasNext()) {
final Object next = iterator.next();
if (next instanceof AbstractButton) {
final AbstractButton btn = (AbstractButton) next;
final Action action = btn.getAction();
if (action instanceof IFreeplaneAction) {
((IFreeplaneAction) action).afterMapChange(newMap);
}
}
}
}
IFreeplaneAction decorateAction(final String category, final AFreeplaneAction action) {
if (null == getMenuBar(get(category)) || Controller.getCurrentController().getViewController().isApplet()) {
return action;
}
return decorateAction(action);
}
public IFreeplaneAction decorateAction(final AFreeplaneAction action) {
return new AccelerateableAction(this, action);
}
public IAcceleratorChangeListener getAcceleratorChangeListener() {
return acceleratorChangeListener;
}
@Override
protected Component getChildComponent(final Container parentComponent, final int index) {
if (parentComponent instanceof JMenu) {
return ((JMenu) parentComponent).getMenuComponent(index);
}
return super.getChildComponent(parentComponent, index);
}
@Override
protected Container getNextParentComponent(Container parentComponent) {
if(parentComponent.getComponentCount() > 0 && parentComponent instanceof JMenu)
{
return new MenuSplitter().getExtraSubMenu((JMenu)parentComponent);
}
return null;
}
public Node getMenuBar(DefaultMutableTreeNode element) {
while (element != null) {
final Object userObject = element.getUserObject();
if (userObject instanceof JMenuBar) {
return (Node) element;
}
element = (DefaultMutableTreeNode) element.getParent();
}
return null;
}
private Node getMenuItemForKeystroke(final KeyStroke keyStroke) {
return accelerators.get(keyStroke);
}
@Override
protected DefaultMutableTreeNode getNode(final Object parentKey) {
final DefaultMutableTreeNode parentNode = super.getNode(parentKey);
return parentNode;
}
@Override
protected int getParentComponentCount(final Container parentComponent) {
if (parentComponent instanceof JMenu) {
return ((JMenu) parentComponent).getMenuComponentCount();
}
return super.getParentComponentCount(parentComponent);
}
public String getShortcutKey(final String key) {
return SHORTCUT_PROPERTY_PREFIX + modeController.getModeName() + "/" + key;
}
public void processMenuCategory(final URL menu, Set<String> plugins) {
final Set<String> oldPlugins = this.plugins;
this.plugins = plugins;
try{
reader.processMenu(menu);
}
finally{
this.plugins = oldPlugins;
}
}
private KeyStroke removeAccelerator(final Node node) throws AssertionError {
final KeyStroke oldAccelerator = ((JMenuItem) node.getUserObject()).getAccelerator();
if (oldAccelerator != null) {
final Node oldNode = accelerators.remove(oldAccelerator);
if (!node.equals(oldNode)) {
throw new AssertionError("unexpected action " + "for accelerator " + oldAccelerator);
}
}
return oldAccelerator;
}
@SuppressWarnings("unchecked")
private void removeAccelerators(final DefaultMutableTreeNode node) {
final Object userObject = node.getUserObject();
if (userObject instanceof JMenuItem && !(userObject instanceof JMenu)) {
setAccelerator((Node) node, null);
}
for (final Enumeration<Object> children = node.children(); children.hasMoreElements();) {
removeAccelerators((DefaultMutableTreeNode) children.nextElement());
}
}
@Override
protected void removeChildComponents(final Container parentComponent, final DefaultMutableTreeNode node) {
removeAccelerators(node);
if (parentComponent instanceof JMenu) {
final JMenu menu = (JMenu) parentComponent;
final JPopupMenu popupMenu = menu.getPopupMenu();
super.removeChildComponents(popupMenu, node);
for(int i = popupMenu.getComponentCount()-1; i >= 0; i--){
final Component component = popupMenu.getComponent(i);
if(new MenuSplitter().isExtraSubMenu(component)){
final Container container = (Container) component;
super.removeChildComponents(container, node);
if(container.getComponentCount() == 0)
popupMenu.remove(container);
}
}
}
else{
super.removeChildComponents(parentComponent, node);
}
}
public void removePopupMenuListener(final Object key, final PopupMenuListener listener) {
final DefaultMutableTreeNode node = get(key);
final Container container = getContainer(node, Container.class);
final JPopupMenu popup;
if (container instanceof JPopupMenu) {
popup = (JPopupMenu) container;
}
else if (container instanceof JMenu) {
popup = ((JMenu) container).getPopupMenu();
}
else {
throw new RuntimeException("no popup menu found!");
}
final Object userObject = node.getUserObject();
final PopupMenuListener[] popupMenuListeners = popup.getPopupMenuListeners();
for (int i = 0; i < popupMenuListeners.length; i++) {
final PopupMenuListener popupMenuListener = popupMenuListeners[i];
if (!(popupMenuListener instanceof DelegatingPopupMenuListener)
|| !(((DelegatingPopupMenuListener) popupMenuListener).getSource() == userObject)) {
continue;
}
popup.removePopupMenuListener(popupMenuListener);
break;
}
}
void setAccelerator(final Node node, final KeyStroke keyStroke) {
final Node oldAction = accelerators.put(keyStroke, node);
final JMenuItem item = (JMenuItem) node.getUserObject();
if (keyStroke != null && oldAction != null) {
UITools.errorMessage(TextUtils.format("action_keystroke_in_use_error", keyStroke, item
.getActionCommand(), ((JMenuItem) oldAction.getUserObject()).getActionCommand()));
accelerators.put(keyStroke, oldAction);
final String shortcutKey = getShortcutKey(node.getKey().toString());
ResourceController.getResourceController().setProperty(shortcutKey, "");
return;
}
if (item instanceof JMenu) {
UITools.errorMessage(TextUtils.format("submenu_keystroke_in_use_error", keyStroke, item.getText()));
accelerators.put(keyStroke, oldAction);
final String shortcutKey = getShortcutKey(node.getKey().toString());
ResourceController.getResourceController().setProperty(shortcutKey, "");
return;
}
final KeyStroke removedAccelerator = removeAccelerator(node);
item.setAccelerator(keyStroke);
if (acceleratorChangeListener != null && (removedAccelerator != null || keyStroke != null)) {
acceleratorChangeListener.acceleratorChanged(item, removedAccelerator, keyStroke);
}
}
public void setAcceleratorChangeListener(final IAcceleratorChangeListener acceleratorChangeListener) {
this.acceleratorChangeListener = acceleratorChangeListener;
}
public Map<KeyStroke, Node> getAcceleratorMap() {
return Collections.unmodifiableMap(accelerators);
}
public boolean processKeyBinding(KeyStroke ks, KeyEvent event, int condition, boolean pressed, boolean consumed) {
if (!consumed && condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
Node node = accelerators.get(ks);
if(node != null) {
JFreeplaneMenuItem item = (JFreeplaneMenuItem) node.getUserObject();
Action action = item.getAction();
if(action != null && SwingUtilities.notifyAction(action, ks, event, event.getComponent(), event.getModifiers())) {
return true;
}
}
}
return false;
}
}