PageRenderTime 53ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/gui/FilteredTableModel.java

#
Java | 298 lines | 150 code | 34 blank | 114 comment | 27 complexity | 6563d7245567d0cefd3670a3340c2c74 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * FilteredTableModel.java - A Filtered table model decorator
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2007 Shlomy Reinstein
  7. * Copyright (C) 2007 Matthieu Casanova
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit.gui;
  24. import javax.swing.*;
  25. import javax.swing.event.TableModelEvent;
  26. import javax.swing.event.TableModelListener;
  27. import javax.swing.table.AbstractTableModel;
  28. import javax.swing.table.TableModel;
  29. import java.util.*;
  30. /**
  31. * This TableModel delegates another model to add some filtering features to any
  32. * JTable.
  33. * To use it you must implement the abstract method passFilter().
  34. * This method is called for each row, and must return true if the row should be
  35. * visible, and false otherwise.
  36. * It is also possible to override the method prepareFilter() that allow you to
  37. * transform the filter String. Usually you can return it as lowercase
  38. * <p/>
  39. * Here is an example of how to use it extracted from the InstallPanel
  40. * <code>
  41. * PluginTableModel tableModel = new PluginTableModel();
  42. * filteredTableModel = new FilteredTableModel<PluginTableModel>(tableModel)
  43. * {
  44. * public String prepareFilter(String filter)
  45. * {
  46. * return filter.toLowerCase();
  47. * }
  48. * <p/>
  49. * public boolean passFilter(int row, String filter)
  50. * {
  51. * String pluginName = (String) delegated.getValueAt(row, 1);
  52. * return pluginName.toLowerCase().contains(filter);
  53. * }
  54. * };
  55. * table = new JTable(filteredTableModel);
  56. * filteredTableModel.setTable(table);
  57. * </code>
  58. * It is not mandatory but highly recommended to give the JTable instance to the
  59. * model in order to keep the selection after the filter has been updated
  60. *
  61. * @author Shlomy Reinstein
  62. * @author Matthieu Casanova
  63. * @version $Id: Buffer.java 8190 2006-12-07 07:58:34Z kpouer $
  64. * @since jEdit 4.3pre11
  65. */
  66. public abstract class FilteredTableModel<E extends TableModel> extends AbstractTableModel implements TableModelListener
  67. {
  68. /**
  69. * The delegated table model.
  70. */
  71. protected E delegated;
  72. private Vector<Integer> filteredIndices;
  73. /**
  74. * This map contains the delegated indices as key and true indices as values.
  75. */
  76. private Map<Integer, Integer> invertedIndices;
  77. private String filter;
  78. private JTable table;
  79. //{{{ FilteredTableModel() constructor
  80. protected FilteredTableModel(E delegated)
  81. {
  82. this.delegated = delegated;
  83. delegated.addTableModelListener(this);
  84. resetFilter();
  85. } //}}}
  86. //{{{ setTable() method
  87. /**
  88. * Set the JTable that uses this model.
  89. * It is used to restore the selection after the filter has been applied
  90. * If it is null,
  91. *
  92. * @param table the table that uses the model
  93. */
  94. public void setTable(JTable table)
  95. {
  96. if (table.getModel() != this)
  97. throw new IllegalArgumentException("The given table " + table + " doesn't use this model " + this);
  98. this.table = table;
  99. } //}}}
  100. //{{{ getDelegated() method
  101. public E getDelegated()
  102. {
  103. return delegated;
  104. } //}}}
  105. //{{{ setDelegated() method
  106. public void setDelegated(E delegated)
  107. {
  108. this.delegated.removeTableModelListener(this);
  109. delegated.addTableModelListener(this);
  110. this.delegated = delegated;
  111. } //}}}
  112. //{{{ resetFilter() method
  113. private void resetFilter()
  114. {
  115. filteredIndices = null;
  116. } //}}}
  117. //{{{ setFilter() method
  118. public void setFilter(String filter)
  119. {
  120. Set<Integer> selectedIndices = saveSelection();
  121. this.filter = filter;
  122. if (filter != null && filter.length() > 0)
  123. {
  124. int size = delegated.getRowCount();
  125. filter = prepareFilter(filter);
  126. Vector<Integer> indices = new Vector<Integer>(size);
  127. Map<Integer, Integer> invertedIndices = new HashMap<Integer, Integer>();
  128. for (int i = 0; i < size; i++)
  129. {
  130. if (passFilter(i, filter))
  131. {
  132. Integer delegatedIndice = Integer.valueOf(i);
  133. indices.add(delegatedIndice);
  134. invertedIndices.put(delegatedIndice, indices.size() - 1);
  135. }
  136. }
  137. this.invertedIndices = invertedIndices;
  138. filteredIndices = indices;
  139. }
  140. else
  141. resetFilter();
  142. fireTableDataChanged();
  143. restoreSelection(selectedIndices);
  144. } //}}}
  145. //{{{ prepareFilter() method
  146. public String prepareFilter(String filter)
  147. {
  148. return filter;
  149. } //}}}
  150. //{{{ passFilter() method
  151. /**
  152. * This callback indicates if a row passes the filter.
  153. *
  154. * @param row the row number the delegate row count
  155. * @param filter the filter string
  156. * @return true if the row must be visible
  157. */
  158. public abstract boolean passFilter(int row, String filter);
  159. //}}}
  160. //{{{ saveSelection()
  161. private Set<Integer> saveSelection()
  162. {
  163. if (table == null)
  164. return null;
  165. int[] rows = table.getSelectedRows();
  166. if (rows.length == 0)
  167. return null;
  168. Set<Integer> selectedRows = new HashSet<Integer>(rows.length);
  169. for (int row : rows)
  170. {
  171. selectedRows.add(getTrueRow(row));
  172. }
  173. return selectedRows;
  174. } //}}}
  175. //{{{ restoreSelection() method
  176. private void restoreSelection(Set<Integer> selectedIndices)
  177. {
  178. if (selectedIndices == null || getRowCount() == 0)
  179. return;
  180. for (Integer selectedIndex : selectedIndices)
  181. {
  182. int i = getInternal2ExternalRow(selectedIndex.intValue());
  183. if (i != -1)
  184. table.getSelectionModel().setSelectionInterval(i, i);
  185. }
  186. } //}}}
  187. //{{{ getRowCount() method
  188. public int getRowCount()
  189. {
  190. if (filteredIndices == null)
  191. return delegated.getRowCount();
  192. return filteredIndices.size();
  193. } //}}}
  194. //{{{ getColumnCount() method
  195. public int getColumnCount()
  196. {
  197. return delegated.getColumnCount();
  198. } //}}}
  199. //{{{ getColumnName() method
  200. public String getColumnName(int columnIndex)
  201. {
  202. return delegated.getColumnName(columnIndex);
  203. } //}}}
  204. //{{{ getColumnClass() method
  205. public Class<?> getColumnClass(int columnIndex)
  206. {
  207. return delegated.getColumnClass(columnIndex);
  208. } //}}}
  209. //{{{ isCellEditable() method
  210. public boolean isCellEditable(int rowIndex, int columnIndex)
  211. {
  212. int trueRowIndex = getTrueRow(rowIndex);
  213. return delegated.isCellEditable(trueRowIndex, columnIndex);
  214. } //}}}
  215. //{{{ getValueAt() method
  216. public Object getValueAt(int rowIndex, int columnIndex)
  217. {
  218. int trueRowIndex = getTrueRow(rowIndex);
  219. return delegated.getValueAt(trueRowIndex, columnIndex);
  220. } //}}}
  221. //{{{ setValueAt() method
  222. public void setValueAt(Object aValue, int rowIndex, int columnIndex)
  223. {
  224. int trueRowIndex = getTrueRow(rowIndex);
  225. delegated.setValueAt(aValue, trueRowIndex, columnIndex);
  226. } //}}}
  227. //{{{ getTrueRow() method
  228. /**
  229. * Converts a row index from the JTable to an internal row index from the delegated model.
  230. *
  231. * @param rowIndex the row index
  232. * @return the row index in the delegated model
  233. */
  234. public int getTrueRow(int rowIndex)
  235. {
  236. if (filteredIndices == null)
  237. return rowIndex;
  238. return filteredIndices.get(rowIndex).intValue();
  239. } //}}}
  240. //{{{ getInternal2ExternalRow() method
  241. /**
  242. * Converts a row index from the delegated table model into a row index of the JTable.
  243. *
  244. * @param internalRowIndex the internal row index
  245. * @return the table row index or -1 if this row is not visible
  246. */
  247. public int getInternal2ExternalRow(int internalRowIndex)
  248. {
  249. if (invertedIndices == null)
  250. return internalRowIndex;
  251. Integer externalRowIndex = invertedIndices.get(internalRowIndex);
  252. if (externalRowIndex == null)
  253. return -1;
  254. return externalRowIndex.intValue();
  255. } //}}}
  256. /**
  257. * This fine grain notification tells listeners the exact range
  258. * of cells, rows, or columns that changed.
  259. */
  260. public void tableChanged(TableModelEvent e)
  261. {
  262. setFilter(filter);
  263. }
  264. }