/*FreeMind - A Program for creating and viewing Mindmaps *Copyright (C) 2000-2004 Joerg Mueller, Daniel Polansky, Christian Foltin and others. * *See COPYING for Details * *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, write to the Free Software *Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Created on 21.05.2004 */ /*$Id: StructuredMenuHolder.java,v 1.1.4.7.4.11 2010/09/30 22:38:47 christianfoltin Exp $*/ package freemind.controller; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import java.util.Vector; import javax.swing.Action; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JCheckBoxMenuItem; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.JToolBar; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import freemind.main.HtmlTools; import freemind.main.Resources; import freemind.main.Tools; /** * @author foltin * */ public class StructuredMenuHolder { /** * */ public static final String AMOUNT_OF_VISIBLE_MENU_ITEMS = "AMOUNT_OF_VISIBLE_MENU_ITEMS"; public static final int ICON_SIZE = 16; private String mOutputString; private static Icon blindIcon = new BlindIcon(ICON_SIZE); private static final String SELECTED_ICON_PATH = "images/button_ok.png"; private static final String SEPARATOR_TEXT = "000"; private static final String ORDER_NAME = "/order"; Map menuMap; private static java.util.logging.Logger logger = null; private int mIndent; private static ImageIcon sSelectedIcon; public StructuredMenuHolder() { if (logger == null) { logger = freemind.main.Resources.getInstance().getLogger( this.getClass().getName()); } menuMap = new HashMap(); Vector order = new Vector(); menuMap.put(ORDER_NAME, order); if (sSelectedIcon == null) { sSelectedIcon = new ImageIcon(Resources.getInstance().getResource( SELECTED_ICON_PATH)); } } /** */ public JMenu addMenu(JMenu item, String category) { StringTokenizer tokens = new StringTokenizer(category, "/"); return (JMenu) addMenu(item, tokens); } /** */ public JMenuItem addMenuItem(JMenuItem item, String category) { StringTokenizer tokens = new StringTokenizer(category, "/"); StructuredMenuItemHolder holder = new StructuredMenuItemHolder(); holder.setAction(item.getAction()); holder.setMenuItem(item); adjustTooltips(holder); addMenu(holder, tokens); return item; } /** * @param item is an action. If it derives from MenuItemSelectedListener, * a check box is used. */ public JMenuItem addAction(Action item, String category) { StringTokenizer tokens = new StringTokenizer(category, "/"); StructuredMenuItemHolder holder = new StructuredMenuItemHolder(); holder.setAction(item); /* * Dimitry, Eric and Dan requested to have the check marks with the * original JCheckBoxMenuItem. */ if (item instanceof MenuItemSelectedListener) { holder.setMenuItem(new JCheckBoxMenuItem(item)); } else { holder.setMenuItem(new JMenuItem(item)); } adjustTooltips(holder); addMenu(holder, tokens); return holder.getMenuItem(); } /** * Under Mac, no HTML is rendered for menus. * * @param holder */ private void adjustTooltips(StructuredMenuItemHolder holder) { if (Tools.isMacOsX()) { // remove html tags from tooltips: String toolTipText = holder.getMenuItem().getToolTipText(); if (toolTipText != null) { String toolTipTextWithoutTags = HtmlTools .removeHtmlTagsFromString(toolTipText); logger.finest("Old tool tip: " + toolTipText + ", New tool tip: " + toolTipTextWithoutTags); holder.getMenuItem().setToolTipText(toolTipTextWithoutTags); } } } public void addCategory(String category) { StringTokenizer tokens = new StringTokenizer(category + "/blank", "/"); // with this call, the category is created. MapTokenPair categoryPair = getCategoryMap(tokens, menuMap); } public void addSeparator(String category) { String sep = category; if (!sep.endsWith("/")) { sep += "/"; } sep += SEPARATOR_TEXT; StringTokenizer tokens = new StringTokenizer(sep, "/"); // separators can occur as doubles. MapTokenPair categoryPair = getCategoryMap(tokens, menuMap); // add an separator categoryPair.map.put(categoryPair.token, new SeparatorHolder()); categoryPair.order.add(categoryPair.token); } /** */ private Object addMenu(Object item, StringTokenizer tokens) { MapTokenPair categoryPair = getCategoryMap(tokens, menuMap); // add the item: categoryPair.map.put(categoryPair.token, item); categoryPair.order.add(categoryPair.token); return item; } private final class PrintMenuAdder implements MenuAdder { public void addMenuItem(StructuredMenuItemHolder holder) { print("JMenuItem '" + holder.getMenuItem().getActionCommand() + "'"); } public void addSeparator() { print("Separator '" + "'"); } // public void addAction(Action action) { // print("Action '"+action.getValue(Action.NAME)+"'"); // } public void addCategory(String category) { print("Category: '" + category + "'"); } } private class MapTokenPair { Map map; String token; Vector order; MapTokenPair(Map map, String token, Vector order) { this.map = map; this.token = token; this.order = order; } } private MapTokenPair getCategoryMap(StringTokenizer tokens, Map thisMap) { if (tokens.hasMoreTokens()) { String nextToken = tokens.nextToken(); if (tokens.hasMoreTokens()) { if (!thisMap.containsKey(nextToken)) { Map newMap = new HashMap(); Vector newOrder = new Vector(); newMap.put(ORDER_NAME, newOrder); thisMap.put(nextToken, newMap); } Map nextMap = (Map) thisMap.get(nextToken); Vector order = (Vector) thisMap.get(ORDER_NAME); if (!order.contains(nextToken)) { order.add(nextToken); } return getCategoryMap(tokens, nextMap); } else { Vector order = (Vector) thisMap.get(ORDER_NAME); return new MapTokenPair(thisMap, nextToken, order); } } // error case? return null; } public void updateMenus(final JMenuBar myItem, String prefix) { MapTokenPair pair = getCategoryMap(new StringTokenizer(prefix, "/"), menuMap); Map myMap = (Map) pair.map.get(pair.token); updateMenus(new MenuAdder() { public void addMenuItem(StructuredMenuItemHolder holder) { Tools.setLabelAndMnemonic(holder.getMenuItem(), null); myItem.add(holder.getMenuItem()); } public void addSeparator() { throw new NoSuchMethodError("addSeparator for JMenuBar"); } // public void addAction(Action action) { // throw new NoSuchMethodError("addAction for JMenuBar"); // } public void addCategory(String category) { } }, myMap, new DefaultMenuAdderCreator()); } public void updateMenus(final JPopupMenu myItem, String prefix) { MapTokenPair pair = getCategoryMap(new StringTokenizer(prefix, "/"), menuMap); Map myMap = (Map) pair.map.get(pair.token); updateMenus(new MenuAdder() { StructuredMenuListener listener = new StructuredMenuListener(); public void addMenuItem(StructuredMenuItemHolder holder) { Tools.setLabelAndMnemonic(holder.getMenuItem(), null); JMenuItem menuItem = holder.getMenuItem(); adjustMenuItem(menuItem); myItem.add(menuItem); if (myItem instanceof MenuEventSupplier) { MenuEventSupplier receiver = (MenuEventSupplier) myItem; receiver.addMenuListener(listener); listener.addItem(holder); } } public void addSeparator() { if (lastItemIsASeparator(myItem)) return; myItem.addSeparator(); } // public void addAction(Action action) { // myItem.add(action); // } public void addCategory(String category) { } }, myMap, new DefaultMenuAdderCreator()); } /** */ public void updateMenus(final JToolBar bar, String prefix) { MapTokenPair pair = getCategoryMap(new StringTokenizer(prefix, "/"), menuMap); Map myMap = (Map) pair.map.get(pair.token); updateMenus(new MenuAdder() { public void addMenuItem(StructuredMenuItemHolder holder) { bar.add(holder.getAction()); } public void addSeparator() { // no separators to save place. But they look good. fc, // 16.6.2005. bar.addSeparator(); } // public void addAction(Action action) { // bar.add(action); // } public void addCategory(String category) { } }, myMap, new DefaultMenuAdderCreator()); } private interface MenuAdder { void addMenuItem(StructuredMenuItemHolder holder); void addSeparator(); // void addAction(Action action); void addCategory(String category); } private static class MenuItemAdder implements MenuAdder { /** * */ private int mAmountOfVisibleMenuItems = 20; private int mItemCounter = 0; private int mMenuCounter = 0; private JMenu mBaseMenuItem; private JMenu myMenuItem; private StructuredMenuListener listener; public MenuItemAdder(JMenu pMenuItem) { this.myMenuItem = pMenuItem; this.mBaseMenuItem = myMenuItem; mAmountOfVisibleMenuItems = Resources.getInstance().getIntProperty(AMOUNT_OF_VISIBLE_MENU_ITEMS, 20); listener = new StructuredMenuListener(); pMenuItem.addMenuListener(listener); } public void addMenuItem(StructuredMenuItemHolder holder) { mItemCounter++; if(mItemCounter > mAmountOfVisibleMenuItems) { String label = Resources.getInstance().getResourceString("StructuredMenuHolder.next"); if(mMenuCounter > 0) { label += " " + mMenuCounter; } JMenu jMenu = new JMenu(label); mBaseMenuItem.add(jMenu); myMenuItem = jMenu; mItemCounter = 0; mMenuCounter++; } Tools.setLabelAndMnemonic(holder.getMenuItem(), null); JMenuItem item = holder.getMenuItem(); adjustMenuItem(item); listener.addItem(holder); myMenuItem.add(item); } public void addSeparator() { if (lastItemIsASeparator(myMenuItem)) { return; } myMenuItem.addSeparator(); } // public void addAction(Action action) { // myItem.add(action); // } public void addCategory(String category) { } } /** */ static private void adjustMenuItem(JMenuItem item) { if (item.getIcon() == null) { item.setIcon(blindIcon); } else { // align if (item.getIcon().getIconWidth() < ICON_SIZE) { item.setIconTextGap(item.getIconTextGap() + (ICON_SIZE - item.getIcon().getIconWidth())); } } } private interface MenuAdderCreator { MenuAdder createAdder(JMenu baseObject); } private class DefaultMenuAdderCreator implements MenuAdderCreator { /* * (non-Javadoc) * * @see * freemind.controller.StructuredMenuHolder.MenuAdderCreator#createAdder * (javax.swing.JMenu) */ public MenuAdder createAdder(JMenu baseObject) { return new MenuItemAdder(baseObject); } } private class SeparatorHolder { public SeparatorHolder() { } } private void updateMenus(MenuAdder menuAdder, Map thisMap, MenuAdderCreator factory) { // System.out.println(thisMap); // iterate through maps and do the changes: Vector myVector = (Vector) thisMap.get(ORDER_NAME); for (Iterator i = myVector.iterator(); i.hasNext();) { String category = (String) i.next(); // The "." target was handled earlier. if (category.equals(".")) continue; Object nextObject = thisMap.get(category); if (nextObject instanceof SeparatorHolder) { menuAdder.addSeparator(); continue; } if (nextObject instanceof StructuredMenuItemHolder) { StructuredMenuItemHolder holder = (StructuredMenuItemHolder) nextObject; menuAdder.addMenuItem(holder); }/* * if(nextObject instanceof JMenuItem) { * menuAdder.addMenuItem((JMenuItem) nextObject); } *//* * else if(nextObject instanceof Action){ * menuAdder.addAction((Action) nextObject); } */else if (nextObject instanceof Map) { menuAdder.addCategory(category); Map nextMap = (Map) nextObject; MenuAdder nextItem; if (nextMap.containsKey(".")) { // add this item to the current place: JMenu baseObject = (JMenu) nextMap.get("."); StructuredMenuItemHolder holder = new StructuredMenuItemHolder(); holder.setMenuItem(baseObject); menuAdder.addMenuItem(holder); nextItem = factory.createAdder(baseObject); } else { nextItem = menuAdder; } mIndent++; updateMenus(nextItem, nextMap, factory); mIndent--; } } } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ public String toString() { mIndent = 0; mOutputString = ""; updateMenus(new PrintMenuAdder(), menuMap, new PrintMenuAdderCreator()); return mOutputString; } private class PrintMenuAdderCreator implements MenuAdderCreator { /* * (non-Javadoc) * * @see * freemind.controller.StructuredMenuHolder.MenuAdderCreator#createAdder * (javax.swing.JMenu) */ public MenuAdder createAdder(JMenu baseObject) { return new PrintMenuAdder(); } } private void print(String string) { for (int i = 0; i < mIndent; ++i) { mOutputString += (" "); } mOutputString += (string) + "\n"; } public interface MenuEventSupplier { void addMenuListener(MenuListener listener); void removeMenuListener(MenuListener listener); } public static class StructuredMenuListener implements javax.swing.event.MenuListener { private Vector menuItemHolder = new Vector(); public StructuredMenuListener() { } public void menuSelected(MenuEvent arg0) { // System.out.println("Selected menu items " + arg0); for (Iterator i = menuItemHolder.iterator(); i.hasNext();) { StructuredMenuItemHolder holder = (StructuredMenuItemHolder) i .next(); Action action = holder.getAction(); boolean isEnabled = false; JMenuItem menuItem = holder.getMenuItem(); if (holder.getEnabledListener() != null) { try { isEnabled = holder.getEnabledListener().isEnabled( menuItem, action); } catch (Exception e) { Resources.getInstance().logException(e); } action.setEnabled(isEnabled); // menuItem.setEnabled(isEnabled); } isEnabled = menuItem.isEnabled(); if (isEnabled && holder.getSelectionListener() != null) { boolean selected = false; try { selected = holder.getSelectionListener().isSelected( menuItem, action); } catch (Exception e) { Resources.getInstance().logException(e); } if (menuItem instanceof JCheckBoxMenuItem) { JCheckBoxMenuItem checkItem = (JCheckBoxMenuItem) menuItem; checkItem.setSelected(selected); } else { // Do icon change if not a check box menu! setSelected(menuItem, selected); } } } } public void menuDeselected(MenuEvent arg0) { } public void menuCanceled(MenuEvent arg0) { } public void addItem(StructuredMenuItemHolder holder) { menuItemHolder.add(holder); } } public static boolean lastItemIsASeparator(JMenu menu) { if (menu.getItemCount() > 0) { if (menu.getMenuComponents()[menu.getItemCount() - 1] instanceof JSeparator) { // no separator, if the last was such. return true; } } return false; } public static boolean lastItemIsASeparator(JPopupMenu menu) { if (menu.getComponentCount() > 0) { if (menu.getComponent(menu.getComponentCount() - 1) instanceof JPopupMenu.Separator) { // no separator, if the last was such. return true; } } return false; } private static void setSelected(JMenuItem menuItem, boolean state) { if (state) { menuItem.setIcon(sSelectedIcon); } else { Icon normalIcon = (Icon) menuItem.getAction().getValue( Action.SMALL_ICON); if (normalIcon == null) { normalIcon = blindIcon; } menuItem.setIcon(normalIcon); } } }