package org.geogebra.desktop.gui.view.spreadsheet; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.SwingUtilities; import javax.swing.table.DefaultTableModel; import javax.swing.text.JTextComponent; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.plugin.EventType; import org.geogebra.desktop.main.AppD; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; public class SpreadsheetKeyListenerD implements KeyListener { private AppD app; private SpreadsheetViewD view; private Kernel kernel; private MyTableD table; private DefaultTableModel model; private MyCellEditorSpreadsheet editor; public SpreadsheetKeyListenerD(AppD app, MyTableD table) { this.app = app; this.kernel = app.getKernel(); this.table = table; this.view = table.getView(); this.model = (DefaultTableModel) table.getModel(); this.editor = table.editor; } @Override public void keyTyped(KeyEvent e) { // only handle key pressed } @Override @SuppressFBWarnings({ "SF_SWITCH_FALLTHROUGH", "missing break is deliberate" }) public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); // Application.debug(keyCode+""); // boolean shiftDown = e.isShiftDown(); boolean altDown = e.isAltDown(); boolean ctrlDown = AppD.isControlDown(e) // Windows ctrl/Mac Meta || e.isControlDown(); // Fudge (Mac ctrl key) int row = table.getSelectedRow(); int column = table.getSelectedColumn(); switch (keyCode) { case KeyEvent.VK_UP: if (AppD.isControlDown(e)) { if (model.getValueAt(row, column) != null) { // move to top of current "block" // if shift pressed, select cells too while (row > 0 && model.getValueAt(row - 1, column) != null) { row--; } table.changeSelection(row, column, false, e.isShiftDown()); } else { // move up to next defined cell while (row > 0 && model.getValueAt(row - 1, column) == null) { row--; } table.changeSelection(Math.max(0, row - 1), column, false, false); } e.consume(); } // copy description into input bar when a cell is entered // GeoElement geo = (GeoElement) // getModel().getValueAt(table.getSelectedRow() - 1, // table.getSelectedColumn()); // if (geo != null) { // AlgebraInput ai = // (AlgebraInput)(app.getGuiManager().getAlgebraInput()); // ai.setString(geo); // } break; case KeyEvent.VK_LEFT: if (AppD.isControlDown(e)) { if (model.getValueAt(row, column) != null) { // move to left of current "block" // if shift pressed, select cells too while (column > 0 && model.getValueAt(row, column - 1) != null) { column--; } table.changeSelection(row, column, false, e.isShiftDown()); } else { // move left to next defined cell while (column > 0 && model.getValueAt(row, column - 1) == null) { column--; } table.changeSelection(row, Math.max(0, column - 1), false, false); } e.consume(); } // // copy description into input bar when a cell is entered // geo = (GeoElement) getModel().getValueAt(table.getSelectedRow(), // table.getSelectedColumn() - 1); // if (geo != null) { // AlgebraInput ai = // (AlgebraInput)(app.getGuiManager().getAlgebraInput()); // ai.setString(geo); // } break; case KeyEvent.VK_DOWN: // auto increase spreadsheet size when you go off the bottom if (table.getSelectedRow() + 1 == table.getRowCount() && table.getSelectedRow() + 1 < app .getMaxSpreadsheetRowsVisible()) { model.setRowCount(table.getRowCount() + 1); // getView().getRowHeader().revalidate(); //G.STURR 2010-1-9 } else if (AppD.isControlDown(e)) { if (model.getValueAt(row, column) != null) { // move to bottom of current "block" // if shift pressed, select cells too while (row < table.getRowCount() - 1 && model.getValueAt(row + 1, column) != null) { row++; } table.changeSelection(row, column, false, e.isShiftDown()); } else { // move down to next selected cell while (row < table.getRowCount() - 1 && model.getValueAt(row + 1, column) == null) { row++; } table.changeSelection( Math.min(table.getRowCount() - 1, row + 1), column, false, false); } e.consume(); } // // copy description into input bar when a cell is entered // geo = (GeoElement) // getModel().getValueAt(table.getSelectedRow()+1, // table.getSelectedColumn()); // if (geo != null) { // AlgebraInput ai = // (AlgebraInput)(app.getGuiManager().getAlgebraInput()); // ai.setString(geo); // } break; case KeyEvent.VK_HOME: // if shift pressed, select cells too if (AppD.isControlDown(e)) { // move to top left of spreadsheet table.changeSelection(0, 0, false, e.isShiftDown()); } else { // move to left of current row table.changeSelection(row, 0, false, e.isShiftDown()); } e.consume(); break; case KeyEvent.VK_END: // move to bottom right of spreadsheet // if shift pressed, select cells too // find rectangle that will contain all cells for (int c = 0; c < table.getColumnCount(); c++) { for (int r = 0; r < table.getRowCount(); r++) { if ((r > row || c > column) && model.getValueAt(r, c) != null) { if (r > row) { row = r; } if (c > column) { column = c; } } } } table.changeSelection(row, column, false, e.isShiftDown()); e.consume(); break; case KeyEvent.VK_RIGHT: // auto increase spreadsheet size when you go off the right if (table.getSelectedColumn() + 1 == table.getColumnCount() && table.getSelectedColumn() + 1 < app .getMaxSpreadsheetColumnsVisible()) { model.setColumnCount(table.getColumnCount() + 1); view.getColumnHeader().revalidate(); // these two lines are a workaround for Java 6 // (Java bug?) table.changeSelection(row, column + 1, false, false); e.consume(); } else if (AppD.isControlDown(e)) { if (model.getValueAt(row, column) != null) { // move to bottom of current "block" // if shift pressed, select cells too while (column < table.getColumnCount() - 1 && model.getValueAt(row, column + 1) != null) { column++; } table.changeSelection(row, column, false, e.isShiftDown()); } else { // move right to next defined cell while (column < table.getColumnCount() - 1 && model.getValueAt(row, column + 1) == null) { column++; } table.changeSelection(row, Math.min(table.getColumnCount() - 1, column + 1), false, false); } e.consume(); } // // copy description into input bar when a cell is entered // geo = (GeoElement) getModel().getValueAt(table.getSelectedRow(), // table.getSelectedColumn() + 1); // if (geo != null) { // AlgebraInput ai = // (AlgebraInput)(app.getGuiManager().getAlgebraInput()); // ai.setString(geo); // } break; case KeyEvent.VK_SHIFT: case KeyEvent.VK_CONTROL: case KeyEvent.VK_ALT: case KeyEvent.VK_META: // MAC_OS Meta e.consume(); // stops editing start break; case KeyEvent.VK_F9: kernel.updateConstruction(); e.consume(); // stops editing start break; case KeyEvent.VK_R: if (AppD.isControlDown(e)) { kernel.updateConstruction(); e.consume(); } else { letterOrDigitTyped(); } break; // needs to be here to stop keypress starting a cell edit after the undo case KeyEvent.VK_Z: // undo if (ctrlDown) { // Application.debug("undo"); app.getGuiManager().undo(); e.consume(); } else { letterOrDigitTyped(); } break; // needs to be here to stop keypress starting a cell edit after the redo case KeyEvent.VK_Y: // redo if (ctrlDown) { // Application.debug("redo"); app.getGuiManager().redo(); e.consume(); } else { letterOrDigitTyped(); } break; case KeyEvent.VK_C: case KeyEvent.VK_V: case KeyEvent.VK_X: case KeyEvent.VK_DELETE: case KeyEvent.VK_BACK_SPACE: if (!editor.isEditing()) { if (Character.isLetterOrDigit(e.getKeyChar()) && !editor.isEditing() && !(ctrlDown || e.isAltDown())) { letterOrDigitTyped(); } else if (ctrlDown) { e.consume(); if (keyCode == KeyEvent.VK_C) { table.copy(altDown); } else if (keyCode == KeyEvent.VK_V) { boolean storeUndo = table.paste(); view.getRowHeader().revalidate(); if (storeUndo) { app.storeUndoInfo(); } } else if (keyCode == KeyEvent.VK_X) { boolean storeUndo = table.cut(); if (storeUndo) { app.storeUndoInfo(); } } } if (keyCode == KeyEvent.VK_DELETE || keyCode == KeyEvent.VK_BACK_SPACE) { e.consume(); // Application.debug("deleting..."); boolean storeUndo = table.delete(); if (storeUndo) { app.storeUndoInfo(); } } return; } break; // case KeyEvent.VK_ENTER: case KeyEvent.VK_F2: if (!editor.isEditing()) { table.setAllowEditing(true); table.editCellAt(table.getSelectedRow(), table.getSelectedColumn()); final JTextComponent f = (JTextComponent) table .getEditorComponent(); f.requestFocus(); f.getCaret().setVisible(true); table.setAllowEditing(false); } e.consume(); break; case KeyEvent.VK_ENTER: if (editor.tabReturnCol > -1) { table.changeSelection(row, editor.tabReturnCol, false, false); editor.tabReturnCol = -1; } // fall through case KeyEvent.VK_PAGE_DOWN: case KeyEvent.VK_PAGE_UP: // stop cell being erased before moving break; // stop TAB erasing cell before moving case KeyEvent.VK_TAB: // disable shift-tab in column A if (table.getSelectedColumn() == 0 && e.isShiftDown()) { e.consume(); } break; case KeyEvent.VK_A: if (AppD.isControlDown(e)) { // select all cells row = 0; column = 0; // find rectangle that will contain all defined cells for (int c = 0; c < table.getColumnCount(); c++) { for (int r = 0; r < table.getRowCount(); r++) { if ((r > row || c > column) && model.getValueAt(r, c) != null) { if (r > row) { row = r; } if (c > column) { column = c; } } } } table.changeSelection(0, 0, false, false); table.changeSelection(row, column, false, true); e.consume(); } // no break, fall through default: if (!Character.isIdentifierIgnorable(e.getKeyChar()) && !editor.isEditing() && !(ctrlDown || e.isAltDown())) { letterOrDigitTyped(); } else { e.consume(); } break; } /* * if (keyCode >= 37 && keyCode <= 40) { if (editor.isEditing()) return; * } * * for (int i = 0; i < defaultKeyListeners.length; ++ i) { if * (e.isConsumed()) break; defaultKeyListeners[i].keyPressed(e); } */ } public void letterOrDigitTyped() { table.setAllowEditing(true); table.repaint(); // G.Sturr 2009-10-10: cleanup when keypress edit // begins // check if cell fixed Object o = model.getValueAt(table.getSelectedRow(), table.getSelectedColumn()); if (o != null && o instanceof GeoElement) { GeoElement geo = (GeoElement) o; if (geo.isProtected(EventType.UPDATE)) { return; } } model.setValueAt(null, table.getSelectedRow(), table.getSelectedColumn()); table.editCellAt(table.getSelectedRow(), table.getSelectedColumn()); // workaround, see // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4192625 final JTextComponent f = (JTextComponent) table.getEditorComponent(); f.requestFocus(); f.getCaret().setVisible(true); // workaround for Mac OS X 10.5 problem (first character typed deleted) if (AppD.MAC_OS) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { f.setSelectionStart(1); f.setSelectionEnd(1); } }); } table.setAllowEditing(false); } @Override public void keyReleased(KeyEvent e) { // only handle key pressed } }