/* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores * CA 94065 USA or visit www.oracle.com if you need additional information or * have any questions. */ package com.sun.lwuit.resources.editor.editors; import java.awt.event.MouseEvent; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.TreeMap; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.event.MouseInputListener; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; /** * Base class for forms containing common functionality for all forms * * @author Shai Almog */ public class BaseForm extends JPanel { private static final TableCellRenderer headerInstance = new HeaderRenderer(); private static final Icon DOWN_ICON = new ImageIcon(BaseForm.class.getResource("/downarrow.gif")); private static final Icon UP_ICON = new ImageIcon(BaseForm.class.getResource("/uparrow.gif")); /** * Returns the selection in the JXTable mapped to the model since the table * might be sorted or filtered */ protected int getModelSelection(JTable t) { EditorTable table = (EditorTable)t; int r = table.getSelectedRow(); if(r != -1) { r = table.convertRow(r); } return r; } /** * Create an empty sortable JXTable with sorting etc. enabled for a common look */ protected JTable createTable() { EditorTable table = new EditorTable(); try { table.getAccessibleContext().setAccessibleName("Table"); table.getAccessibleContext().setAccessibleDescription("Table"); table.setFillsViewportHeight(true); } catch(Throwable err) { // doesn't exist in Java 5 } table.setShowGrid(false); return table; } /** * Binds search filtering to a JTable */ protected void bindSearch(final JTextField search, JTable table) { final EditorTable jx = (EditorTable) table; search.getDocument().addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { updateSearch(); } public void removeUpdate(DocumentEvent e) { updateSearch(); } public void changedUpdate(DocumentEvent e) { updateSearch(); } private void updateSearch() { String t = search.getText(); jx.filter(t); } }); } /** * Implementation of the JTable providing sorting, highlighting and filtering */ public static class EditorTable extends JTable { private List<Integer> rows = null; private int sortColumn = -1; private String filter; private boolean ascending; private TableModel internalModel; // initialize a table model that implements sorting and filtering public void setModel(TableModel model) { internalModel = model; super.setModel(new Wrapper()); } public TableModel getInternalModel() { return internalModel; } protected JTableHeader createDefaultTableHeader() { return new EditorTableHeader(columnModel); } public void sort(int column) { if(sortColumn == column) { ascending = !ascending; } else { ascending = true; } sortColumn = column; if(sortColumn == -1) { if(filter == null || filter.length() == 0) { rows = null; } else { rows = new ArrayList<Integer>(); for(int iter = 0 ; iter < internalModel.getRowCount() ; iter++) { if(checkFilter(iter)) { rows.add(iter); } } } } else { TreeMap<Object, List<Integer>> sorter = new TreeMap<Object, List<Integer>>(new Comparator<Object>() { public int compare(Object o1, Object o2) { if(o1 == null) { return -1; } if(o2 == null) { return 1; } if(o1.getClass() == o2.getClass()) { if(o1 instanceof Comparable) { if(o1 instanceof String && o2 instanceof String) { return String.CASE_INSENSITIVE_ORDER.compare((String)o1, (String)o2); } return ((Comparable)o1).compareTo(o2); } } return -1; } }); for(int iter = 0 ; iter < internalModel.getRowCount() ; iter++) { if(checkFilter(iter)) { Object o = internalModel.getValueAt(iter, column); if(sorter.containsKey(o)) { sorter.get(o).add(iter); } else { List<Integer> l = new ArrayList<Integer>(); l.add(iter); sorter.put(o, l); } } } rows = new ArrayList<Integer>(); for(List<Integer> entries : sorter.values()) { rows.addAll(entries); } if(!ascending) { Collections.reverse(rows); } } ((Wrapper)getModel()).tableChanged(new TableModelEvent(getModel(), -1, -1, -1, TableModelEvent.ALL_COLUMNS)); } public boolean isAscending() { return ascending; } private boolean checkFilter(int row) { if(filter != null && filter.length() > 0) { for(int iter = 0 ; iter < internalModel.getColumnCount() ; iter++) { Object o = internalModel.getValueAt(row, iter); if(o instanceof String) { if(((String)o).toUpperCase().indexOf(filter) > -1) { return true; } } } return false; } return true; } public void filter(String text) { filter = text.toUpperCase(); ascending = !ascending; sort(sortColumn); } public int getSortedColumn() { return sortColumn; } public int convertRow(int i) { return ((Wrapper)getModel()).mapRow(i); } class Wrapper implements TableModel, TableModelListener { private List<TableModelListener> listeners = new ArrayList<TableModelListener>(); public Wrapper() { internalModel.addTableModelListener(this); } public int getRowCount() { if(rows == null) { return internalModel.getRowCount(); } return rows.size(); } public int getColumnCount() { return internalModel.getColumnCount(); } public String getColumnName(int columnIndex) { return internalModel.getColumnName(columnIndex); } public Class<?> getColumnClass(int columnIndex) { return internalModel.getColumnClass(columnIndex); } public boolean isCellEditable(int rowIndex, int columnIndex) { if(rows == null) { return internalModel.isCellEditable(rowIndex, columnIndex); } else { return internalModel.isCellEditable(rows.get(rowIndex), columnIndex); } } public Object getValueAt(int rowIndex, int columnIndex) { if(rows == null) { return internalModel.getValueAt(rowIndex, columnIndex); } else { return internalModel.getValueAt(rows.get(rowIndex), columnIndex); } } public void setValueAt(Object aValue, int rowIndex, int columnIndex) { if(rows == null) { internalModel.setValueAt(aValue, rowIndex, columnIndex); } else { internalModel.setValueAt(aValue, rows.get(rowIndex), columnIndex); } } public void addTableModelListener(TableModelListener l) { if(!listeners.contains(l)) { listeners.add(l); } } public void removeTableModelListener(TableModelListener l) { listeners.remove(l); } private int mapRow(int row) { if(row < 0 || rows == null) { return row; } return rows.get(row); } private int reverseMapRow(int row) { for(int iter = 0 ; iter < rows.size() ; iter++) { if(rows.get(iter).intValue() == row) { return iter; } } return -1; } public void tableChanged(TableModelEvent e) { if(rows != null) { e = new TableModelEvent(this, reverseMapRow(e.getFirstRow()), reverseMapRow(e.getLastRow()), e.getColumn(), e.getType()); } for(TableModelListener l : listeners) { l.tableChanged(e); } } } } private static class EditorTableHeader extends JTableHeader implements MouseInputListener{ private TableColumn cachedResizingColumn; public EditorTableHeader(TableColumnModel columnModel) { super(columnModel); } public void setTable(JTable table) { super.setTable(table); installHeaderListener(); } private int getViewIndexForColumn(TableColumn col) { if (col == null) { return -1; } TableColumnModel cm = getColumnModel(); for (int column = 0; column < cm.getColumnCount(); column++) { if (cm.getColumn(column) == col) { return column; } } return -1; } protected TableCellRenderer createDefaultRenderer() { return headerInstance; } protected void installHeaderListener() { addMouseListener(this); addMouseMotionListener(this); } protected void uninstallHeaderListener() { removeMouseListener(this); removeMouseMotionListener(this); } public void mouseClicked(MouseEvent e) { if (shouldIgnore(e)) { return; } if (isInResizeRegion(e)) { doResize(e); } else { doSort(e); } } private boolean shouldIgnore(MouseEvent e) { return !SwingUtilities.isLeftMouseButton(e) || !table.isEnabled(); } private void doSort(MouseEvent e) { EditorTable table = (EditorTable)getTable(); if (e.getClickCount() != 1) { return; } if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) == MouseEvent.SHIFT_DOWN_MASK) { table.sort(-1); } else { int column = columnAtPoint(e.getPoint()); if (column >= 0) { table.sort(column); } uncacheResizingColumn(); } repaint(); } private void doResize(MouseEvent e) { if (e.getClickCount() != 2) return; int column = getViewIndexForColumn(cachedResizingColumn); uncacheResizingColumn(); } public void mouseReleased(MouseEvent e) { cacheResizingColumn(e); } public void mousePressed(MouseEvent e) { cacheResizingColumn(e); } private void cacheResizingColumn(MouseEvent e) { if (e.getClickCount() != 1) return; TableColumn column = getResizingColumn(); if (column != null) { cachedResizingColumn = column; } } private void uncacheResizingColumn() { cachedResizingColumn = null; } private boolean isInResizeRegion(MouseEvent e) { return cachedResizingColumn != null; // inResize; } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { uncacheResizingColumn(); } public void mouseDragged(MouseEvent e) { uncacheResizingColumn(); } public void mouseMoved(MouseEvent e) { } } private static class HeaderRenderer extends JComponent implements TableCellRenderer { private TableCellRenderer internal; public HeaderRenderer() { JTableHeader header = new JTableHeader(); internal = header.getDefaultRenderer(); } public java.awt.Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowIndex, int columnIndex) { java.awt.Component cmp = internal.getTableCellRendererComponent(table, value, isSelected, hasFocus, rowIndex, columnIndex); int col = ((EditorTable) table).getSortedColumn(); if (col == columnIndex) { ((JLabel)cmp).setIcon(((EditorTable) table).isAscending() ? UP_ICON : DOWN_ICON); } else { ((JLabel)cmp).setIcon(null); } return cmp; } } }