001: /*
002: * @(#)TableSearchable.java
003: *
004: * Copyright 2002 - 2004 JIDE Software Inc. All rights reserved.
005: */
006: package com.jidesoft.swing;
007:
008: import com.jidesoft.swing.event.SearchableEvent;
009:
010: import javax.swing.*;
011: import javax.swing.event.TableModelEvent;
012: import javax.swing.event.TableModelListener;
013: import javax.swing.table.TableModel;
014: import java.awt.event.KeyEvent;
015: import java.beans.PropertyChangeEvent;
016: import java.beans.PropertyChangeListener;
017:
018: /**
019: * <code>TableSearchable</code> is an concrete implementation of {@link Searchable}
020: * that enables the search function in JTable.
021: * <p>It's very simple to use it. Assuming you have a JTable, all you need to do is to
022: * call
023: * <code><pre>
024: * JTable table = ....;
025: * TableSearchable searchable = new TableSearchable(table);
026: * </pre></code>
027: * Now the JTable will have the search function.
028: * <p/>
029: * As JTable is a two dimension data, the search is a little different from JList and JTree which both have
030: * one dimension data. So there is a little work you need to do in order to convert from two dimension data
031: * to one dimension data. We use the selection mode to determine how to convert. There is a special property
032: * called mainIndex. You can set it using setMainIndex(). If the JTable is in row selection mode, mainIndex will
033: * be the column that you want search at. Please note you can change mainIndex at any time.
034: * <p/>
035: * On the other hand, if the JTable is in column selection mode, mainIndex will be the row that you want search at.
036: * There is one more case when cell selection is enabled. In this case, mainIndex will be ignore; all cells will be searched.
037: * <p/>
038: * In three cases above, the keys for find next and find previous are different too. In row selection mode, up/down arrow are the keys.
039: * In column selection mode, left/right arrow are keys. In cell selection mode, both up and left arrow are keys to
040: * find previous occurence, both down and right arrow are keys to find next occurence.
041: * <p/>
042: * In addition, you might need to override convertElementToString() to provide you own algorithm to do the conversion.
043: * <code><pre>
044: * JTable table = ....;
045: * TableSearchable searchable = new TableSearchable(table) {
046: * protected String convertElementToString(Object object) {
047: * ...
048: * }
049: * };
050: * </pre></code>
051: * <p/>
052: * Additional customization can be done on the base Searchable class such as background and foreground color, keystrokes,
053: * case sensitivity,
054: */
055: public class TableSearchable extends Searchable implements
056: TableModelListener, PropertyChangeListener {
057:
058: private int _mainIndex = 0;
059:
060: public TableSearchable(JTable table) {
061: super (table);
062: table.getModel().addTableModelListener(this );
063: table.addPropertyChangeListener("model", this );
064: }
065:
066: @Override
067: public void uninstallListeners() {
068: super .uninstallListeners();
069: if (_component instanceof JTable) {
070: ((JTable) _component).getModel().removeTableModelListener(
071: this );
072: }
073: _component.removePropertyChangeListener("model", this );
074: }
075:
076: @Override
077: protected void setSelectedIndex(int index, boolean incremental) {
078: int majorIndex, minorIndex;
079: JTable table = ((JTable) _component);
080: if (isColumnSelectionAllowed(table)) {
081: majorIndex = index;
082: minorIndex = getMainIndex();
083: addTableSelection(table, majorIndex, minorIndex,
084: incremental);
085: } else if (isRowSelectionAllowed(table)) {
086: majorIndex = index;
087: minorIndex = getMainIndex();
088: addTableSelection(table, majorIndex, minorIndex,
089: incremental);
090: } else { // cell selection allowed
091: majorIndex = index
092: / table.getColumnModel().getColumnCount();
093: minorIndex = index
094: % table.getColumnModel().getColumnCount();
095: addTableSelection(table, majorIndex, minorIndex,
096: incremental);
097: }
098: }
099:
100: /**
101: * Selects the cell at the specified row and column index. If incremental is true, the previous selection will not be cleared.
102: * This method will use {@link JTable#changeSelection(int,int,boolean,boolean)} method to select the cell
103: * if the row and column index is in the range and the cell was not selected. The last two parameters of changeSelection
104: * is true and false respectively.
105: *
106: * @param table the table
107: * @param rowIndex the row index of the cell.
108: * @param columnIndex the column index of the cell
109: * @param incremental false to clear all previous selection. True to keep the previous selection.
110: */
111: protected void addTableSelection(JTable table, int rowIndex,
112: int columnIndex, boolean incremental) {
113: if (!incremental)
114: table.clearSelection();
115: if (rowIndex >= 0 && columnIndex >= 0
116: && rowIndex < table.getRowCount()
117: && columnIndex < table.getColumnCount()
118: && !table.isCellSelected(rowIndex, columnIndex)) {
119: table.changeSelection(rowIndex, columnIndex, true, false);
120: }
121: }
122:
123: /**
124: * Is the column selection allowed?
125: *
126: * @param table the table.
127: * @return true if the table is the column selection.
128: */
129: protected boolean isColumnSelectionAllowed(JTable table) {
130: return getMainIndex() != -1
131: && (table.getColumnSelectionAllowed() && !table
132: .getRowSelectionAllowed());
133: }
134:
135: /**
136: * Is the row selection allowed?
137: *
138: * @param table the table.
139: * @return true if the table is the row selection.
140: */
141: protected boolean isRowSelectionAllowed(JTable table) {
142: return getMainIndex() != -1
143: && (!table.getColumnSelectionAllowed() && table
144: .getRowSelectionAllowed());
145: }
146:
147: /**
148: * Gets the selected index.
149: *
150: * @return the selected index.
151: */
152: @Override
153: protected int getSelectedIndex() {
154: JTable table = ((JTable) _component);
155: if (isColumnSelectionAllowed(table)) {
156: return table.getColumnModel().getSelectionModel()
157: .getLeadSelectionIndex();
158: } else if (isRowSelectionAllowed(table)) {
159: return table.getSelectionModel().getLeadSelectionIndex();
160: } else { // cell selection allowed
161: return table.getSelectionModel().getLeadSelectionIndex()
162: * table.getColumnCount()
163: + table.getColumnModel().getSelectionModel()
164: .getLeadSelectionIndex();
165: }
166: }
167:
168: @Override
169: protected Object getElementAt(int index) {
170: TableModel model = ((JTable) _component).getModel();
171: JTable table = ((JTable) _component);
172: if (isColumnSelectionAllowed(table)) { // column selection mode
173: return getValueAt(model, getMainIndex(), table
174: .convertColumnIndexToModel(index));
175: } else if (isRowSelectionAllowed(table)) { // row selection mode
176: return getValueAt(model, index, table
177: .convertColumnIndexToModel(getMainIndex()));
178: } else { // cell selection allowed
179: int columnIndex = index
180: % table.getColumnModel().getColumnCount();
181: int rowIndex = index
182: / table.getColumnModel().getColumnCount();
183: return getValueAt(model, rowIndex, table
184: .convertColumnIndexToModel(columnIndex));
185: }
186: }
187:
188: private Object getValueAt(TableModel model, int rowIndex,
189: int columnIndex) {
190: if (rowIndex >= 0 && rowIndex < model.getRowCount()
191: && columnIndex >= 0
192: && columnIndex < model.getColumnCount()) {
193: return model.getValueAt(rowIndex, columnIndex);
194: } else {
195: return null;
196: }
197: }
198:
199: @Override
200: protected int getElementCount() {
201: TableModel model = ((JTable) _component).getModel();
202: JTable table = ((JTable) _component);
203: if (isColumnSelectionAllowed(table)) {
204: return table.getColumnModel().getColumnCount();
205: } else if (isRowSelectionAllowed(table)) {
206: return model.getRowCount();
207: } else { // cell selection allowed
208: return table.getColumnModel().getColumnCount()
209: * model.getRowCount();
210: }
211: }
212:
213: @Override
214: protected String convertElementToString(Object item) {
215: if (item != null) {
216: return item.toString();
217: } else {
218: return "";
219: }
220: }
221:
222: /**
223: * Gets the index of the column to be searched.
224: *
225: * @return the index of the column to be searched.
226: */
227: public int getMainIndex() {
228: return _mainIndex;
229: }
230:
231: /**
232: * Sets the main index. Main index is the column index which you want to be searched.
233: *
234: * @param mainIndex the index of the column to be searched. If -1, all columns will be searched.
235: */
236: public void setMainIndex(int mainIndex) {
237: int old = _mainIndex;
238: if (old != mainIndex) {
239: _mainIndex = mainIndex;
240: hidePopup();
241: }
242: }
243:
244: @Override
245: protected boolean isFindNextKey(KeyEvent e) {
246: int keyCode = e.getKeyCode();
247: JTable table = ((JTable) _component);
248: if (isColumnSelectionAllowed(table)) {
249: return keyCode == KeyEvent.VK_RIGHT;
250: } else if (isRowSelectionAllowed(table)) {
251: return keyCode == KeyEvent.VK_DOWN;
252: } else { // cell selection allowed
253: return keyCode == KeyEvent.VK_DOWN
254: || keyCode == KeyEvent.VK_RIGHT;
255: }
256: }
257:
258: @Override
259: protected boolean isFindPreviousKey(KeyEvent e) {
260: int keyCode = e.getKeyCode();
261: JTable table = ((JTable) _component);
262: if (isColumnSelectionAllowed(table)) {
263: return keyCode == KeyEvent.VK_LEFT;
264: } else if (isRowSelectionAllowed(table)) {
265: return keyCode == KeyEvent.VK_UP;
266: } else { // cell selection allowed
267: return keyCode == KeyEvent.VK_UP
268: || keyCode == KeyEvent.VK_LEFT;
269: }
270: }
271:
272: public void tableChanged(TableModelEvent e) {
273: hidePopup();
274: fireSearchableEvent(new SearchableEvent(this ,
275: SearchableEvent.SEARCHABLE_MODEL_CHANGE));
276: }
277:
278: public void propertyChange(PropertyChangeEvent evt) {
279: if ("model".equals(evt.getPropertyName())) {
280: hidePopup();
281:
282: if (evt.getOldValue() instanceof TableModel) {
283: ((TableModel) evt.getOldValue())
284: .removeTableModelListener(this );
285: }
286:
287: if (evt.getNewValue() instanceof TableModel) {
288: ((TableModel) evt.getNewValue())
289: .addTableModelListener(this );
290: }
291: fireSearchableEvent(new SearchableEvent(this ,
292: SearchableEvent.SEARCHABLE_MODEL_CHANGE));
293: }
294: }
295:
296: @Override
297: protected boolean isActivateKey(KeyEvent e) {
298: boolean editable = isSelectedCellEditable();
299: return !editable && super .isActivateKey(e);
300: }
301:
302: /**
303: * Checks if the selected cell is editable. If yes, we will not activate Searchable when key is typed.
304: *
305: * @return true if the selected cell is editable.
306: */
307: protected boolean isSelectedCellEditable() {
308: int selectedRow = ((JTable) _component).getSelectionModel()
309: .getLeadSelectionIndex();
310: int selectedColumn = ((JTable) _component).getColumnModel()
311: .getSelectionModel().getLeadSelectionIndex();
312: return ((JTable) _component).isCellEditable(selectedRow,
313: selectedColumn);
314: }
315: }
|