/* * Freeplane - mind map editor * Copyright (C) 2009 Dimitry * * This file author is Dimitry * * 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.components; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.LayoutManager; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import java.util.HashMap; import java.util.Map; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JRootPane; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.text.JTextComponent; import org.freeplane.core.resources.ResourceController; import org.freeplane.core.ui.AFreeplaneAction; import org.freeplane.core.ui.IAcceleratorChangeListener; import org.freeplane.core.ui.KeyBindingProcessor; import org.freeplane.core.ui.SetAcceleratorOnNextClickAction; import org.freeplane.core.util.TextUtils; import org.freeplane.features.mode.Controller; import org.freeplane.features.mode.mindmapmode.MModeController; import org.pushingpixels.flamingo.api.ribbon.JRibbon; /** * @author Dimitry Polivaev * 03.07.2009 */ public class FButtonBar extends JComponent implements IAcceleratorChangeListener, KeyEventDispatcher, WindowFocusListener { private static final int BUTTON_NUMBER = 12; /** * */ private static final long serialVersionUID = 1L; final private Map<Integer, JButton[]> buttons; private int lastModifiers = -1; private int nextModifiers = 0; private JFrame ownWindowAncestor; final private Timer timer = new Timer(500, new ActionListener() { public void actionPerformed(final ActionEvent e) { onModifierChangeImpl(); } }); private final KeyBindingProcessor keyProcessor; @SuppressWarnings("serial") private class ContentPane extends JPanel{ @Override protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) { if(keyProcessor.processKeyBinding(ks, e, condition, pressed)) { return true; } return processFKey(e); } return false; } } public FButtonBar(JRootPane rootPane, KeyBindingProcessor proc) { KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this); final Container oldContentPane = rootPane.getContentPane(); final ContentPane newContentPane = new ContentPane(); final LayoutManager layoutManager = oldContentPane.getLayout(); oldContentPane.setLayout(null); newContentPane.setLayout(layoutManager); //RIBBONS impl: if ribbon found add it to NORTH for (Component c : oldContentPane.getComponents()) { if(c instanceof JRibbon) { newContentPane.add(c, BorderLayout.NORTH); } else { newContentPane.add(c); } } rootPane.setContentPane(newContentPane); buttons = new HashMap<Integer, JButton[]>(); onModifierChange(); this.keyProcessor = proc; } public FButtonBar(JRootPane rootPane) { this(rootPane, null); } public void acceleratorChanged(final JMenuItem action, final KeyStroke oldStroke, final KeyStroke newStroke) { final int oldButtonNumber = oldStroke != null ? oldStroke.getKeyCode() - KeyEvent.VK_F1 : -1; final int newButtonNumber = newStroke != null ? newStroke.getKeyCode() - KeyEvent.VK_F1 : -1; if (oldButtonNumber >= 0 && oldButtonNumber < BUTTON_NUMBER) { final int modifiers = oldStroke.getModifiers() & (KeyEvent.CTRL_MASK | KeyEvent.META_MASK | KeyEvent.SHIFT_MASK | KeyEvent.ALT_MASK | KeyEvent.ALT_GRAPH_MASK); final JButton[] buttonRow = buttons.get(modifiers); final JButton button = buttonRow[oldButtonNumber]; setAcceleratorAction(button, oldStroke); } if (newButtonNumber >= 0 && newButtonNumber < BUTTON_NUMBER) { final int modifiers = newStroke.getModifiers() & (KeyEvent.CTRL_MASK | KeyEvent.META_MASK | KeyEvent.SHIFT_MASK | KeyEvent.ALT_MASK | KeyEvent.ALT_GRAPH_MASK); final JButton[] buttonRow = createButtons(modifiers); final JButton button = buttonRow[newButtonNumber]; final String text = action.getActionCommand(); button.setText(text); button.setToolTipText(text); button.setAction(action.getAction()); button.setEnabled(action.isEnabled()); } } public void acceleratorChanged(final AFreeplaneAction action, final KeyStroke oldStroke, final KeyStroke newStroke) { final int oldButtonNumber = oldStroke != null ? oldStroke.getKeyCode() - KeyEvent.VK_F1 : -1; final int newButtonNumber = newStroke != null ? newStroke.getKeyCode() - KeyEvent.VK_F1 : -1; if (oldButtonNumber >= 0 && oldButtonNumber < BUTTON_NUMBER) { final int modifiers = oldStroke.getModifiers() & (KeyEvent.CTRL_MASK | KeyEvent.META_MASK | KeyEvent.SHIFT_MASK | KeyEvent.ALT_MASK | KeyEvent.ALT_GRAPH_MASK); final JButton[] buttonRow = buttons.get(modifiers); final JButton button = buttonRow[oldButtonNumber]; setAcceleratorAction(button, oldStroke); } if (newButtonNumber >= 0 && newButtonNumber < BUTTON_NUMBER) { final int modifiers = newStroke.getModifiers() & (KeyEvent.CTRL_MASK | KeyEvent.META_MASK | KeyEvent.SHIFT_MASK | KeyEvent.ALT_MASK | KeyEvent.ALT_GRAPH_MASK); final JButton[] buttonRow = createButtons(modifiers); final JButton button = buttonRow[newButtonNumber]; final String text = TextUtils.getText(action.getTextKey()); button.setText(text); button.setToolTipText(text); button.setAction(action); button.setEnabled(action.isEnabled()); } } private void setAcceleratorAction(final JButton button, final KeyStroke ks) { final SetAcceleratorOnNextClickAction setAcceleratorAction = new SetAcceleratorOnNextClickAction(ks); button.setAction(setAcceleratorAction); final String text = TextUtils.getText("f_button_unassigned"); button.setText(text); button.setToolTipText(setAcceleratorAction.getValue(Action.NAME).toString()); } private void cleanModifiers(final int modifiers) { if ((nextModifiers & modifiers) == 0) { return; } nextModifiers &= ~modifiers; onModifierChange(); } private JButton[] createButtonRow(final int modifiers) { final JButton[] buttons = new JButton[BUTTON_NUMBER]; for (int i = 0; i < BUTTON_NUMBER; i++) { final String name = "/images/f" + (i + 1) + ".png"; final JButton button = buttons[i] = new JButton(new ImageIcon( ResourceController.getResourceController().getResource(name))) { /** * */ private static final long serialVersionUID = 1L; @Override protected void configurePropertiesFromAction(final Action a) { } }; button.setFocusable(false); button.setBorder(BorderFactory.createEtchedBorder()); if (System.getProperty("os.name").startsWith("Mac OS")) { button.setBorderPainted(false); } KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F1 + i, modifiers); setAcceleratorAction(button, ks); } return buttons; } private JButton[] createButtons(final int modifiers) { JButton[] buttonRow = buttons.get(modifiers); if (buttonRow == null) { buttonRow = createButtonRow(modifiers); buttons.put(modifiers, buttonRow); } return buttonRow; } public boolean dispatchKeyEvent(final KeyEvent e) { if(! (Controller.getCurrentModeController() instanceof MModeController )) return false; if (ownWindowAncestor == null) { ownWindowAncestor = (JFrame) SwingUtilities.getWindowAncestor(this); if (ownWindowAncestor == null) { return false; } ownWindowAncestor.addWindowFocusListener(this); } final Window windowAncestor = SwingUtilities.getWindowAncestor(e.getComponent()); if (windowAncestor == ownWindowAncestor && ownWindowAncestor.getJMenuBar() != null && ownWindowAncestor.getJMenuBar().isEnabled()) { processDispatchedKeyEvent(e); } else { resetModifiers(); } return false; } private void onModifierChange() { if (lastModifiers == nextModifiers) { return; } if (timer.isRunning()) { timer.stop(); } if (nextModifiers == 0) { onModifierChangeImpl(); } else { timer.start(); } } private void onModifierChangeImpl() { if (lastModifiers == nextModifiers) { return; } lastModifiers = nextModifiers; removeAll(); final JButton[] buttonRow = createButtons(nextModifiers); for (final JButton button : buttonRow) { add(button); } revalidate(); repaint(); } private void processDispatchedKeyEvent(final KeyEvent e) { final int keyCode = e.getKeyCode(); switch (e.getID()) { case KeyEvent.KEY_PRESSED: switch (keyCode) { case KeyEvent.VK_CONTROL: setModifiers(KeyEvent.CTRL_MASK); break; case KeyEvent.VK_META: setModifiers(KeyEvent.META_MASK); break; case KeyEvent.VK_SHIFT: setModifiers(KeyEvent.SHIFT_MASK); break; case KeyEvent.VK_ALT: setModifiers(KeyEvent.ALT_MASK); break; case KeyEvent.VK_ALT_GRAPH: setModifiers(KeyEvent.ALT_GRAPH_MASK); break; } break; case KeyEvent.KEY_RELEASED: switch (keyCode) { case KeyEvent.VK_CONTROL: cleanModifiers(KeyEvent.CTRL_MASK); break; case KeyEvent.VK_META: cleanModifiers(KeyEvent.META_MASK); break; case KeyEvent.VK_SHIFT: cleanModifiers(KeyEvent.SHIFT_MASK); break; case KeyEvent.VK_ALT: cleanModifiers(KeyEvent.ALT_MASK); case KeyEvent.VK_ALT_GRAPH: cleanModifiers(KeyEvent.ALT_GRAPH_MASK); break; default: break; } break; default: break; } } private boolean processFKey(final KeyEvent e){ if(e.getID() != KeyEvent.KEY_PRESSED) return false; final Window windowAncestor = SwingUtilities.getWindowAncestor(e.getComponent()); //RIBBONS - commented condition if (windowAncestor != ownWindowAncestor /*|| !ownWindowAncestor.getJMenuBar().isEnabled()*/) { resetModifiers(); return false; } int keyCode = e.getKeyCode(); if (keyCode >= KeyEvent.VK_F1 && keyCode <= KeyEvent.VK_F12 ) { final JButton btn = createButtons(nextModifiers)[keyCode - KeyEvent.VK_F1]; if(btn.getAction() instanceof SetAcceleratorOnNextClickAction && e.getComponent() instanceof JTextComponent) return false; if(timer.isRunning()){ timer.stop(); onModifierChangeImpl(); } btn.doClick(); return true; } return false; } private void resetModifiers() { if (nextModifiers == 0) { return; } nextModifiers = 0; onModifierChange(); } private void setModifiers(final int modifiers) { if ((nextModifiers ^ modifiers) == 0) { return; } nextModifiers |= modifiers; onModifierChange(); } public void windowGainedFocus(final WindowEvent e) { } public void windowLostFocus(final WindowEvent e) { resetModifiers(); } @Override public void layout() { final int w = getParent().getWidth(); final int border = 5; final int h = getComponent(1).getPreferredSize().height; final int componentCount = getComponentCount(); final float availableWidth = w - 2 * border + 0f; final float dw = availableWidth / componentCount; int preferredWidth = 0; int narrowComponentPreferredWidth = 0; for (int i = 0; i < componentCount; i++) { final int cw = getComponent(i).getPreferredSize().width; preferredWidth += cw; if (cw <= dw) { narrowComponentPreferredWidth += cw; } } final float k; if (availableWidth < preferredWidth) { k = (availableWidth - narrowComponentPreferredWidth) / (preferredWidth - narrowComponentPreferredWidth); } else { k = availableWidth / preferredWidth; } float x = border; for (int i = 0; i < componentCount; i++) { float cw = getComponent(i).getPreferredSize().width; if (k > 1f || cw > dw) { cw *= k; } getComponent(i).setBounds((int) x, 0, (int) cw, h); x += cw; } } @Override public Dimension getPreferredSize() { return new Dimension(getParent().getWidth(), getComponent(1).getPreferredSize().height); } }