package org.geogebra.desktop.gui.util; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.event.MouseEvent; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.border.Border; import javax.swing.event.MouseInputAdapter; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import org.geogebra.common.awt.GColor; import org.geogebra.common.gui.util.SelectionTable; import org.geogebra.common.main.GeoGebraColorConstants; import org.geogebra.desktop.main.AppD; /** * Creates a table to display and select images and other custom icons. The * table takes a one dimensional array of data objects as input and then, using * row and column size parameters, displays the data as GeoGebra icons in a 2D * table. User selection is returned as an index to the data array. * * The table is intended for use in a popup menu. * * @author G.Sturr * */ public class SelectionTableD extends JTable { private static final long serialVersionUID = 1L; private AppD app; private MyCellRenderer renderer; private DefaultTableModel model; private int rollOverRow = -1; private int rollOverColumn = -1; private int sliderValue; private int horizontalAlignment = SwingConstants.LEFT; private boolean showSelection = true; public void setShowSelection(boolean showSelection) { this.showSelection = showSelection; } public void setHorizontalAlignment(int horizontalAlignment) { this.horizontalAlignment = horizontalAlignment; } private Object[] data; private int numRows, numColumns, rowHeight, columnWidth; public int getColumnWidth() { return columnWidth; } private Dimension iconSize; private SelectionTable mode; public void setFgColor(GColor fgColor) { repaint(); } boolean useColorSwatchBorder = false; public void setUseColorSwatchBorder(boolean useColorSwatchBorder) { this.useColorSwatchBorder = useColorSwatchBorder; setCellDimensions(); } private String[] toolTipArray = null; /** * sets the tooTip strings for the selection table; the toolTipArray should * have a 1-1 correspondence with the data array * * @param toolTipArray */ public void setToolTipArray(String[] toolTipArray) { this.toolTipArray = toolTipArray; } /******************************************************** * Constructor * * @param app * @param data0 * @param rows0 * @param columns0 * @param iconSize * @param mode */ public SelectionTableD(AppD app, Object[] data0, int rows0, int columns0, Dimension iconSize, SelectionTable mode) { int columns = columns0; int rows = rows0; this.app = app; this.mode = mode; this.iconSize = iconSize; if (mode == SelectionTable.MODE_LATEX) { this.data = createLatexIconArray((String[]) data0); } else { this.data = data0; } // ======================================= // determine the dimensions of the table // rows = -1, cols = -1 ==> square table to fit data if (rows == -1 && columns == -1) { rows = (int) Math.floor(Math.sqrt(data.length)); columns = (int) Math.ceil(data.length / (double) rows); } // rows = -1 ==> fixed cols, rows added to fit data else if (rows == -1) { rows = (int) (Math.ceil(data.length / (double) columns)); } // cols = -1 ==> fixed rows, cols added to fit data else if (columns == -1) { columns = (int) (Math.ceil(data.length / (double) rows)); } numRows = rows; numColumns = columns; // ======================================= // set the table model with the data populateModel(data); // ======================================= // set cell renderer renderer = new MyCellRenderer(); this.setDefaultRenderer(Object.class, renderer); // ======================================= // set various display properties this.setAutoResizeMode(AUTO_RESIZE_OFF); this.setAutoCreateColumnsFromModel(false); setShowGrid(false); setGridColor(Color.GRAY); this.setTableHeader(null); this.setBorder(null); setCellDimensions(); setFont(app.getPlainFont()); setAutoResizeMode(JTable.AUTO_RESIZE_OFF); // set cell selection properties setCellSelectionEnabled(true); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // ======================================= // add listener for mouse roll over RollOverListener rollOverListener = new RollOverListener(); addMouseMotionListener(rollOverListener); addMouseListener(rollOverListener); } /** Disables cell editing */ @Override public boolean isCellEditable(int rowIndex, int vColIndex) { return false; } /** Loads a one dimensional array of data into the table model */ public void populateModel(Object[] data1) { model = new DefaultTableModel(numRows, numColumns); int r = 0; int c = 0; for (int i = 0; i < Math.min(data1.length, this.numRows * this.numColumns); i++) { model.setValueAt(data1[i], r, c); ++c; if (c == this.numColumns) { c = 0; ++r; } } setModel(model); } public ImageIcon[] createLatexIconArray(String[] symbols) { ImageIcon[] iconArray = new ImageIcon[symbols.length]; for (int i = 0; i < symbols.length; i++) { iconArray[i] = GeoGebraIconD.createLatexIcon(app, symbols[i], app.getPlainFont(), Color.BLACK, null); } return iconArray; } // set cell dimensions private void setCellDimensions() { int padding = useColorSwatchBorder ? 1 : 4; // match row height to specified icon height // when mode=text then let font size adjust row height automatically if (!(mode == SelectionTable.MODE_TEXT || mode == SelectionTable.MODE_LATEX)) { rowHeight = iconSize.height + padding; } else { rowHeight = getMaxRowHeight(this) + padding; } setRowHeight(rowHeight); // set the column widths columnWidth = iconSize.width + padding; int w; for (int i = 0; i < getColumnCount(); ++i) { // for mode=text, adjust column width to the maximum width in the // column if (mode == SelectionTable.MODE_TEXT || mode == SelectionTable.MODE_LATEX) { w = getMaxColumnWidth(this, i); getColumnModel().getColumn(i).setPreferredWidth(w); columnWidth = Math.max(w, columnWidth); } else { getColumnModel().getColumn(i).setPreferredWidth(columnWidth); } } repaint(); } public void updateFonts() { setFont(app.getPlainFont()); setCellDimensions(); } // ============================================== // Listeners // ============================================== private class RollOverListener extends MouseInputAdapter { @Override public void mouseExited(MouseEvent e) { rollOverRow = -1; rollOverColumn = -1; repaint(); } @Override public void mouseMoved(MouseEvent e) { int row = rowAtPoint(e.getPoint()); int column = columnAtPoint(e.getPoint()); if (row != rollOverRow || column != rollOverColumn) { rollOverRow = row; rollOverColumn = column; if (toolTipArray != null) { int index = getColumnCount() * row + column; if (index < data.length) { setToolTipText(toolTipArray[index]); } else { setToolTipText(null); } } repaint(); } } } // ============================================== // Getters/Setters // ============================================== public int getSelectedIndex() { int index = this.getColumnCount() * this.getSelectedRow() + this.getSelectedColumn(); if (index < -1) { index = -1; } return index; } public void setSelectedIndex(int index) { if (index == -1) { this.clearSelection(); return; } int row = index / getColumnCount(); int column = index - (row * getColumnCount()); this.changeSelection(row, column, false, false); rollOverRow = -1; rollOverColumn = -1; } public Object getSelectedValue() { if (getSelectedRow() != -1 && getSelectedColumn() != -1) { return model.getValueAt(getSelectedRow(), getSelectedColumn()); } return null; } public int getSliderValue() { return sliderValue; } public void setSliderValue(int sliderValue) { this.sliderValue = sliderValue; } public Object[] getData() { return data; } public ImageIcon getDataIcon(Object value) { ImageIcon icon = null; if (value == null) { return GeoGebraIconD.createEmptyIcon(1, 1); // GeoGebraIcon.createStringIcon("\u00D8", app.getPlainFont(), true, // false, true, iconSize , Color.GRAY, null); } switch (mode) { default: // do nothing break; case MODE_ICON: case MODE_LATEX: icon = (ImageIcon) value; break; } return icon; } // ============================================== // Cell Renderer // ============================================== class MyCellRenderer extends JLabel implements TableCellRenderer { private static final long serialVersionUID = 1L; private Border normalBorder, selectedBorder, rollOverBorder, paddingBorder; private Color selectionColor, rollOverColor; public MyCellRenderer() { // TODO --- selection color should be centralized, not from // spreadsheet selectionColor = org.geogebra.desktop.awt.GColorD.getAwtColor( GeoGebraColorConstants.TABLE_SELECTED_BACKGROUND_COLOR); rollOverColor = Color.LIGHT_GRAY; paddingBorder = BorderFactory.createEmptyBorder(0, 5, 0, 5); rollOverBorder = BorderFactory.createLineBorder(Color.GRAY, 3); normalBorder = BorderFactory.createLineBorder(Color.GRAY, 1); if (mode == SelectionTable.MODE_LATEX) { selectedBorder = rollOverBorder; } else { selectedBorder = BorderFactory.createLineBorder(Color.BLACK, 3); } setOpaque(true); setHorizontalAlignment(CENTER); setVerticalAlignment(CENTER); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { setAlignmentX(CENTER_ALIGNMENT); setAlignmentY(CENTER_ALIGNMENT); if (mode == SelectionTable.MODE_TEXT) { this.setHorizontalAlignment(horizontalAlignment); this.setVerticalAlignment(SwingConstants.CENTER); setText((String) value); setFont(app.getFontCanDisplayAwt((String) value, Font.PLAIN)); setBorder(paddingBorder); } else { setText(""); setIcon(getDataIcon(value)); } if (useColorSwatchBorder) { setBackground(table.getBackground()); if (showSelection && isSelected) { setBorder(selectedBorder); } else if (row == rollOverRow && column == rollOverColumn) { setBorder(rollOverBorder); } else { setBorder(normalBorder); } } else { if (showSelection && isSelected) { setBackground(selectionColor); } else if (row == rollOverRow && column == rollOverColumn) { setBackground(rollOverColor); } else { setBackground(table.getBackground()); } } return this; } } /** * Finds the maximum preferred width of a column. */ public int getMaxColumnWidth(JTable table, int column) { // iterate through the rows and find the preferred width int maxPrefWidth = 0; int colPrefWidth = 0; for (int row = 0; row < table.getRowCount(); row++) { if (table.getValueAt(row, column) != null) { colPrefWidth = (int) table.getCellRenderer(row, column) .getTableCellRendererComponent(table, table.getValueAt(row, column), false, false, row, column) .getPreferredSize().getWidth(); maxPrefWidth = Math.max(maxPrefWidth, colPrefWidth); } } return maxPrefWidth + table.getIntercellSpacing().width; } /** * Finds the maximum preferred height of all cells. */ public int getMaxRowHeight(JTable table) { // iterate through all cells int maxPrefHeight = 0; int cellPrefHeight = 0; for (int r = 0; r < table.getRowCount(); r++) { for (int c = 0; c < table.getColumnCount(); c++) { if (table.getValueAt(r, c) != null) { cellPrefHeight = (int) table.getCellRenderer(r, c) .getTableCellRendererComponent(table, table.getValueAt(r, c), false, false, r, c) .getPreferredSize().getHeight(); maxPrefHeight = Math.max(maxPrefHeight, cellPrefHeight); } } } return maxPrefHeight + table.getIntercellSpacing().height; } }