package org.geogebra.desktop.gui.view.spreadsheet; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.SwingConstants; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import org.geogebra.common.awt.GPoint; import org.geogebra.common.gui.view.spreadsheet.MyTableInterface; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.main.App; import org.geogebra.common.util.SpreadsheetTraceSettings; import org.geogebra.desktop.gui.layout.LayoutD; import org.geogebra.desktop.main.AppD; import org.geogebra.desktop.main.LocalizationD; import org.geogebra.desktop.util.GuiResourcesD; public class SpreadsheetColumnControllerD implements KeyListener, MouseListener, MouseMotionListener { private AppD app; private SpreadsheetViewD view; private Kernel kernel; private MyTableD table; private DefaultTableModel model; protected int column0 = -1; protected boolean isResizing = false; private int overTraceButtonColumn = -1; /** localization */ final LocalizationD loc; public SpreadsheetColumnControllerD(AppD app, MyTableD table) { this.app = app; this.loc = app.getLocalization(); this.kernel = app.getKernel(); this.table = table; this.view = table.getView(); this.model = (DefaultTableModel) table.getModel(); } // ========================================================= // Mouse Listener Methods // ========================================================= @Override public void mouseClicked(MouseEvent e) { // Double clicking on a column boundary auto-adjusts the // width of the column on the left if (isResizing && !AppD.isRightClick(e) && e.getClickCount() == 2) { // get column to adjust int x = e.getX(); int y = e.getY(); GPoint point = table.getIndexFromPixel(x, y); GPoint testPoint = table.getIndexFromPixel(x - 4, y); int col = point.getX(); if (point.getX() != testPoint.getX()) { col = col - 1; } // enlarge or shrink to fit the contents table.fitColumn(col); e.consume(); } } @Override public void mouseEntered(MouseEvent e) { // nothing to do } @Override public void mouseExited(MouseEvent e) { overTraceButtonColumn = -1; if (table.getTableHeader() != null) { table.getTableHeader().resizeAndRepaint(); } } @Override public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); boolean shiftDown = e.isShiftDown(); boolean rightClick = AppD.isRightClick(e); if (!view.hasViewFocus()) { ((LayoutD) app.getGuiManager().getLayout()).getDockManager() .setFocusedPanel(App.VIEW_SPREADSHEET); } // ensure that table header keeps the focus table.getTableHeader().requestFocus(); if (!rightClick) { GPoint point = table.getIndexFromPixel(x, y); if (point != null) { // check if the cursor is within the resizing region (i.e. // border +- 3pixels) GPoint point2 = table.getPixel(point.getX(), point.getY(), true); GPoint point3 = table.getPixel(point.getX(), point.getY(), false); int x2 = point2.getX(); int x3 = point3.getX(); isResizing = !(x > x2 + 2 && x < x3 - 3); if (!isResizing) { // launch trace dialog if over a trace button if (point.x == this.overTraceButtonColumn) { int column = point.getX(); app.getTraceManager().togglePauseTraceGeo(column); view.repaintView(); e.consume(); return; } // otherwise handle column selection if (table .getSelectionType() != MyTableInterface.COLUMN_SELECT) { table.setSelectionType(MyTableInterface.COLUMN_SELECT); if (table.getTableHeader() != null) { table.getTableHeader().requestFocusInWindow(); } } if (shiftDown) { if (column0 != -1) { int column = point.getX(); table.setColumnSelectionInterval(column0, column); } // } else if (metaDown) { // column0 = point.getX(); // // Note: ctrl-select now handled in // // table.changeSelection // table.setColumnSelectionInterval(column0, column0); } else { column0 = point.getX(); table.setColumnSelectionInterval(column0, column0); } // repaint(); } } } } @Override public void mouseReleased(MouseEvent e) { boolean rightClick = AppD.isRightClick(e); if (!((AppD) kernel.getApplication()).letShowPopupMenu()) { return; } if (rightClick) { if (!app.letShowPopupMenu()) { return; } GPoint p = table.getIndexFromPixel(e.getX(), e.getY()); if (p == null) { return; } // if click is outside current selection then change selection if (p.getY() < table.minSelectionRow || p.getY() > table.maxSelectionRow || p.getX() < table.minSelectionColumn || p.getX() > table.maxSelectionColumn) { // switch to column selection mode and select column if (table .getSelectionType() != MyTableInterface.COLUMN_SELECT) { table.setSelectionType(MyTableInterface.COLUMN_SELECT); } // selectNone(); table.setColumnSelectionInterval(p.getX(), p.getX()); } // show contextMenu SpreadsheetContextMenuD contextMenu = new SpreadsheetContextMenuD( table); JPopupMenu popup = (JPopupMenu) contextMenu.getMenuContainer(); popup.show(e.getComponent(), e.getX(), e.getY()); } else if (isResizing) { if (e.getClickCount() == 2) { return; } int x = e.getX(); int y = e.getY(); GPoint point = table.getIndexFromPixel(x, y); if (point == null) { return; } GPoint point2 = table.getPixel(point.getX(), point.getY(), false); int column = point.getX(); if (x < point2.getX() - 3) { --column; } if (x <= 0) { x = 0; // G.Sturr 2010-4-10 prevent x=-1 with very small row // size } int width = table.getColumnModel().getColumn(column).getWidth(); int[] selected = table.getSelectedColumns(); boolean in = false; for (int i = 0; i < selected.length; ++i) { if (column == selected[i]) { in = true; } } if (!in) { return; } for (int i = 0; i < selected.length; ++i) { table.getColumnModel().getColumn(selected[i]) .setPreferredWidth(width); } } // ensure that table header keeps the focus table.getTableHeader().requestFocus(); } // ========================================================= // MouseMotion Listener Methods // ========================================================= @Override public void mouseDragged(MouseEvent e) { if (AppD.isRightClick(e)) { return; // G.Sturr 2009-9-30 } if (isResizing) { return; } int x = e.getX(); int y = e.getY(); GPoint point = table.getIndexFromPixel(x, y); if (point != null) { int column = point.getX(); if (column0 == -1) { column0 = column; } table.setColumnSelectionInterval(column0, column); // repaint(); } } @Override public void mouseMoved(MouseEvent e) { // handles mouse over a trace button int column = -1; boolean isOver = false; Point mouseLoc = e.getPoint(); GPoint cellLoc = table.getIndexFromPixel(mouseLoc.x, mouseLoc.y); if (cellLoc != null) { column = cellLoc.x; if (app.getTraceManager().isTraceColumn(column)) { // adjust mouseLoc to the coordinate space of this column header mouseLoc.x = mouseLoc.x - table.getCellRect(0, column, true).x; // int lowBound = table.getCellRect(0, column, true).x + 3; // isOver = mouseLoc.x > lowBound && mouseLoc.x < lowBound + 24; // Point sceeenMouseLoc = // MouseInfo.getPointerInfo().getLocation(); isOver = ((ColumnHeaderRenderer) table.getColumnModel() .getColumn(column).getHeaderRenderer()) .isOverTraceButton(column, mouseLoc, table.getColumnModel().getColumn(column) .getHeaderValue()); } } // System.out.println("isOver = " + isOver ); if (isOver && overTraceButtonColumn != column) { overTraceButtonColumn = column; if (table.getTableHeader() != null) { table.getTableHeader().resizeAndRepaint(); } } if (!isOver && overTraceButtonColumn > 0) { overTraceButtonColumn = -1; if (table.getTableHeader() != null) { table.getTableHeader().resizeAndRepaint(); } } } // ========================================================= // Key Listener Methods // ========================================================= @Override public void keyTyped(KeyEvent e) { // only handle key pressed } @Override public void keyPressed(KeyEvent e) { boolean metaDown = AppD.isControlDown(e); boolean altDown = e.isAltDown(); boolean shiftDown = e.isShiftDown(); int keyCode = e.getKeyCode(); switch (keyCode) { default: // do nothing break; case KeyEvent.VK_LEFT: if (shiftDown) { // extend the column selection int column = table.getColumnModel().getSelectionModel() .getLeadSelectionIndex(); table.changeSelection(-1, column - 1, false, true); } else { // select topmost cell in first column to the left of the // selection if (table.minSelectionColumn > 0) { table.setSelection(table.minSelectionColumn - 1, 0); } else { table.setSelection(table.minSelectionColumn, 0); } table.requestFocus(); } break; case KeyEvent.VK_RIGHT: if (shiftDown) { // extend the column selection int column = table.getColumnModel().getSelectionModel() .getLeadSelectionIndex(); table.changeSelection(-1, column + 1, false, true); } else { // select topmost cell in first column to the right of the // selection if (table.minSelectionColumn > 0) { table.setSelection(table.minSelectionColumn + 1, 0); } else { table.setSelection(table.minSelectionColumn, 0); } table.requestFocus(); } break; case KeyEvent.VK_C: // control + c // Application.debug(minSelectionColumn); // Application.debug(maxSelectionColumn); if (metaDown && table.minSelectionColumn != -1 && table.maxSelectionColumn != -1) { table.copyPasteCut.copy(table.minSelectionColumn, 0, table.maxSelectionColumn, model.getRowCount() - 1, altDown); e.consume(); } break; case KeyEvent.VK_V: // control + v if (metaDown && table.minSelectionColumn != -1 && table.maxSelectionColumn != -1) { boolean storeUndo = table.copyPasteCut.paste( table.minSelectionColumn, 0, table.maxSelectionColumn, model.getRowCount() - 1); if (storeUndo) { app.storeUndoInfo(); } view.getRowHeader().revalidate(); e.consume(); } break; case KeyEvent.VK_X: // control + x if (metaDown && table.minSelectionColumn != -1 && table.maxSelectionColumn != -1) { boolean storeUndo = table.copyPasteCut.cut( table.minSelectionColumn, 0, table.maxSelectionColumn, model.getRowCount() - 1); if (storeUndo) { app.storeUndoInfo(); } e.consume(); } break; case KeyEvent.VK_BACK_SPACE: // delete case KeyEvent.VK_DELETE: // delete boolean storeUndo = table.copyPasteCut.delete( table.minSelectionColumn, 0, table.maxSelectionColumn, model.getRowCount() - 1); if (storeUndo) { app.storeUndoInfo(); } break; } } @Override public void keyReleased(KeyEvent e) { // only handle key pressed } // ========================================================= // Renderer Class // ========================================================= protected class ColumnHeaderRenderer extends JPanel implements TableCellRenderer { private static final long serialVersionUID = 1L; private Color defaultBackground; private JLabel lblHeader; private JButton btnTrace; private BorderLayout layout; private ImageIcon pauseIcon = app .getScaledIcon(GuiResourcesD.SPREADSHEETTRACE_PAUSE); private ImageIcon recordIcon = app .getScaledIcon(GuiResourcesD.SPREADSHEETTRACE_RECORD); public ColumnHeaderRenderer() { super(new BorderLayout()); lblHeader = new JLabel(); lblHeader.setHorizontalAlignment(SwingConstants.CENTER); this.add(lblHeader, BorderLayout.CENTER); btnTrace = new JButton(); btnTrace.setBorderPainted(false); btnTrace.setContentAreaFilled(false); btnTrace.setPreferredSize(new Dimension(18, 18)); setOpaque(true); defaultBackground = MyTableD.BACKGROUND_COLOR_HEADER; setBorder(BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder(0, 0, 1, 1, MyTableD.HEADER_GRID_COLOR), BorderFactory.createEmptyBorder(0, 5, 0, 0))); layout = (BorderLayout) this.getLayout(); } @Override public Component getTableCellRendererComponent(JTable table0, Object value, boolean isSelected, boolean hasFocus, int rowIndex, int colIndex) { MyTableD table1; if (table0 instanceof MyTableD) { table1 = (MyTableD) table0; } else { return null; } lblHeader.setFont(app.getPlainFont()); lblHeader.setText(value.toString()); if (table1.getSelectionType() == MyTableInterface.ROW_SELECT) { setBackground(defaultBackground); } else { if (table1.selectedColumnSet.contains(colIndex) || (colIndex >= table1.minSelectionColumn && colIndex <= table1.maxSelectionColumn)) { setBackground(MyTableD.SELECTED_BACKGROUND_COLOR_HEADER); } else { setBackground(defaultBackground); } } // add/remove trace button if (app.hasTraceManager()) { SpreadsheetTraceSettings t = app.getTraceManager() .getTraceSettings(colIndex); if (t == null) { // no geo traced in this column if (layout.getLayoutComponent(loc.borderWest()) != null) { this.remove( layout.getLayoutComponent(loc.borderWest())); } } else { this.add(btnTrace, loc.borderWest()); // set icon if (t.pause) { btnTrace.setIcon(pauseIcon); setToolTipText( loc.getMenuTooltip("TraceToSpreadsheet")); // button // switches // back // to // record } else { btnTrace.setIcon(recordIcon); setToolTipText(loc.getMenuTooltip("Pause")); // button // pauses // the // trace } } } return this; } private Rectangle rect = new Rectangle(); /** * Returns true if the given mouse location (in local coordinates of the * header component) is over a trace button. * * @param colIndex * @param loc * @param value * @return */ public boolean isOverTraceButton(int colIndex, Point loc, Object value) { if (!app.getTraceManager().isTraceColumn(colIndex)) { return false; } try { getTableCellRendererComponent(table, value, false, false, -1, colIndex); // layout.getLayoutComponent(app.borderWest()).getBounds(rect); btnTrace.getBounds(rect); // System.out.println(loc.toString() + " : " + // rect.toString()); return rect.contains(loc); } catch (Exception e) { // e.printStackTrace(); } return false; } } }