package com.vitco.layout.content.shortcut; import com.jidesoft.swing.JideButton; import com.jidesoft.swing.JideTabbedPane; import com.vitco.layout.content.JCustomScrollPane; import com.vitco.layout.content.JCustomTable; import com.vitco.layout.content.ViewPrototype; import com.vitco.manager.async.AsyncAction; import com.vitco.manager.async.AsyncActionManager; import com.vitco.settings.VitcoSettings; import com.vitco.util.misc.SaveResourceLoader; import org.springframework.beans.factory.annotation.Autowired; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellEditor; import java.awt.*; import java.awt.event.*; import java.util.EventObject; /** * Handle the displaying and the logic for the editing of shortcuts. (view & link to logic) */ public class ShortcutManagerView extends ViewPrototype implements ShortcutManagerViewInterface { protected AsyncActionManager asyncActionManager; @Autowired public final void setAsyncActionManager(AsyncActionManager asyncActionManager) { this.asyncActionManager = asyncActionManager; } // last hover cell private int curRow = -1; private int curCol = -1; // layout of cells private class TableRenderer extends DefaultTableCellRenderer { // mainview table cell @Override public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) { setForeground(VitcoSettings.DEFAULT_TEXT_COLOR); // set text color if (row == curRow && column == curCol && column == 1) { setBackground(VitcoSettings.DEFAULT_HOVER_COLOR); // hover effect setToolTipText(langSelector.getString("dbl-clc-to-edit_tooltip")); } else { setBackground(VitcoSettings.DEFAULT_CELL_COLOR); } setFont(VitcoSettings.TABLE_FONT); setBorder(VitcoSettings.DEFAULT_CELL_BORDER); // padding setValue(value.toString()); // set the value return this; } } // cell edit action private class CellEditor extends AbstractCellEditor implements TableCellEditor { // handles the editing of the cell value JPanel wrapper; JTextArea component; // var & setter (constructor) // so we have a reference to the current frame // this can be null if we handle global shortcuts private final String frame; public CellEditor(String frame) { super(); this.frame = frame; } @Override public boolean isCellEditable( EventObject e ) { // only edit with double-click return !(e instanceof MouseEvent) || ((MouseEvent) e).getClickCount() >= 2; } private void handleKeyStroke(final KeyStroke keyStroke, JTable table, final int rowIndex, int vColIndex) { if (shortcutManager.isValidShortcut(keyStroke)) { if (shortcutManager.isFreeShortcut(frame, keyStroke)) { // update the shortcut if (shortcutManager.updateShortcutObject(keyStroke, frame, rowIndex)) { String shortcutText = shortcutManager.asString(keyStroke); component.setText(shortcutText); // make sure the table is up to date // note: workaround for resize bug table.setValueAt(shortcutText, rowIndex, vColIndex); } wrapper.setBackground(shortcutManager.getEditBgColor(frame, rowIndex)); stopCellEditing(); // immediately stop editing } else { // this shortcut is already used (!) // show error color for one second wrapper.setBackground(VitcoSettings.EDIT_ERROR_BG_COLOR); Toolkit.getDefaultToolkit().beep(); // play beep new Thread() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e1) { // no need to track this // e1.printStackTrace(); } wrapper.setBackground(shortcutManager.getEditBgColor(frame, rowIndex)); } }.start(); } } } // start editing public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, final int rowIndex, final int vColIndex) { component = new JTextArea(); component.setText((String) value); // set initial text component.setBorder(VitcoSettings.DEFAULT_CELL_BORDER_EDIT); component.setFont(VitcoSettings.TABLE_FONT_BOLD); // add the listener component.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { e.consume(); // prevent further use of this keystroke } @Override public void keyPressed(final KeyEvent e) { asyncActionManager.addAsyncAction(new AsyncAction() { @Override public void performAction() { handleKeyStroke(KeyStroke.getKeyStrokeForEvent(e), table, rowIndex, vColIndex); } }); // prevent further use of this keystroke (this mustn't be done async) e.consume(); } @Override public void keyReleased(KeyEvent e) { e.consume(); // prevent further use of this keystroke } }); component.setHighlighter(null); // do not show selection // NOTE: We can't use "setEditable" b/c then global shortcuts would not be deactivated component.setCaretColor(new Color(0, 0, 0, 0)); // hide the caret component.setCursor(Cursor.getDefaultCursor()); // just show the ordinary mouse cursor component.setOpaque(false); component.setForeground(VitcoSettings.EDIT_TEXT_COLOR); // set text color when edit // Allow for clearing the shortcut JideButton clearButton = new JideButton(); clearButton.setIcon(new SaveResourceLoader("resource/img/icons/clear.png").asIconImage()); clearButton.setButtonStyle(JideButton.FLAT_STYLE); clearButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { handleKeyStroke(null, table, rowIndex, vColIndex); } }); // holds edit field and clear button wrapper = new JPanel(); wrapper.setLayout(new BorderLayout()); wrapper.add(component, BorderLayout.CENTER); wrapper.add(clearButton, BorderLayout.EAST); wrapper.setBackground(shortcutManager.getEditBgColor(frame, rowIndex)); // bg color when edit return wrapper; } // edit complete // Important: can not rely on this to fire (resize bug!) public Object getCellEditorValue() { return component.getText(); // return new value } } // internal - takes shortcuts, col names and frame key // frame key can be null if we handle global shortcuts // global flag private JTable createTab(String[][] data, String[] columnNames, String frameKey) { // create the default model for data and column names DefaultTableModel model = new DefaultTableModel(data, columnNames); // create table, only allow editing for second column (shortcuts) JCustomTable shortcut_table = new JCustomTable(model) { @Override public boolean isCellEditable(int rowIndex, int colIndex) { // only allow editing shortcuts return colIndex == 1; } }; // add hover effect shortcut_table.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { JTable aTable = (JTable)e.getSource(); int tRow = aTable.rowAtPoint(e.getPoint()); int tCol = aTable.columnAtPoint(e.getPoint()); if (curRow != tRow || curCol != tCol) { curRow = tRow; curCol = tCol; aTable.repaint(); } } }); shortcut_table.addMouseListener(new MouseAdapter() { @Override public void mouseExited(MouseEvent e) { curRow = -1; curCol = -1; ((JTable)e.getSource()).repaint(); } }); // custom layout for the cells TableRenderer tableRenderer = new TableRenderer(); shortcut_table.getColumnModel().getColumn(0).setCellRenderer(tableRenderer); shortcut_table.getColumnModel().getColumn(1).setCellRenderer(tableRenderer); // create a new custom cell editor for the second column (shortcuts) shortcut_table.getColumnModel().getColumn(1).setCellEditor(new CellEditor(frameKey)); return shortcut_table; } // return a JideTabbedPane that is autonomous and manages shortcuts @Override public JTabbedPane getEditTables() { // create the content of this frame (Shortcut Manager) // default header String[] columnNames = { langSelector.getString("shortcut_mg_header_action"), langSelector.getString("shortcut_mg_header_shortcut") }; // the different frame shortcuts are in different tabs final JideTabbedPane tabbedPane = new JideTabbedPane(JTabbedPane.TOP, JideTabbedPane.SCROLL_TAB_LAYOUT); tabbedPane.setFocusable(false); // looks nicer //tabbedPane.setTabShape(JideTabbedPane.SHAPE_WINDOWS); // make square //tabbedPane.setTabResizeMode(JideTabbedPane.RESIZE_MODE_FIT); // add the global shortcuts String[][] globalShortcuts = shortcutManager.getShortcuts(null); tabbedPane.addTab(langSelector.getString("global_shortcuts_caption"), new JCustomScrollPane(createTab(globalShortcuts, columnNames, null))); // add the frame shortcuts String[][] frames = shortcutManager.getFrames(); for (String[] frame : frames) { // create the list of shortcuts for this frame String[][] data = shortcutManager.getShortcuts(frame[0]); // the shortcuts (list) if (data.length > 0) { // only create tab if it has shortcuts // add this tab to the tabbedPane tabbedPane.addTab(frame[1], new JCustomScrollPane(createTab(data, columnNames, frame[0]))); } } // set tooltips for (int i = 0; i < tabbedPane.getTabCount(); i ++) { tabbedPane.setToolTipTextAt(i, tabbedPane.getTitleAt(i)); } // todo add setter and getter for this and store and load in linkage class // load from preferences if (preferences.contains("shortcut-manager_active-tab")) { int selectedIndex = preferences.loadInteger("shortcut-manager_active-tab"); if (tabbedPane.getTabCount() > selectedIndex) { tabbedPane.setSelectedIndex(selectedIndex); // set the stored index as active } } // store when active tab changes tabbedPane.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { preferences.storeInteger("shortcut-manager_active-tab", tabbedPane.getSelectedIndex()); } }); return tabbedPane; } }