diff --git a/maven/repository/org/oxbow/swing-bits/1.0.0-SNAPSHOT/maven-metadata-local.xml b/maven/repository/org/oxbow/swing-bits/1.0.0-SNAPSHOT/maven-metadata-local.xml index b80f55e..75531de 100644 --- a/maven/repository/org/oxbow/swing-bits/1.0.0-SNAPSHOT/maven-metadata-local.xml +++ b/maven/repository/org/oxbow/swing-bits/1.0.0-SNAPSHOT/maven-metadata-local.xml @@ -33,4 +33,4 @@ - + \ No newline at end of file diff --git a/maven/repository/org/oxbow/swing-bits/maven-metadata-local.xml b/maven/repository/org/oxbow/swing-bits/maven-metadata-local.xml index f8f84c7..ae27bc0 100644 --- a/maven/repository/org/oxbow/swing-bits/maven-metadata-local.xml +++ b/maven/repository/org/oxbow/swing-bits/maven-metadata-local.xml @@ -10,4 +10,4 @@ 20130102042349 - + \ No newline at end of file diff --git a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/AbstractTableFilter.java b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/AbstractTableFilter.java index 2793aa1..577d6f1 100644 --- a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/AbstractTableFilter.java +++ b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/AbstractTableFilter.java @@ -60,6 +60,7 @@ public abstract class AbstractTableFilter implements ITableFil private final T table; private final TableFilterState filterState = new TableFilterState(); private boolean autoclean; + private boolean sortable = true; public AbstractTableFilter( T table ) { this.table = table; @@ -188,4 +189,12 @@ public void clear() { fireFilterChange(); } + public boolean getSortable() { + return this.sortable; + } + + public void setSortable(boolean sortable) { + this.sortable = sortable; + } + } diff --git a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/ExcelFilterTableHeaderRenderer.java b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/ExcelFilterTableHeaderRenderer.java new file mode 100644 index 0000000..7dd49a5 --- /dev/null +++ b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/ExcelFilterTableHeaderRenderer.java @@ -0,0 +1,107 @@ +package org.oxbow.swingbits.table.filter; + +import javax.swing.*; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +public class ExcelFilterTableHeaderRenderer extends JPanel implements TableCellRenderer { + private static final long serialVersionUID = 1L; + + private final int filterIconPlacement; + private final ITableFilter tableFilter; + + private int column = -1; + private JTable table = null; + private JButton button; + private JLabel title; + private Icon filteringIcon;//icon which is displayed on column before any data filtered + private Icon filteredIcon;//icon which is displayed on column after any data filtered + + public ExcelFilterTableHeaderRenderer(ITableFilter tableFilter, + int filterIconPlacement, + String columnName, + Icon filteringIcon, + Icon filteredIcon) { + super(new BorderLayout()); + this.filterIconPlacement = filterIconPlacement; + if (this.filterIconPlacement != SwingConstants.LEADING && + this.filterIconPlacement != SwingConstants.TRAILING) { + throw new UnsupportedOperationException("The filter icon " + + "placement can only take the values of " + + "SwingConstants.LEADING or SwingConstants.TRAILING"); + } + + this.tableFilter = tableFilter; + this.filteringIcon = filteringIcon; + this.filteredIcon = filteredIcon; + this.tableFilter.addChangeListener(filter -> button.setIcon(filter.isFiltered(column)? filteredIcon : filteringIcon)); + + button = new JButton(this.filteringIcon); + button.setPreferredSize(new Dimension(25, 15)); + button.setBorder(BorderFactory.createBevelBorder(1,Color.GRAY, Color.GRAY)); + title = new JLabel(columnName); + + switch (this.filterIconPlacement) { + case SwingConstants.LEADING: + add(button, BorderLayout.WEST); + add(title, BorderLayout.CENTER); + break; + case SwingConstants.TRAILING: + add(title, BorderLayout.CENTER); + add(button, BorderLayout.EAST); + break; + } + title.setHorizontalAlignment(JLabel.CENTER); + title.setVerticalAlignment(JLabel.CENTER); + setBorder(UIManager.getBorder("TableHeader.cellBorder")); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, + boolean hasFocus, int row, int column) { + if (table != null && this.table != table) { + //commented to update the column with respective names + //title.setText(table.getColumnName(column)); + this.table = table; + final JTableHeader header = table.getTableHeader(); + if (header != null) { + header.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + int col = header.getTable().columnAtPoint(e.getPoint()); + if (col != column || col == -1) return; + + int index = header.getColumnModel().getColumnIndexAtX(e.getPoint().x); + if (index == -1) return; + + setBounds(header.getHeaderRect(index)); + header.add(ExcelFilterTableHeaderRenderer.this); + validate(); + + Rectangle buttonBounds = new Rectangle(button.getLocationOnScreen().x, button.getLocationOnScreen().y, + button.getBounds().width, button.getBounds().height); + if ((buttonBounds.contains(e.getLocationOnScreen()))) { + button.doClick(); + + for (MouseListener ml : header.getMouseListeners()) { + if (ml instanceof TableFilterColumnPopup) { + ((TableFilterColumnPopup) ml).showPopupWindow(e); + } + } + } + header.remove(ExcelFilterTableHeaderRenderer.this); + + header.repaint(); + } + }); + } + } + this.column = column; + return this; + } + +} diff --git a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/FilterTableHeaderRenderer.java b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/FilterTableHeaderRenderer.java index b4263de..19a78c1 100644 --- a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/FilterTableHeaderRenderer.java +++ b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/FilterTableHeaderRenderer.java @@ -31,22 +31,18 @@ package org.oxbow.swingbits.table.filter; -import java.awt.Component; -import java.awt.ComponentOrientation; -import java.awt.Image; - -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JTable; -import javax.swing.SwingConstants; - import org.oxbow.swingbits.table.TableHeaderRenderer; import org.oxbow.swingbits.util.swing.CompoundIcon; +import javax.swing.*; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.awt.event.*; + /** - * Table header renderer to show the column filter state - * + * Table header renderer to show the column filter state + * * Created on Feb 10, 2011 * @author Eugene Ryzhikov * @@ -59,9 +55,9 @@ class FilterTableHeaderRenderer extends TableHeaderRenderer { private final int filterIconPlacement; private final ITableFilter tableFilter; - + public FilterTableHeaderRenderer(ITableFilter tableFilter, - int filterIconPlacement) { + int filterIconPlacement) { this.tableFilter = tableFilter; this.filterIconPlacement = filterIconPlacement; @@ -72,7 +68,7 @@ public FilterTableHeaderRenderer(ITableFilter tableFilter, "SwingConstants.LEADING or SwingConstants.TRAILING"); } } - + private Icon getFilterIcon() { if (icon == null) { @@ -85,8 +81,8 @@ private Icon getFilterIcon() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, - boolean hasFocus, int row, int column) { - + boolean hasFocus, int row, int column) { + final JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); int modelColumn = table.convertColumnIndexToModel(column); @@ -94,7 +90,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole Icon oldIcon = label.getIcon(); Icon newIcon = null; if (oldIcon == null) { - newIcon = getFilterIcon(); + newIcon = getFilterIcon(); } else { ComponentOrientation orientation = label.getComponentOrientation(); @@ -114,7 +110,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole } label.setIcon(newIcon); } - + return label; } diff --git a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/ITableFilter.java b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/ITableFilter.java index 7ecee12..40d27f4 100644 --- a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/ITableFilter.java +++ b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/ITableFilter.java @@ -102,4 +102,12 @@ public interface Row { */ void modelChanged( TableModel model ); + /** + * Determines whether the table is sortable or not - Default true + * @return true by default + */ + + void setSortable(boolean sortable); + boolean getSortable(); + } diff --git a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/JTableFilter.java b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/JTableFilter.java index 1dbfec1..4cb8eab 100644 --- a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/JTableFilter.java +++ b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/JTableFilter.java @@ -112,8 +112,12 @@ public void modelChanged( TableModel model ) { TableRowSorter sorter = new TableRowSorter( model ); sorter.setSortsOnUpdates(true); getTable().setRowSorter( sorter ); + if (!this.getSortable()) { + for (int i = 0; i < this.getTable().getColumnCount(); i++) { + sorter.setSortable(i, false); + } + } } } - } diff --git a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/TableFilterColumnPopup.java b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/TableFilterColumnPopup.java index d49aa83..40fd079 100644 --- a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/TableFilterColumnPopup.java +++ b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/TableFilterColumnPopup.java @@ -39,25 +39,13 @@ import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.ResourceBundle; - -import javax.swing.BorderFactory; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JToolBar; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; +import java.util.*; + +import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.table.JTableHeader; +import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.oxbow.swingbits.list.ActionCheckListModel; @@ -92,6 +80,11 @@ static class ColumnAttrs { private boolean actionsVisible = true; private boolean useTableRenderers = false; ResourceBundle bundle = ResourceBundle.getBundle( "task-dialog" ); // NOI18N + private Set searchableColumns; + private boolean enableRightClick; + private Icon filteringIcon;//icon which is displayed on column before any data filtered + private Icon filteredIcon;//icon which is displayed on column after any data filtered + private boolean clearTableFilterIcon = false; public TableFilterColumnPopup( ITableFilter filter ) { @@ -170,14 +163,16 @@ protected JComponent buildContent() { JToolBar toolbar = new JToolBar(); toolbar.setFloatable(false); toolbar.setOpaque(false); - toolbar.add( new PopupWindow.CommandAction( - bundle.getString( "Clear_ALL_COLUMN_FILTERS" ), - new ImageIcon(getClass().getResource("funnel_delete.png"))) { - @Override - protected boolean perform() { - return clearAllFilters(); - } - }); + if (clearTableFilterIcon) { + + toolbar.add(new PopupWindow.CommandAction(bundle.getString("Clear_ALL_COLUMN_FILTERS"), + new ImageIcon(getClass().getResource("funnel_delete.png"))) { + @Override + protected boolean perform() { + return clearAllFilters(); + } + }); + } commands.add( toolbar ); commands.add(Box.createHorizontalGlue()); @@ -231,12 +226,12 @@ public void setEnabled(boolean enabled) { @Override public void mousePressed(MouseEvent e) { - if ( enabled && e.isPopupTrigger() ) showFilterPopup(e); + if ( enabled && e.isPopupTrigger() && enableRightClick ) showFilterPopup(e); } @Override public void mouseReleased(MouseEvent e) { - if ( enabled && e.isPopupTrigger() ) showFilterPopup(e); + if ( enabled && e.isPopupTrigger() && enableRightClick ) showFilterPopup(e); } private void showFilterPopup(MouseEvent e) { @@ -247,6 +242,9 @@ private void showFilterPopup(MouseEvent e) { int vColumnIndex = colModel.getColumnIndexAtX(e.getX()); if ( vColumnIndex < 0 ) return; + //if a set of columns provided for only those columns to be filtered, then ignore if the column name is not in the list. + TableColumn column = colModel.getColumn(vColumnIndex); + if (searchableColumns != null && !searchableColumns.isEmpty() && !searchableColumns.contains(column.getHeaderValue())) return; // Determine if mouse was clicked between column heads Rectangle headerRect = filter.getTable().getTableHeader().getHeaderRect(vColumnIndex); @@ -320,6 +318,28 @@ public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} + public void setSearchableColumns(Set searchableColumns) { + this.searchableColumns = searchableColumns; + } + + public void setEnableRightClick(boolean enableRightClick) { + this.enableRightClick = enableRightClick; + } + protected void showPopupWindow(MouseEvent e) { + showFilterPopup(e); + } + + public void setFilteringIcon(Icon filteringIcon) { + this.filteringIcon = filteringIcon; + } + + public void setFilteredIcon(Icon filteredIcon) { + this.filteredIcon = filteredIcon; + } + + public void setClearFilterIcon(boolean clearFilter) { + this.clearTableFilterIcon = clearFilter; + } } diff --git a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/TableRowFilterSupport.java b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/TableRowFilterSupport.java index 154e9e2..01d5023 100644 --- a/swingbits/src/main/java/org/oxbow/swingbits/table/filter/TableRowFilterSupport.java +++ b/swingbits/src/main/java/org/oxbow/swingbits/table/filter/TableRowFilterSupport.java @@ -34,12 +34,11 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.Collections; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.stream.Collectors; -import javax.swing.JTable; -import javax.swing.SwingConstants; +import javax.swing.*; +import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableModel; @@ -58,6 +57,18 @@ public final class TableRowFilterSupport { private boolean useTableRenderers = false; private boolean autoclean = false; + public enum FilterType { + DEFAULT, + EXCEL + } + private Set searchableColumns; + private boolean enableRightClick; + + private Icon filteringIcon;//icon which is displayed on column before any data filtered + private Icon filteredIcon;//icon which is displayed on column after any data filtered + private FilterType filterType = FilterType.DEFAULT; + private boolean clearTableFilter = false; + private TableRowFilterSupport( ITableFilter filter ) { if ( filter == null ) throw new NullPointerException(); //this.table = table; @@ -150,6 +161,11 @@ public JTable apply() { filterPopup.setSearchFilter(searchFilter); filterPopup.setSearchTranslator(translator); filterPopup.setUseTableRenderers( useTableRenderers ); + filterPopup.setSearchableColumns(searchableColumns); + filterPopup.setEnableRightClick(enableRightClick); + filterPopup.setFilteringIcon(filteringIcon); + filterPopup.setFilteredIcon(filteredIcon); + filterPopup.setClearFilterIcon(clearTableFilter); setupTableHeader(); @@ -190,14 +206,40 @@ private void setupHeaderRenderers( TableModel newModel, boolean fullSetup ) { JTable table = filter.getTable(); - FilterTableHeaderRenderer headerRenderer = - new FilterTableHeaderRenderer(filter, filterIconPlacement); filter.modelChanged( newModel ); - for( TableColumn c: Collections.list( table.getColumnModel().getColumns()) ) { - c.setHeaderRenderer( headerRenderer ); + //default icons + if (filteringIcon == null) { + filteringIcon = new ImageIcon(getClass().getResource("filtering.png")); + } + if (filteredIcon == null) { + filteredIcon = new ImageIcon(getClass().getResource("filtered.png")); } + TableCellRenderer headerRenderer = null; + switch (filterType) { + case DEFAULT: + for( TableColumn c: Collections.list( table.getColumnModel().getColumns()) ) { + if (searchable && ((searchableColumns == null || searchableColumns.isEmpty()) + || searchableColumns.contains(c.getHeaderValue()))) { + headerRenderer = new FilterTableHeaderRenderer(filter, filterIconPlacement); + c.setHeaderRenderer( headerRenderer ); + } + } + break; + case EXCEL: + for( TableColumn c: Collections.list( table.getColumnModel().getColumns()) ) { + if (searchable && ((searchableColumns == null || searchableColumns.isEmpty()) + || searchableColumns.contains(c.getHeaderValue()))) { + headerRenderer = + new ExcelFilterTableHeaderRenderer(filter, filterIconPlacement, (String) c.getHeaderValue(), filteringIcon, filteredIcon); + c.setHeaderRenderer( headerRenderer ); + } + } + break; + } + + if ( !fullSetup ) return; table.addPropertyChangeListener("model", new PropertyChangeListener() { @@ -210,5 +252,68 @@ public void propertyChange(PropertyChangeEvent e) { } + /** + * Column filter list is searchable & supported columns for searching + * Any column is not listed in the table model will be ignored + * + * @param searchableColumns + * @return + */ + public TableRowFilterSupport searchableColumns(Object... searchableColumns) { + return this.searchableColumns(Arrays.stream(searchableColumns).collect(Collectors.toSet())); + } + + /** + * Column filter list is searchable & supported columns for searching + * Any column is not listed in the table model will be ignored + * + * @param searchableColumns + * @return + */ + public TableRowFilterSupport searchableColumns(Set searchableColumns) { + this.searchable = true; + this.searchableColumns = searchableColumns; + return this; + } + /** + * Column filter list is searchable & supported columns for searching + * Any column is not listed in the table model will be ignored + * + * @param searchableColumns + * @return + */ + public TableRowFilterSupport searchableColumns(List searchableColumns) { + return this.searchableColumns(new HashSet<>(searchableColumns)); + } + + public TableRowFilterSupport sortable(boolean sortable) { + this.filter.setSortable(sortable); + return this; + } + + public TableRowFilterSupport enableRightClick(boolean enableRightClick) { + this.enableRightClick = enableRightClick; + return this; + } + + public TableRowFilterSupport filteringIcon(Icon filteringIcon) { + this.filteringIcon = filteringIcon; + return this; + } + + public TableRowFilterSupport filteredIcon(Icon filteredIcon) { + this.filteredIcon = filteredIcon; + return this; + } + + public TableRowFilterSupport filterType(FilterType filterType) { + this.filterType = filterType; + return this; + } + + public TableRowFilterSupport enableClearTableFilter(boolean clearTableFilter) { + this.clearTableFilter = clearTableFilter; + return this; + } } diff --git a/swingbits/src/main/resources/org/oxbow/swingbits/table/filter/filtered.png b/swingbits/src/main/resources/org/oxbow/swingbits/table/filter/filtered.png new file mode 100644 index 0000000..4f204eb Binary files /dev/null and b/swingbits/src/main/resources/org/oxbow/swingbits/table/filter/filtered.png differ diff --git a/swingbits/src/main/resources/org/oxbow/swingbits/table/filter/filtering.png b/swingbits/src/main/resources/org/oxbow/swingbits/table/filter/filtering.png new file mode 100644 index 0000000..08d4dbd Binary files /dev/null and b/swingbits/src/main/resources/org/oxbow/swingbits/table/filter/filtering.png differ diff --git a/swingbits/src/test/java/org/oxbow/swingbits/table/filter/TableFilterTest.java b/swingbits/src/test/java/org/oxbow/swingbits/table/filter/TableFilterTest.java index f5347d0..3a1b11e 100644 --- a/swingbits/src/test/java/org/oxbow/swingbits/table/filter/TableFilterTest.java +++ b/swingbits/src/test/java/org/oxbow/swingbits/table/filter/TableFilterTest.java @@ -114,9 +114,15 @@ public void filterChanged(ITableFilter filter) { System.out.println("Filter Changed"); } }) + .filterIconPlacement(SwingConstants.TRAILING) + .filterType(TableRowFilterSupport.FilterType.EXCEL) .actions(true) + .sortable(false) + .enableRightClick(false) .searchable(true) + .searchableColumns("A123","B123") .useTableRenderers(true) + .enableClearTableFilter(true) .autoclean(true); JTable table = filter.apply();