/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander 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 3 of the License, or * (at your option) any later version. * * muCommander 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 com.mucommander.ui.button; import com.mucommander.ui.helper.MnemonicHelper; import javax.swing.*; import java.awt.*; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; /** * ButtonChoicePanel lays out an array of buttons on a grid and provides an easy way * for the user to navigate and select buttons : * <ul> * <li>At any given time, the current active button (the first one initially) can be selected by pressing ENTER * <li>LEFT key goes back to the previous button, to the last button if current button is the first one * <li>RIGHT key goes forward one button, to the first button if current button is the last one * <li>UP key goes up one row, to the last row if current button is on the first row * <li>DOWN key goes down one row, to the first row if current button is on the last row * <li>Buttons can directly be selected by pressing the Mnemonic associated with the button (if any) * </ul> * This does not interfere with regular focus management where TAB (resp. Shift+TAB) goes to the next (resp. previous) * focusable component. * * @author Maxence Bernard */ public class ButtonChoicePanel extends JPanel implements KeyListener, FocusListener { /** Provided JButton instances */ private JButton buttons[]; /** RootPane associated with this ButtonChoicePanel */ private JRootPane rootPane; /** Number of columns of the buttons grid */ private int nbCols; /** Number of row of the buttons grid */ private int nbRows; /** Current button, i.e. the one that currently has focus */ private int currentButton; /** * Creates a new ButtonChoicePanel and lays out the given buttons on a grid * according to the provided number of colums. * * <p>Initial focus will be given to the first button.</p> * * @param buttons the JButton instances to layout * @param nbCols number of columns for the buttons grid, if <=0 all buttons will be put on a single row * @param rootPane associated with this ButtonChoicePanel */ public ButtonChoicePanel(JButton buttons[], int nbCols, JRootPane rootPane) { this.buttons = buttons; int nbButtons = buttons.length; this.nbCols = nbCols<=0?nbButtons:nbCols; this.rootPane = rootPane; this.nbRows = nbCols<=0?1:nbButtons/nbCols+(nbButtons%nbCols==0?0:1); // If the provided number of columns is <= 0, lay out all buttons on a single row // and use FlowLayout to do that if (nbCols<=0) { setLayout(new FlowLayout(FlowLayout.RIGHT)); } // Use GridLayout to lay out buttons on 2-dimensional grid else { setLayout(new GridLayout(0, nbCols)); } for (JButton button : buttons) { // Listener to key events to transfer focus button.addKeyListener(this); // Allow buttons to be made 'default buttons' when enter is pressed button.setDefaultCapable(true); // Listen to focus events to make the focused button the default button button.addFocusListener(this); add(button); } // Set mnemonics to buttons updateMnemonics(); // First button is made 'default button' rootPane.setDefaultButton(buttons[0]); this.currentButton = 0; } /** * Updates the buttons mnemonics. This method should be called when at least one of the button's label has changed. */ public void updateMnemonics() { MnemonicHelper mnemonicHelper = new MnemonicHelper(); char mnemonic; for (JButton button : buttons) { mnemonic = mnemonicHelper.getMnemonic(button); if(mnemonic!=0) button.setMnemonic(mnemonic); } } ///////////////////////// // KeyListener methods // ///////////////////////// public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); int oldCurrentButton = currentButton; int nbButtons = buttons.length; // LEFT key goes back one button, to the last button if current button is the first one if (keyCode==KeyEvent.VK_LEFT) { this.currentButton = currentButton==0?nbButtons-1:currentButton-1; } // RIGHT key goes forward one button, to the first button if current button is the last one else if (keyCode==KeyEvent.VK_RIGHT) { this.currentButton = currentButton==nbButtons-1?0:currentButton+1; } // UP key goes up one row, to the last row if current button is on the first row else if (keyCode==KeyEvent.VK_UP) { if (currentButton<nbCols) { // If current button is on the first row this.currentButton = (nbRows-1)*nbCols+currentButton%nbCols; if(this.currentButton>nbButtons-1) this.currentButton -= nbCols; } else this.currentButton -= nbCols; } // DOWN key goes down one row, to the first row if current button is on the last row else if (keyCode==KeyEvent.VK_DOWN) { if(nbButtons-currentButton>0 && nbButtons-currentButton<=nbCols) // If current button is on the last row this.currentButton = currentButton%nbCols; else this.currentButton += nbCols; } // Click button when a key that corresponds to one of the buttons' mnemonic has been pressed else if(!e.isAltDown()) { for (JButton button : buttons) { if (keyCode==button.getMnemonic()) { button.doClick(); return; } } } // Make the new button the default button and request focus on this button if(oldCurrentButton!=currentButton) { rootPane.setDefaultButton(buttons[currentButton]); buttons[currentButton].requestFocus(); } } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } ////////////////////////////////// // FocusListener implementation // ////////////////////////////////// public void focusGained(FocusEvent focusEvent) { // Makes the newly focused button the default button rootPane.setDefaultButton((JButton)focusEvent.getComponent()); } public void focusLost(FocusEvent focusEvent) { } }