This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository jaxx. See http://git.nuiton.org/jaxx.git commit e8990c6406975264c112c84222e2b09b2474e182 Author: Kevin Morin <morin@codelutin.com> Date: Thu Sep 18 11:53:42 2014 +0200 fixes #3515 Create a table filter --- .../main/java/jaxx/runtime/swing/CompoundIcon.java | 301 +++++++++++ .../java/jaxx/runtime/swing/JSearchTextField.java | 75 +++ .../runtime/swing/list/ActionCheckListModel.java | 193 +++++++ .../java/jaxx/runtime/swing/list/CheckList.java | 144 ++++++ .../jaxx/runtime/swing/list/CheckListAction.java | 33 ++ .../jaxx/runtime/swing/list/CheckListEditor.java | 84 +++ .../jaxx/runtime/swing/list/CheckListModel.java | 74 +++ .../jaxx/runtime/swing/list/CheckListRenderer.java | 228 +++++++++ .../runtime/swing/list/DefaultCheckListModel.java | 143 ++++++ .../swing/list/filter/CheckListFilterType.java | 31 ++ .../filter/DefaultFilterableCheckListModel.java | 62 +++ .../filter/FilterableActionCheckListModel.java | 48 ++ .../swing/list/filter/FilterableCheckList.java | 33 ++ .../list/filter/FilterableCheckListModel.java | 18 + .../swing/table/filter/AbstractTableFilter.java | 171 +++++++ .../swing/table/filter/FilterChangeListener.java | 37 ++ .../table/filter/FilterTableHeaderRenderer.java | 116 +++++ .../runtime/swing/table/filter/JTableFilter.java | 133 +++++ .../table/filter/TableAwareCheckListRenderer.java | 66 +++ .../runtime/swing/table/filter/TableFilter.java | 104 ++++ .../swing/table/filter/TableFilterColumnPopup.java | 566 +++++++++++++++++++++ .../swing/table/filter/TableFilterState.java | 129 +++++ .../swing/table/filter/TableRowFilterSupport.java | 161 ++++++ .../resources/i18n/jaxx-widgets_en_GB.properties | 3 + .../resources/i18n/jaxx-widgets_es_ES.properties | 3 + .../resources/i18n/jaxx-widgets_fr_FR.properties | 3 + jaxx-widgets/src/main/resources/icons/funnel.png | Bin 0 -> 797 bytes .../src/main/resources/icons/funnel_delete.png | Bin 0 -> 906 bytes jaxx-widgets/src/main/resources/icons/search.png | Bin 0 -> 851 bytes 29 files changed, 2959 insertions(+) diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/CompoundIcon.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/CompoundIcon.java new file mode 100644 index 0000000..5ad1e6f --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/CompoundIcon.java @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +package jaxx.runtime.swing; + +import java.awt.*; +import javax.swing.*; + +/** + * The CompoundIcon will paint two, or more, Icons as a single Icon. The Icons + * are painted in the order in which they are added. + * + * The Icons can be laid out + * <ul> + * <li>Horizontally + * <li>Vertically + * <li>Stacked + * </ul> + * + */ +public class CompoundIcon implements Icon { + public enum Layout { + HORIZONTAL, VERTICAL, STACKED; + } + + public final static float TOP = 0.0f; + public final static float LEFT = 0.0f; + public final static float CENTER = 0.5f; + public final static float BOTTOM = 1.0f; + public final static float RIGHT = 1.0f; + + private Icon[] icons; + + private Layout layout; + + private int gap; + + private float alignmentX = CENTER; + private float alignmentY = CENTER; + + /** + * Convenience constructor for creating a CompoundIcon where the icons are + * laid out horizontally, the gap is 0 and the X/Y alignments will default + * to CENTER. + * + * @param icons + * the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Icon... icons) { + this(Layout.HORIZONTAL, icons); + } + + /** + * Convenience constructor for creating a CompoundIcon where the gap is 0 + * and the X/Y alignments will default to CENTER. + * + * @param layout + * the layout used to lay out the icons for painting. + * @param icons + * the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Layout layout, Icon... icons) { + this(layout, 0, icons); + } + + /** + * Convenience constructor for creating a CompoundIcon where the X/Y + * alignments will default to CENTER. + * + * @param layout + * the layout used to lay out the icons for painting + * @param gap + * the gap between the icons + * @param icons + * the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Layout layout, int gap, Icon... icons) { + this(layout, gap, CENTER, CENTER, icons); + } + + /** + * Create a CompoundIcon specifying all the properties. + * + * @param layout + * the layout used to lay out the icons for painting + * @param gap + * the gap between the icons + * @param alignmentX + * the X alignment of the icons. Common values are LEFT, CENTER, + * RIGHT. Can be any value between 0.0 and 1.0 + * @param alignmentY + * the Y alignment of the icons. Common values are TOP, CENTER, + * BOTTOM. Can be any value between 0.0 and 1.0 + * @param icons + * the Icons to be painted as part of the CompoundIcon + */ + public CompoundIcon(Layout layout, int gap, float alignmentX, + float alignmentY, Icon... icons) { + this.layout = layout; + this.gap = gap; + this.alignmentX = alignmentX > 1.0f ? 1.0f : alignmentX < 0.0f ? 0.0f + : alignmentX; + this.alignmentY = alignmentY > 1.0f ? 1.0f : alignmentY < 0.0f ? 0.0f + : alignmentY; + + for (int i = 0; i < icons.length; i++) { + if (icons[i] == null) { + throw new IllegalArgumentException("Icon (" + i + + ") cannot be null"); + } + } + + this.icons = icons; + } + + /** + * Get the layout along which each icon is painted. + * + * @return the layout + */ + public Layout getLayout() { + return layout; + } + + /** + * Get the gap between each icon + * + * @return the gap in pixels + */ + public int getGap() { + return gap; + } + + /** + * Get the alignment of the icon on the x-layout + * + * @return the alignment + */ + public float getAlignmentX() { + return alignmentX; + } + + /** + * Get the alignment of the icon on the y-layout + * + * @return the alignment + */ + public float getAlignmentY() { + return alignmentY; + } + + /** + * Get the number of Icons contained in this CompoundIcon. + * + * @return the total number of Icons + */ + public int getIconCount() { + return icons.length; + } + + /** + * Get the Icon at the specified index. + * + * @param index + * the index of the Icon to be returned + * @return the Icon at the specified index + * @exception IndexOutOfBoundsException + * if the index is out of range + */ + public Icon getIcon(int index) { + return icons[index]; + } + + // ///// Icon Interface ///////////////////////////// + + /** + * Gets the width of this icon. + * + * @return the width of the icon in pixels. + */ + @Override + public int getIconWidth() { + int width = 0; + + // Add the width of all Icons while also including the gap + if (layout == Layout.HORIZONTAL) { + width += (icons.length - 1) * gap; + for (Icon icon : icons) + width += icon.getIconWidth(); + } else { // Just find the maximum width + for (Icon icon : icons) + width = Math.max(width, icon.getIconWidth()); + } + + return width; + } + + /** + * Gets the height of this icon. + * + * @return the height of the icon in pixels. + */ + @Override + public int getIconHeight() { + int height = 0; + + // Add the height of all Icons while also including the gap + if (layout == Layout.VERTICAL) { + height += (icons.length - 1) * gap; + + for (Icon icon : icons) + height += icon.getIconHeight(); + } else // Just find the maximum height + { + for (Icon icon : icons) + height = Math.max(height, icon.getIconHeight()); + } + + return height; + } + + /** + * Paint the icons of this compound icon at the specified location + * + * @param c The component on which the icon is painted + * + * @param g the graphics context + * + * @param x the X coordinate of the icon's top-left corner + * + * @param y the Y coordinate of the icon's top-left corner + * + */ + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + if (layout == Layout.HORIZONTAL) { + int height = getIconHeight(); + + for (Icon icon : icons) { + int iconY = getOffset(height, icon.getIconHeight(), alignmentY); + icon.paintIcon(c, g, x, y + iconY); + x += icon.getIconWidth() + gap; + } + } else if (layout == Layout.VERTICAL) { + int width = getIconWidth(); + + for (Icon icon : icons) { + int iconX = getOffset(width, icon.getIconWidth(), alignmentX); + icon.paintIcon(c, g, x + iconX, y); + y += icon.getIconHeight() + gap; + } + } else {// must be Z_layout + + int width = getIconWidth(); + int height = getIconHeight(); + + for (Icon icon : icons) { + int iconX = getOffset(width, icon.getIconWidth(), alignmentX); + int iconY = getOffset(height, icon.getIconHeight(), alignmentY); + icon.paintIcon(c, g, x + iconX, y + iconY); + } + } + } + + /* + * When the icon value is smaller than the maximum value of all icons the + * icon needs to be aligned appropriately. Calculate the offset to be used + * when painting the icon to achieve the proper alignment. + */ + private int getOffset(int maxValue, int iconValue, float alignment) { + float offset = (maxValue - iconValue) * alignment; + return Math.round(offset); + } +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/JSearchTextField.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/JSearchTextField.java new file mode 100644 index 0000000..2c291d1 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/JSearchTextField.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +package jaxx.runtime.swing; + +import jaxx.runtime.swing.JAXXWidgetUtil; + +import java.awt.Graphics; +import java.awt.Image; + +import javax.swing.ImageIcon; +import javax.swing.JTextField; + +/** + * A text field with search symbol painted to indicate + * that it is used as search field + * + * @author Eugene Ryzhikov + * @since 2.13 + * + */ +public class JSearchTextField extends JTextField { + + private static final String ICON_NAME = "search.png"; + private static final long serialVersionUID = 1L; + + private static ImageIcon icon; + + private static Image getScaledImage( int size ) { + + if (icon == null) { + icon = JAXXWidgetUtil.createImageIcon(ICON_NAME); + } + return new ImageIcon(icon.getImage().getScaledInstance( size, size, Image.SCALE_SMOOTH )).getImage(); + } + + private static int PAD = 4; + private static int PAD2 = PAD*2; + + @Override + public void paint(Graphics g) { + super.paint(g); + int size = getHeight()-PAD2; + g.drawImage( getScaledImage(size), getWidth()-size-PAD, PAD, null); + } + +} + diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/ActionCheckListModel.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/ActionCheckListModel.java new file mode 100644 index 0000000..6001189 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/ActionCheckListModel.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.list; + +import org.nuiton.decorator.Decorator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; + +public class ActionCheckListModel<T> implements CheckListModel<T> { + + protected final List<ListDataListener> listeners = Collections.synchronizedList( new ArrayList<ListDataListener>()); + protected final CheckListModel<T> originalModel; + + protected final CheckListAction<T> actionCheckAll = new CheckListAction.CheckAll<T>(); + + @SuppressWarnings("unchecked") + protected final List<CheckListAction<T>> actionItems = Arrays.asList( actionCheckAll ); + protected final Set<CheckListAction<T>> checks = new HashSet<CheckListAction<T>>(); + + public ActionCheckListModel( final CheckListModel<T> originalModel ) { + + if ( originalModel == null ) throw new NullPointerException(); + this.originalModel = originalModel; + + //react on original model changes + this.originalModel.addListDataListener( new ListDataListener () { + + @Override + public void intervalAdded(ListDataEvent e) { + ListDataEvent event = toDecoratedEvent(e); + for( ListDataListener l: listeners ) { + l.intervalAdded(event); + } + } + + @Override + public void intervalRemoved(ListDataEvent e) { + ListDataEvent event = toDecoratedEvent(e); + for( ListDataListener l: listeners ) { + l.intervalRemoved(event); + } + } + + @Override + public void contentsChanged(ListDataEvent e) { + ListDataEvent event = toDecoratedEvent(e); + for( ListDataListener l: listeners ) { + l.contentsChanged(event); + } + if ( originalModel.getCheckedItems().size() < originalModel.getOriginalSize() ) { + checks.remove(actionCheckAll); + } else { + checks.add(actionCheckAll); + } + fireListDataChanged(); + } + }); + } + + @Override + public int getSize() { + return originalModel.getSize() + actionItems.size(); + } + + @Override + public int getOriginalSize() { + return originalModel.getOriginalSize() + actionItems.size(); + } + + @Override + public Object getElementAt(int index) { + if ( isDecoratedIndex(index)) { + return actionItems.get(index); + } else { + return originalModel.getElementAt( toOriginalIndex(index)); + } + } + + private int toOriginalIndex( int index ) { + return index - actionItems.size(); + } + + private int toDecoratedIndex( int index ) { + return index + actionItems.size(); + } + + private boolean isDecoratedIndex( int index ) { + int size = actionItems.size(); + return size > 0 && index >= 0 && index < size; + } + + + @Override + public void addListDataListener(ListDataListener l) { + listeners.add(l); + } + + @Override + public void removeListDataListener(ListDataListener l) { + listeners.remove(l); + } + + private void fireListDataChanged() { + ListDataEvent e = new ListDataEvent( this, 0, 0, getSize() ); + for( ListDataListener l: listeners ) { + l.contentsChanged(e); + } + } + + private ListDataEvent toDecoratedEvent( ListDataEvent e ) { + return new ListDataEvent( + e.getSource(), + e.getType(), + toDecoratedIndex(e.getIndex0()), + toDecoratedIndex(e.getIndex1())); + } + + @Override + public boolean isCheckedIndex(int index) { + if ( isDecoratedIndex(index)) { + return checks.contains(actionItems.get(index)); + } else { + return originalModel.isCheckedIndex( toOriginalIndex(index)); + } + + } + + @Override + public void setCheckedIndex(int index, boolean value) { + if ( isDecoratedIndex(index)) { + CheckListAction<T> item = actionItems.get(index); + item.check(originalModel, value); + if ( value ) checks.add(item); else checks.remove(item); + fireListDataChanged(); + } else { + originalModel.setCheckedIndex( toOriginalIndex(index), value); + } + } + + @Override + public Collection<T> getCheckedItems() { + return originalModel.getCheckedItems(); + } + + @Override + public void setCheckedItems(Collection<T> items) { + originalModel.setCheckedItems(items); + } + +// @Override +// public void filter(String filter, Decorator<Object> decorator, CheckListFilterType filterType) { +// originalModel.filter(filter, decorator, filterType); +// } + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckList.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckList.java new file mode 100644 index 0000000..9907997 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckList.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.list; + +import jaxx.runtime.swing.list.filter.CheckListFilterType; +import org.nuiton.decorator.Decorator; + +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseListener; +import java.util.Collection; + +import javax.swing.AbstractAction; +import javax.swing.JList; +import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; + +/** + * The decorator for JList which makes it work like check list + * UI can be designed using JList and which can be later decorated to become a check list + * @author Eugene Ryzhikov + * + * @param <T> list item type + */ +public class CheckList<T> { + + private final JList list; + private static final MouseAdapter checkBoxEditor = new CheckListEditor(); + + /** + * Wraps the standard JList and makes it work like check list + */ + public CheckList() { + + this.list = new JList(); + this.list.getSelectionModel().setSelectionMode( ListSelectionModel.SINGLE_SELECTION); + + if ( !isEditorAttached() ) list.addMouseListener(checkBoxEditor); + this.list.setCellRenderer(new CheckListRenderer()); + + setupKeyboardActions(list); + + } + + @SuppressWarnings("serial") + private void setupKeyboardActions(final JList list) { + String actionKey = "toggle-check"; + list.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0), actionKey); + list.getActionMap().put(actionKey, new AbstractAction(){ + + @Override + public void actionPerformed(ActionEvent e) { + toggleIndex(list.getSelectedIndex()); + }}); + } + + private boolean isEditorAttached() { + + for( MouseListener ml: list.getMouseListeners() ) { + if ( ml instanceof CheckListEditor ) return true; + } + return false; + + } + + public JList getList() { + return list; + } + + /** + * Sets data to a check list. Simplification for setting new the model + * @param data + */ + public void setData( Collection<T> data ) { + setModel( new DefaultCheckListModel<T>(data)); + } + + /** + * Sets the model for check list. + * @param model + */ + public void setModel( CheckListModel<T> model ) { + list.setModel(model); + } + + @SuppressWarnings("unchecked") + public CheckListModel<T> getModel() { + return (CheckListModel<T>) list.getModel(); + } + + /** + * Returns a collection of checked items. + * @return collection of checked items. Empty collection if nothing is selected + */ + public Collection<T> getCheckedItems() { + return getModel().getCheckedItems(); + } + + /** + * Resets checked elements + * @param elements + */ + public void setCheckedItems( Collection<T> elements ) { + getModel().setCheckedItems(elements); + } + + public void toggleIndex( int index ) { + if ( index >= 0 && index < list.getModel().getSize()) { + CheckListModel<T> model = getModel(); + model.setCheckedIndex(index, !model.isCheckedIndex(index)); + } + } + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListAction.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListAction.java new file mode 100644 index 0000000..efd4402 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListAction.java @@ -0,0 +1,33 @@ +package jaxx.runtime.swing.list; + +import java.util.ArrayList; +import java.util.Collection; + + +public interface CheckListAction<T> { + + void check( CheckListModel<T> model, boolean value ); + + public static class CheckAll<T> implements CheckListAction<T> { + + @Override + public String toString() { + return "(All)"; + } + + @SuppressWarnings("unchecked") + @Override + public void check(CheckListModel<T> model, boolean value) { + Collection<T> items = new ArrayList<T>(); + if (value) { + for( int i=0, s=model.getSize(); i<s; i++ ) { + items.add((T) model.getElementAt(i)); + } + } + model.setCheckedItems( items ); + + } + + } + +} \ No newline at end of file diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListEditor.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListEditor.java new file mode 100644 index 0000000..7d25152 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListEditor.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.list; + +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Arrays; + +import javax.swing.JList; +import javax.swing.SwingUtilities; + + +/** + * Determines mouse click and + * 1. Toggles the check on selected item if clicked once + * 2. Clears checks and checks selected item if clicked more then once + * + * Created on Feb 4, 2011 + * @author Eugene Ryzhikov + * + */ +final class CheckListEditor extends MouseAdapter { + @Override + public void mouseClicked(MouseEvent e) { + + if (!SwingUtilities.isLeftMouseButton(e)) return; + + JList list = (JList) e.getSource(); + if ( !list.isEnabled() || (!(list.getModel() instanceof CheckListModel<?>))) return; + + int index = list.locationToIndex(e.getPoint()); + if (index < 0) return; + + Rectangle bounds = list.getCellBounds(index, index); + + if ( bounds.contains(e.getPoint()) ) { + + @SuppressWarnings("unchecked") + CheckListModel<Object> model = (CheckListModel<Object>) list.getModel(); + + if ( e.getClickCount() > 1 ) { + // clear all and check selected for more then 1 clicks + model.setCheckedItems( Arrays.asList( model.getElementAt(index))); + } else { + // simple toggle for 1 click + model.setCheckedIndex(index, !model.isCheckedIndex(index)); + } + e.consume(); + } + + } + +} + diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListModel.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListModel.java new file mode 100644 index 0000000..8120d51 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListModel.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.list; + +import java.util.Collection; + +import javax.swing.ListModel; + +public interface CheckListModel<T> extends ListModel { + + /** + * Returns the check state of the element at specified position + * @param index element index + * @return true if element at specified position is checked + * @throws IndexOutOfBoundsException if index is out of range + */ + boolean isCheckedIndex(int index); + + /** + * Sets the check state of the element at specified position + * @param index element index + * @param value + * @throws IndexOutOfBoundsException if index is out of range + */ + void setCheckedIndex(int index, boolean value); + + /** + * Returns a collections of checked items + * @return + */ + Collection<T> getCheckedItems(); + + /** + * Sets checked items + * @param items + */ + void setCheckedItems(Collection<T> items); + + /** + * Returns the number of items before the filter was applied + * @return + */ + int getOriginalSize(); + +} \ No newline at end of file diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListRenderer.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListRenderer.java new file mode 100644 index 0000000..c04d2fe --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListRenderer.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.list; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Rectangle; +import java.io.Serializable; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.Icon; +import javax.swing.JCheckBox; +import javax.swing.JList; +import javax.swing.ListCellRenderer; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; + +public class CheckListRenderer extends JCheckBox implements ListCellRenderer, Serializable { + + private static final long serialVersionUID = 1L; + + private static final Border NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1); + private static final Border SAFE_NO_FOCUS_BORDER = NO_FOCUS_BORDER; // may change in the feature + + /** + * Constructs a default renderer object for an item in a list. + */ + public CheckListRenderer() { + super(); + setOpaque(true); + setBorder(getNoFocusBorder()); + } + + private static Border getNoFocusBorder() { + if (System.getSecurityManager() != null) { + return SAFE_NO_FOCUS_BORDER; + } else { + return NO_FOCUS_BORDER; + } + } + + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, + boolean cellHasFocus) { + + setComponentOrientation(list.getComponentOrientation()); + + Color bg = null; + Color fg = null; + + JList.DropLocation dropLocation = list.getDropLocation(); + if (dropLocation != null && !dropLocation.isInsert() && dropLocation.getIndex() == index) { + + bg = UIManager.getColor("List.dropCellBackground"); + fg = UIManager.getColor("List.dropCellForeground"); + + isSelected = true; + } + + if (isSelected) { + setBackground(bg == null ? list.getSelectionBackground() : bg); + setForeground(fg == null ? list.getSelectionForeground() : fg); + } else { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + + if (value instanceof Icon) { + setIcon((Icon) value); + setText(""); + } else { + setIcon(null); + setText(getObjectAsText(value)); + } + + setSelected( isChecked(list, index)); + + setEnabled(list.isEnabled()); + setFont(list.getFont()); + + Border border = null; + if (cellHasFocus) { + if (isSelected) { + border = UIManager.getBorder("List.focusSelectedCellHighlightBorder"); + } + if (border == null) { + border = UIManager.getBorder("List.focusCellHighlightBorder"); + } + } else { + border = getNoFocusBorder(); + } + setBorder(border); + + return this; + } + + protected String getObjectAsText(Object obj) { + return (obj == null) ? "" : obj.toString(); + } + + private boolean isChecked(JList list, int index) { + + if (list.getModel() instanceof CheckListModel<?>) { + return ((CheckListModel<?>) list.getModel()).isCheckedIndex(index); + } else { + return false; + } + + } + + /** + * @return true if the background is opaque and differs from the JList's background; false otherwise + */ + @Override + public boolean isOpaque() { + Color back = getBackground(); + Component p = getParent(); + if (p != null) { + p = p.getParent(); + } + // p should now be the JList. + boolean colorMatch = (back != null) && (p != null) && back.equals(p.getBackground()) && p.isOpaque(); + return !colorMatch && super.isOpaque(); + } + + @Override + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + + if ("text".equals(propertyName) || + (("font".equals(propertyName) || "foreground".equals(propertyName)) && + oldValue != newValue && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) { + + super.firePropertyChange(propertyName, oldValue, newValue); + } + } + + // Methods below are overridden for performance reasons. + + @Override + public void validate() { + } + + @Override + public void invalidate() { + } + + @Override + public void repaint() { + } + + @Override + public void revalidate() { + } + + @Override + public void repaint(long tm, int x, int y, int width, int height) { + } + + @Override + public void repaint(Rectangle r) { + } + + @Override + public void firePropertyChange(String propertyName, byte oldValue, byte newValue) { + } + + @Override + public void firePropertyChange(String propertyName, char oldValue, char newValue) { + } + + @Override + public void firePropertyChange(String propertyName, short oldValue, short newValue) { + } + + @Override + public void firePropertyChange(String propertyName, int oldValue, int newValue) { + } + + @Override + public void firePropertyChange(String propertyName, long oldValue, long newValue) { + } + + @Override + public void firePropertyChange(String propertyName, float oldValue, float newValue) { + } + + @Override + public void firePropertyChange(String propertyName, double oldValue, double newValue) { + } + + @Override + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { + } + + @SuppressWarnings("serial") + public static class UIResource extends DefaultListCellRenderer implements javax.swing.plaf.UIResource { + } + +} \ No newline at end of file diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/DefaultCheckListModel.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/DefaultCheckListModel.java new file mode 100644 index 0000000..fa384f0 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/DefaultCheckListModel.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.list; + +import jaxx.runtime.JAXXUtil; +import org.nuiton.decorator.Decorator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.AbstractListModel; + +/** + * Default model for check list. It is based on the list of items + * Implementation of checks is based on HashSet of checked items + * + * @author Eugene Ryzhikov + * + * @param <T> list element type + */ +public class DefaultCheckListModel<T> extends AbstractListModel implements CheckListModel<T> { + + private static final long serialVersionUID = 1L; + + protected final List<T> data = new ArrayList<T>(); + protected final Set<T> checks = new HashSet<T>(); + + public DefaultCheckListModel( Collection<? extends T> data ) { + + if ( data == null ) return; + for (T object : data) { + this.data.add( object ); + checks.clear(); + } + } + + public DefaultCheckListModel( T... data ) { + this( Arrays.asList( data )); + } + + /* (non-Javadoc) + * @see org.oxbow.swingbits.list.ICheckListModel#getSize() + */ + @Override + public int getSize() { + return data().size(); + } + + @Override + public int getOriginalSize() { + return data.size(); + } + + protected List<T> data() { + return data; + } + + + /* (non-Javadoc) + * @see org.oxbow.swingbits.list.ICheckListModel#getElementAt(int) + */ + @Override + public Object getElementAt(int index) { + return data().get(index); + } + + /* (non-Javadoc) + * @see org.oxbow.swingbits.list.ICheckListModel#isChecked(int) + */ + @Override + public boolean isCheckedIndex( int index ) { + return checks.contains( data().get(index)); + } + + /* (non-Javadoc) + * @see org.oxbow.swingbits.list.ICheckListModel#setChecked(int, boolean) + */ + @Override + public void setCheckedIndex( int index, boolean value ) { + T o = data().get(index); + if ( value ) checks.add(o); else checks.remove(o); + fireContentsChanged(this, index, index); + } + + /* (non-Javadoc) + * @see org.oxbow.swingbits.list.ICheckListModel#getChecked() + */ + @Override + public Collection<T> getCheckedItems() { + List<T> items = new ArrayList<T>(checks); + items.retainAll(data); + return Collections.unmodifiableList( items ); + } + + /* (non-Javadoc) + * @see org.oxbow.swingbits.list.ICheckListModel#setChecked(java.util.Collection) + */ + @Override + public void setCheckedItems( Collection<T> items ) { + + List<T> correctedItems = new ArrayList<T>(items); + correctedItems.retainAll(data); + + checks.clear(); + checks.addAll( correctedItems ); + fireContentsChanged(this, 0, checks.size()-1); + } + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/CheckListFilterType.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/CheckListFilterType.java new file mode 100644 index 0000000..6f61a9b --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/CheckListFilterType.java @@ -0,0 +1,31 @@ +package jaxx.runtime.swing.list.filter; + +public enum CheckListFilterType { + + STARTS_WITH { + + @Override + public boolean include( String element, String filter ) { + + if ( element == null || filter == null ) return false; + return element.startsWith(filter); + + } + + }, + + CONTAINS { + + @Override + public boolean include( String element, String filter ) { + + if ( element == null || filter == null ) return false; + return element.contains(filter); + + } + + }; + + public abstract boolean include( String element, String filter ); + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/DefaultFilterableCheckListModel.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/DefaultFilterableCheckListModel.java new file mode 100644 index 0000000..4aa39c0 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/DefaultFilterableCheckListModel.java @@ -0,0 +1,62 @@ +package jaxx.runtime.swing.list.filter; + +import jaxx.runtime.JAXXUtil; +import jaxx.runtime.swing.list.DefaultCheckListModel; +import org.nuiton.decorator.Decorator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * @author Kevin Morin (Code Lutin) + * @since 2.13 + */ +public class DefaultFilterableCheckListModel<T> extends DefaultCheckListModel<T> implements FilterableCheckListModel<T> { + + private List<T> filteredData = null; + + public DefaultFilterableCheckListModel(Collection<? extends T> data) { + super(data); + } + + public DefaultFilterableCheckListModel(T... data) { + super(Arrays.asList(data)); + } + + protected List<T> data() { + return filteredData == null ? data: filteredData; + } + + @Override + public void filter(String filter, Decorator<Object> decorator, CheckListFilterType filterType) { + + if ( filter == null || filter.trim().length() == 0 ) { + filteredData = null; + } else { + + CheckListFilterType ft = filterType == null? CheckListFilterType.CONTAINS: filterType; + + String f = filter.toLowerCase(); + + List<T> fData = new ArrayList<T>(); + + for( T o: data ) { + String decorated; + if (o != null && decorator != null) { + decorated = decorator.toString(o); + } else { + decorated = JAXXUtil.getStringValue(o); + } + if ( ft.include(decorated, f)) { + fData.add(o); + } + } + filteredData = fData; + } + + fireContentsChanged( this, 0, data.size()-1); + } + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableActionCheckListModel.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableActionCheckListModel.java new file mode 100644 index 0000000..c1d14c1 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableActionCheckListModel.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.list.filter; + +import jaxx.runtime.swing.list.ActionCheckListModel; +import org.nuiton.decorator.Decorator; + +public class FilterableActionCheckListModel<T> extends ActionCheckListModel<T> implements FilterableCheckListModel<T> { + + public FilterableActionCheckListModel(final DefaultFilterableCheckListModel<T> originalModel) { + super(originalModel); + } + + @Override + public void filter(String filter, Decorator<Object> decorator, CheckListFilterType filterType) { + ((DefaultFilterableCheckListModel) originalModel).filter(filter, decorator, filterType); + } + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableCheckList.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableCheckList.java new file mode 100644 index 0000000..f1f7956 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableCheckList.java @@ -0,0 +1,33 @@ +package jaxx.runtime.swing.list.filter; + +import jaxx.runtime.swing.list.CheckList; +import jaxx.runtime.swing.list.CheckListModel; +import org.nuiton.decorator.Decorator; + +/** + * @author Kevin Morin (Code Lutin) + * @since 2.13 + */ +public class FilterableCheckList<T> extends CheckList<T> { + + /** + * Sets the model for check list. + * @param model + */ + public void setModel( CheckListModel<T> model ) { + getList().setModel(model); + } + + @SuppressWarnings("unchecked") + public FilterableCheckListModel<T> getModel() { + return (FilterableCheckListModel<T>) getList().getModel(); + } + + /** + * Filters list view without losing actual data + * @param filter + */ + public void filter( String filter, Decorator<Object> decorator, CheckListFilterType filterType ) { + getModel().filter(filter, decorator, filterType); + } +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableCheckListModel.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableCheckListModel.java new file mode 100644 index 0000000..402475c --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableCheckListModel.java @@ -0,0 +1,18 @@ +package jaxx.runtime.swing.list.filter; + +import jaxx.runtime.swing.list.CheckListModel; +import org.nuiton.decorator.Decorator; + +/** + * @author Kevin Morin (Code Lutin) + * @since 2.13 + */ +public interface FilterableCheckListModel<T> extends CheckListModel<T> { + + /** + * Filters list view without losing actual data + * @param filter + */ + void filter( String filter, Decorator<Object> decorator, CheckListFilterType filterType ); + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/AbstractTableFilter.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/AbstractTableFilter.java new file mode 100644 index 0000000..b51ea9b --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/AbstractTableFilter.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.table.filter; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ComparatorUtils; +import org.apache.commons.collections4.Transformer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import javax.swing.JTable; + +/** + * Partial implementation of table filter + * + * Created on Feb 10, 2011 + * @author Eugene Ryzhikov + * + * @param <T> + */ +@SuppressWarnings("serial") +public abstract class AbstractTableFilter<T extends JTable> implements TableFilter<T> { + + private final Set<FilterChangeListener> listeners = Collections.synchronizedSet( new HashSet<FilterChangeListener>()); + + private final T table; + private final TableFilterState filterState = new TableFilterState(); + private Map<Integer, Integer> columnDistnctIntemNumbers = new HashMap<Integer, Integer>(); + + public AbstractTableFilter( T table ) { + this.table = table; + } + + @Override + public T getTable() { + return table; + } + + protected abstract boolean execute(int col, Collection<Object> items); + + @Override + public boolean apply(int col, Collection<Object> items) { + setFilterState(col, items); + boolean result; + if ( result = execute( col, items ) ) fireFilterChange(); + return result; + } + + @Override + public final void addChangeListener( FilterChangeListener listener ) { + if ( listener != null ) listeners.add(listener); + } + + @Override + public final void removeChangeListener( FilterChangeListener listener ) { + if ( listener != null ) listeners.remove(listener); + } + + public final void fireFilterChange() { + for( FilterChangeListener l: listeners ) { + l.filterChanged(AbstractTableFilter.this); + } + } + + @Override + public Collection<Object> getDistinctColumnItems( int column ) { + Collection<Object> result = collectDistinctColumnItems( column ); + columnDistnctIntemNumbers.put(column, result != null ? result.size() : 0); + return result; + + } + + private Collection<Object> collectDistinctColumnItems( final int column ) { +// Set<Object> set = new HashSet<Object>(); // to collect unique items +// int nullIndex = -1; +// for( int row=0; row<table.getModel().getRowCount(); row++) { +// Object value = table.getModel().getValueAt( row, column); +// // adding null to TreeSet will produce NPE, just remember we had it +// if ( value == null ) { +// nullIndex = row; +// } else { +// set.add( new DistinctColumnItem(value, row )); +// } +// } + Set<Object> set = distinctValuesForColumn(column); + List<Object> result = null; + if (set != null) { + result = new ArrayList<Object>(set); + // if ( nullIndex >= 0 ) result.add(0, null); // add null to resulting collection if we had it + + Collections.sort(result, new Comparator<Object>() { + @Override + public int compare(Object o1, Object o2) { + return ComparatorUtils.transformedComparator(ComparatorUtils.NATURAL_COMPARATOR, + new Transformer<Object, Comparable>() { + @Override + public Comparable transform(Object input) { + return AbstractTableFilter.this.toString(input); + } + }).compare(o1, o2); + } + }); + + } + return result; + } + + @Override + public Collection<Object> getFilterState( int column ) { + return filterState.getValues(column); + } + + @Override + public boolean isFiltered( int column ) { + Collection<Object> checks = getFilterState( column ); + return CollectionUtils.isNotEmpty(checks) && !Objects.equals(columnDistnctIntemNumbers.get(column), checks.size()); + } + + @Override + public boolean includeRow( TableFilter.Row row ) { + return filterState.include(row); + } + + public void setFilterState(int column, Collection<Object> values ) { + filterState.setValues(column, values); + } + + public void clear() { + filterState.clear(); + fireFilterChange(); + } + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/FilterChangeListener.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/FilterChangeListener.java new file mode 100644 index 0000000..b674c13 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/FilterChangeListener.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.table.filter; + +public interface FilterChangeListener { + + void filterChanged( TableFilter<?> filter ); +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/FilterTableHeaderRenderer.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/FilterTableHeaderRenderer.java new file mode 100644 index 0000000..45faba9 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/FilterTableHeaderRenderer.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.table.filter; + +import jaxx.runtime.swing.CompoundIcon; +import jaxx.runtime.swing.JAXXWidgetUtil; + +import java.awt.Component; +import java.awt.Image; + +import javax.swing.*; +import javax.swing.table.TableCellRenderer; + +/** + * Table header renderer to show the column filter state + * + * Created on Feb 10, 2011 + * @author Eugene Ryzhikov + * + */ +class FilterTableHeaderRenderer extends JComponent implements TableCellRenderer { + + private static final long serialVersionUID = 1L; + + private ImageIcon icon; + private final TableFilter<?> tableFilter; + private boolean rendererInit = true; + private int originalHorizontalTextPosition; + + public FilterTableHeaderRenderer( TableFilter<?> tableFilter ) { + this.tableFilter = tableFilter; + } + + private Icon getFilterIcon() { + + if (icon == null) { + icon = JAXXWidgetUtil.createImageIcon("funnel.png"); + icon = new ImageIcon( icon.getImage().getScaledInstance( 12, 12, Image.SCALE_SMOOTH )); + } + return icon; + + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, + boolean hasFocus, int row, int column) { + + final JLabel label = (JLabel) table.getTableHeader().getDefaultRenderer() + .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if ( rendererInit ) { + originalHorizontalTextPosition = label.getHorizontalTextPosition(); + rendererInit = false; + } + + int modelColumn = table.convertColumnIndexToModel(column); + if ( tableFilter.isFiltered(modelColumn) ) { + + Icon originalIcon = label.getIcon(); + if ( originalIcon == null ) { + label.setIcon( getFilterIcon() ); + } else { + label.setIcon( new CompoundIcon( getFilterIcon(), originalIcon ) ); + } + label.setHorizontalTextPosition( JLabel.TRAILING ); + + } else { + label.setHorizontalTextPosition( originalHorizontalTextPosition ); + } + + return label; + } + + // following methods are overriden for performance reasons + + @Override + public void validate() {} + + @Override + public void revalidate() {} + + @Override + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {} + + @Override + public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {} + +} \ No newline at end of file diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/JTableFilter.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/JTableFilter.java new file mode 100644 index 0000000..e802b6f --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/JTableFilter.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.table.filter; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +import javax.swing.DefaultRowSorter; +import javax.swing.JTable; +import javax.swing.RowFilter; +import javax.swing.RowSorter; +import javax.swing.table.TableModel; +import javax.swing.table.TableRowSorter; + +public class JTableFilter extends AbstractTableFilter<JTable> { + + private static final long serialVersionUID = 1L; + + private final TableRowFilter filter = new TableRowFilter(); + + public JTableFilter( JTable table) { + super(table); + } + + @Override + protected boolean execute(int col, Collection<Object> items) { + + RowSorter<?> rs = getTable().getRowSorter(); + + if (!( rs instanceof DefaultRowSorter )) return false; + + DefaultRowSorter<?, ?> drs = (DefaultRowSorter<?, ?>) rs; + + @SuppressWarnings("unchecked") + RowFilter<Object,Object> prevFilter = (RowFilter<Object, Object>) drs.getRowFilter(); + if ( !(prevFilter instanceof TableRowFilter)) { + filter.setParentFilter(prevFilter); + } + + drs.setRowFilter(filter); + return true; + + } + + class TableRowFilter extends RowFilter<Object,Object> implements Serializable { + + private static final long serialVersionUID = 1L; + + private RowFilter<Object, Object> parentFilter; + + public RowFilter<Object, Object> getParentFilter() { + return parentFilter; + } + + public void setParentFilter( RowFilter<Object, Object> parentFilter ) { + this.parentFilter = (parentFilter == null || parentFilter == this )? null: parentFilter; + } + + @Override + public boolean include( final RowFilter.Entry<? extends Object, ? extends Object> entry) { + + // use parent filter condition + if ( parentFilter != null && !parentFilter.include(entry)) return false; + + return includeRow( new TableFilter.Row() { + + @Override + public Object getValue(int column) { return entry.getValue(column); } + + @Override + public int getValueCount() { return entry.getValueCount(); } + + }); + + } + + } + + public void modelChanged( TableModel model ) { + getTable().setRowSorter( new TableRowSorter<TableModel>( model )); + } + + @Override + public Set<Object> distinctValuesForColumn(int i) { + //TODO + return null; + } + + @Override + public String toString(Object o) { + //TODO + return null; + } + + public void clear() { + super.clear(); + Collection<Object> items = Collections.emptyList(); + for( int column=0; column<getTable().getModel().getColumnCount(); column++) { + execute(column, items); + } + } +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableAwareCheckListRenderer.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableAwareCheckListRenderer.java new file mode 100644 index 0000000..83279e0 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableAwareCheckListRenderer.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +package jaxx.runtime.swing.table.filter; + +import jaxx.runtime.JAXXUtil; +import jaxx.runtime.swing.list.CheckListAction; +import jaxx.runtime.swing.list.CheckListRenderer; +import org.nuiton.decorator.Decorator; + +import java.awt.Component; + +import javax.swing.JList; + +@SuppressWarnings("serial") +public class TableAwareCheckListRenderer extends CheckListRenderer { + + private final Decorator<Object> decorator; + + public TableAwareCheckListRenderer(Decorator<Object> decorator ) { + this.decorator = decorator; + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + String text; + if (value != null && decorator != null) { + text = decorator.toString(value); + } else { + text = JAXXUtil.getStringValue(value); + } + setText(text); + return this; + + } + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilter.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilter.java new file mode 100644 index 0000000..7cb5c29 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilter.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + + +package jaxx.runtime.swing.table.filter; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Set; + +import javax.swing.JTable; +import javax.swing.table.TableModel; + +public interface TableFilter<T extends JTable> extends Serializable { + + /** + * The table under filter + * @return + */ + T getTable(); + + /** + * + * @param column model column index + * @return + */ + Collection<Object> getDistinctColumnItems( int column ); + + /** + * + * @param column model column index + * @return + */ + Collection<Object> getFilterState( int column ); + + /** + * Checks if column is filtered + * @param column model column index + * @return true if column is filtered + */ + boolean isFiltered( int column ); + + boolean includeRow( Row entry ); + + /** + * Apply filter for specified column based on collection of distinct items + * @param col + * @param items + * @return + */ + boolean apply( int col, Collection<Object> items ); + + public interface Row { + int getValueCount(); + Object getValue( int column ); + } + + void addChangeListener( FilterChangeListener listener ); + void removeChangeListener( FilterChangeListener listener ); + + /** + * Clear the filter + */ + void clear(); + + /** + * Describes what filter has to do when table model changes + * @param model + */ + void modelChanged( TableModel model ); + + Set<Object> distinctValuesForColumn(int i); + + String toString(Object o); + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilterColumnPopup.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilterColumnPopup.java new file mode 100644 index 0000000..2248646 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilterColumnPopup.java @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + + +package jaxx.runtime.swing.table.filter; + +import jaxx.runtime.swing.JAXXWidgetUtil; +import jaxx.runtime.swing.JSearchTextField; +import jaxx.runtime.swing.list.ActionCheckListModel; +import jaxx.runtime.swing.list.CheckList; +import jaxx.runtime.swing.list.filter.CheckListFilterType; +import jaxx.runtime.swing.list.DefaultCheckListModel; +import jaxx.runtime.swing.list.CheckListModel; +import jaxx.runtime.swing.list.filter.DefaultFilterableCheckListModel; +import jaxx.runtime.swing.list.filter.FilterableActionCheckListModel; +import jaxx.runtime.swing.list.filter.FilterableCheckList; +import jaxx.runtime.swing.list.filter.FilterableCheckListModel; +import org.apache.commons.collections4.CollectionUtils; +import org.nuiton.decorator.Decorator; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableColumnModel; + +import static org.nuiton.i18n.I18n.t; + +class TableFilterColumnPopup extends MouseAdapter { + + static class ColumnAttrs { + public Dimension preferredSize; + } + + private boolean enabled = false; + + private final FilterableCheckList<Object> filterList = new FilterableCheckList(); + private final JSearchTextField searchField = new JSearchTextField(); + + private final Map<Integer, ColumnAttrs> colAttrs = new HashMap<Integer, ColumnAttrs>(); + private int mColumnIndex = -1; + + private final TableFilter<?> filter; + private boolean searchable; + private Decorator<Object> decorator; + private boolean actionsVisible = true; + private boolean useTableRenderers = false; + + private final JPopupMenu menu; + + private Dimension defaultSize = new Dimension(100, 100); + + public TableFilterColumnPopup(TableFilter<?> filter) { + + menu = new ResizablePopupMenu() { + + private static final long serialVersionUID = 1L; + + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + if (menu.getComponentCount() == 0) { + JComponent content = buildContent(); + defaultSize = content.getPreferredSize(); + + menu.add(content); + + } + beforeShow(); + } + + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + beforeHide(); + } + + }; + + this.filter = filter; + filterList.getList().setVisibleRowCount(8); + + setupTableHeader(); + filter.getTable().addPropertyChangeListener("tableHeader", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + setupTableHeader(); + } + } + ); + filter.getTable().addPropertyChangeListener("model", new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + colAttrs.clear(); + } + } + ); + + searchField.getDocument().addDocumentListener(new DocumentListener() { + + @Override + public void removeUpdate(DocumentEvent e) { + perform(e); + } + + @Override + public void insertUpdate(DocumentEvent e) { + perform(e); + } + + @Override + public void changedUpdate(DocumentEvent e) { + perform(e); + } + + private void perform(DocumentEvent e) { + filterList.filter(searchField.getText(), decorator, CheckListFilterType.CONTAINS); + } + + }); + + } + + public final Dimension getDefaultSize() { + return defaultSize; + } + + public final Dimension getPreferredSize() { + return menu.getPreferredSize(); + } + + public final void setPreferredSize(Dimension preferredSize) { + menu.setPreferredSize(preferredSize); + } + + /** + * Shows Popup in predefined location + * + * @param invoker + * @param x + * @param y + */ + public void show(Component invoker, int x, int y) { + menu.show(invoker, x, y); + } + + /** + * Shows popup in predefined location + * + * @param invoker + * @param location + */ + public void show(Component invoker, Point location) { + show(invoker, location.x, location.y); + } + + /** + * Hides popup + */ + public final void hide() { + menu.setVisible(false); + } + + public void setSearchable(boolean searchable) { + this.searchable = searchable; + } + + public void searchDecorator(Decorator<Object> decorator) { + this.decorator = decorator; + } + + public void setActionsVisible(boolean actionsVisible) { + this.actionsVisible = actionsVisible; + } + + public void setUseTableRenderers(boolean reuseRenderers) { + this.useTableRenderers = reuseRenderers; + } + + private void setupTableHeader() { + JTableHeader header = filter.getTable().getTableHeader(); + if (header != null) header.addMouseListener(this); + } + + protected JComponent buildContent() { + + JPanel owner = new JPanel(new BorderLayout(3, 3)); + owner.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); + owner.setPreferredSize(new Dimension(250, 150)); // default popup size + + Box commands = new Box(BoxLayout.LINE_AXIS); + + JToolBar toolbar = new JToolBar(); + toolbar.setFloatable(false); + toolbar.setOpaque(false); + toolbar.add(new CommandAction( + t("jaxx.table.filter.popup.button.clearAll"), + JAXXWidgetUtil.createImageIcon("funnel_delete.png")) { + @Override + protected boolean perform() { + return clearAllFilters(); + } + }); + commands.add(toolbar); + + commands.add(Box.createHorizontalGlue()); + + commands.add(new JButton(new CommandAction(t("jaxx.table.filter.popup.button.apply")) { + @Override + protected boolean perform() { + return applyColumnFilter(); + } + }) + ); + commands.add(Box.createHorizontalStrut(5)); + commands.add(new JButton(new CommandAction(t("jaxx.table.filter.popup.button.cancel")))); + commands.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 0)); + commands.setBackground(UIManager.getColor("Panel.background")); + commands.setOpaque(true); + + if (searchable) { + owner.add(searchField, BorderLayout.NORTH); + } + owner.add(new JScrollPane(filterList.getList()), BorderLayout.CENTER); + owner.add(commands, BorderLayout.SOUTH); + + return owner; + + } + + private boolean applyColumnFilter() { + Collection<Object> checked = filterList.getCheckedItems(); + FilterableCheckListModel<Object> model = filterList.getModel(); + model.filter("", decorator, CheckListFilterType.CONTAINS); // clear filter to get true results + filter.apply(mColumnIndex, checked); + return true; + } + + private boolean clearAllFilters() { + filter.clear(); + return true; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + // Popup menus are triggered differently on different platforms + // Therefore, isPopupTrigger should be checked in both mousePressed and mouseReleased + // events for for proper cross-platform functionality + + @Override + public void mousePressed(MouseEvent e) { + if (enabled && e.isPopupTrigger()) showFilterPopup(e); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (enabled && e.isPopupTrigger()) showFilterPopup(e); + } + + private void showFilterPopup(MouseEvent e) { + JTableHeader header = (JTableHeader) (e.getSource()); + TableColumnModel colModel = filter.getTable().getColumnModel(); + + // The index of the column whose header was clicked + int vColumnIndex = colModel.getColumnIndexAtX(e.getX()); + if (vColumnIndex < 0) return; + + + // Determine if mouse was clicked between column heads + Rectangle headerRect = filter.getTable().getTableHeader().getHeaderRect(vColumnIndex); + if (vColumnIndex == 0) { + headerRect.width -= 2; + } else { + headerRect.grow(-2, 0); + } + + // Mouse was clicked between column heads + if (!headerRect.contains(e.getX(), e.getY())) return; + + // restore popup's size for the column + mColumnIndex = filter.getTable().convertColumnIndexToModel(vColumnIndex); + setPreferredSize(getColumnAttrs(vColumnIndex).preferredSize); + + Collection<Object> distinctItems = filter.getDistinctColumnItems(mColumnIndex); + if (distinctItems != null) { + + DefaultFilterableCheckListModel<Object> model = new DefaultFilterableCheckListModel<Object>(distinctItems); + filterList.setModel(actionsVisible ? new FilterableActionCheckListModel<Object>(model) : model); + Collection<Object> checked = filter.getFilterState(mColumnIndex); + + // replace empty checked items with full selection + filterList.setCheckedItems(CollectionUtils.isEmpty(checked) ? distinctItems : checked); + + if (useTableRenderers) { + filterList.getList().setCellRenderer(new TableAwareCheckListRenderer(decorator)); + } + + // show pop-up + show(header, headerRect.x, header.getHeight()); + } + } + + private ColumnAttrs getColumnAttrs(int column) { + ColumnAttrs attrs = colAttrs.get(column); + if (attrs == null) { + attrs = new ColumnAttrs(); + colAttrs.put(column, attrs); + } + + return attrs; + } + + protected void beforeShow() { + if (searchable) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + searchField.setText(""); + searchField.requestFocusInWindow(); + } + }); + } + } + + public void beforeHide() { + // save pop-up's dimensions before pop-up becomes hidden + getColumnAttrs(mColumnIndex).preferredSize = getPreferredSize(); + } + + /** + * Simple action to for the popup window. + * To use - override perform method. + * <p/> + * Created on Feb 4, 2011 + * + * @author Eugene Ryzhikov + */ + protected class CommandAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + + public CommandAction(String name, Icon icon) { + super(name, icon); + + if (icon != null) { + putValue(Action.SHORT_DESCRIPTION, name); + putValue(Action.NAME, null); + } + + } + + public CommandAction(String name) { + super(name); + } + + @Override + public final void actionPerformed(ActionEvent e) { + if (perform()) hide(); + } + + /** + * Preforms action + * + * @return true if popup should be closed + */ + protected boolean perform() { + return true; + } + } + + protected class ResizablePopupMenu extends JPopupMenu implements PopupMenuListener { + + private static final long serialVersionUID = 1L; + + private static final int DOT_SIZE = 2; + private static final int DOT_START = 2; + private static final int DOT_STEP = 4; + + public ResizablePopupMenu() { + super(); + new PopupMenuResizer(this); + addPopupMenuListener(this); + } + + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + } + + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + } + + @Override + public void popupMenuCanceled(PopupMenuEvent e) { + } + + @Override + public void paintChildren(Graphics g) { + super.paintChildren(g); + drawResizer(g); + } + + private void drawResizer(Graphics g) { + + int x = getWidth() - 2; + int y = getHeight() - 2; + + Graphics g2 = g.create(); + + try { + for (int dy = DOT_START, j = 2; j > 0; j--, dy += DOT_STEP) { + for (int dx = DOT_START, i = 0; i < j; i++, dx += DOT_STEP) { + drawDot(g2, x - dx, y - dy); + } + } + + } finally { + g2.dispose(); + } + }; + + private void drawDot(Graphics g, int x, int y) { + g.setColor(Color.WHITE); + g.fillRect(x, y, DOT_SIZE, DOT_SIZE); + g.setColor(Color.LIGHT_GRAY); + g.fillRect(x - 1, y - 1, DOT_SIZE, DOT_SIZE); + } + + } + + /** + * Allows to resize popup with the mouse. + * <p/> + * Created on Aug 6, 2010 + * + * @author exr0bs5 + */ + protected class PopupMenuResizer extends MouseAdapter { + + private final JPopupMenu menu; + + private static final int REZSIZE_SPOT_SIZE = 10; + + private Point mouseStart = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE); + + private Dimension startSize; + + private boolean isResizing = false; + + public PopupMenuResizer(JPopupMenu menu) { + this.menu = menu; + this.menu.setLightWeightPopupEnabled(true); + menu.addMouseListener(this); + menu.addMouseMotionListener(this); + } + + private boolean isInResizeSpot(Point point) { + + if (point == null) return false; + + Rectangle resizeSpot = new Rectangle( + menu.getWidth() - REZSIZE_SPOT_SIZE, + menu.getHeight() - REZSIZE_SPOT_SIZE, + REZSIZE_SPOT_SIZE, + REZSIZE_SPOT_SIZE); + + return resizeSpot.contains(point); + + } + + @Override + public void mouseMoved(MouseEvent e) { + + menu.setCursor( + Cursor.getPredefinedCursor( + isInResizeSpot(e.getPoint()) ? Cursor.SE_RESIZE_CURSOR : Cursor.DEFAULT_CURSOR)); + } + + private Point toScreen(MouseEvent e) { + + Point p = e.getPoint(); + SwingUtilities.convertPointToScreen(p, e.getComponent()); + return p; + + } + + @Override + public void mousePressed(MouseEvent e) { + mouseStart = toScreen(e); + startSize = menu.getSize(); + isResizing = isInResizeSpot(e.getPoint()); + } + + @Override + public void mouseReleased(MouseEvent e) { + mouseStart = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE); + isResizing = false; + } + + @Override + public void mouseDragged(MouseEvent e) { + + if (!isResizing) return; + + Point p = toScreen(e); + + int dx = p.x - mouseStart.x; + int dy = p.y - mouseStart.y; + + + Dimension minDim = menu.getMinimumSize(); + // Dimension maxDim = menu.getMaximumSize(); + Dimension newDim = new Dimension(startSize.width + dx, startSize.height + dy); + + if (newDim.width >= minDim.width && newDim.height >= minDim.height /*&& + newDim.width <= maxDim.width && newDim.height <= maxDim.height*/) { + menu.setPopupSize(newDim); + } + + } + } + +} + diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilterState.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilterState.java new file mode 100644 index 0000000..e42f1a1 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilterState.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.table.filter; + +import org.apache.commons.collections4.CollectionUtils; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +class TableFilterState implements Serializable { + + private static final long serialVersionUID = 1L; + + // no set - filter cleared; set - some kind of filtering + private final Map<Integer,Set<Object>> data = new HashMap<Integer,Set<Object>>(); + + /** + * Clears filtering for specific column + */ + public void clear( int column ) { + data.remove(column); + } + + + /** + * Clears all filtering + */ + public void clear() { + data.clear(); + } + + private Set<Object> prepareValueSet( int column ) { + Set<Object> vals = data.get(column); + if ( vals == null ) { + vals = new HashSet<Object>(); + data.put(column, vals); + } + return vals; + } + + + /** + * Adds filter value for specified column + * @param column + * @param value + */ + public void addValue( int column, Object value ) { + prepareValueSet(column).add(value); + } + + + /** + * Adds a collection of filter values for specified column + * @param column + * @param values + */ + public void addValues( int column, Collection<Object> values ) { + prepareValueSet(column).addAll(values); + } + + /** + * Resets a collection of filter values for specified column + * @param column + * @param values + */ + public void setValues( int column, Collection<Object> values ) { + data.remove(column); + if (CollectionUtils.isNotEmpty(values)) { + prepareValueSet(column).addAll(values); + } + } + + public Collection<Object> getValues( int column ) { + Set<Object> vals = data.get(column); + return vals == null? Collections.<Object>emptySet(): vals; + } + + /** + * Standard test for row inclusion using current filter values + * @param entry + * @return true if row has to be included + */ + public boolean include( TableFilter.Row entry ) { + + for( int col=0; col< entry.getValueCount(); col++ ) { + Collection<Object> values = getValues(col); + if ( CollectionUtils.isEmpty(values) ) continue; // no filtering for this column + if ( !values.contains( entry.getValue(col) )) return false; + } + return true; + + } + + +} diff --git a/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableRowFilterSupport.java b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableRowFilterSupport.java new file mode 100644 index 0000000..80c6794 --- /dev/null +++ b/jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableRowFilterSupport.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2009-2011, EzWare + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer.Redistributions + * in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution.Neither the name of the + * EzWare nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +package jaxx.runtime.swing.table.filter; + +import org.nuiton.decorator.Decorator; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Collections; + +import javax.swing.JTable; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; + +public final class TableRowFilterSupport { + + private boolean searchable = false; + private Decorator<Object> decorator; + private final TableFilter<?> filter; + private boolean actionsVisible = true; + private boolean useTableRenderers = false; + + private TableRowFilterSupport( TableFilter<?> filter ) { + if ( filter == null ) throw new NullPointerException(); + //this.table = table; + this.filter = filter; + } + + public static TableRowFilterSupport forFilter( TableFilter<?> filter ) { + return new TableRowFilterSupport(filter); + } + + /** + * Additional actions visible in column filter list + * @param visible + * @return + */ + public TableRowFilterSupport actions( boolean visible ) { + this.actionsVisible = visible; + return this; + } + + /** + * Comlumn filter list is searchable + * @param searchable + * @return + */ + public TableRowFilterSupport searchable( boolean searchable) { + this.searchable = searchable; + return this; + } + + public TableRowFilterSupport searchDecorator( Decorator<Object> decorator ) { + this.decorator = decorator; + return this; + } + + public TableRowFilterSupport useTableRenderers( boolean value ) { + this.useTableRenderers = value; + return this; + } + + public JTable apply() { + + final TableFilterColumnPopup filterPopup = new TableFilterColumnPopup(filter); + filterPopup.setEnabled(true); + filterPopup.setActionsVisible(actionsVisible); + filterPopup.setSearchable(searchable); + filterPopup.searchDecorator(decorator); + filterPopup.setUseTableRenderers( useTableRenderers ); + + setupTableHeader(); + + return filter.getTable(); + } + + private void setupTableHeader() { + + final JTable table = filter.getTable(); + + filter.addChangeListener(new FilterChangeListener() { + + @Override + public void filterChanged(TableFilter<?> filter) { + table.getTableHeader().repaint(); + + } + }); + + // make sure that search component is reset after table model changes + setupHeaderRenderers(table.getModel(), true ); +// table.addPropertyChangeListener("model", new PropertyChangeListener() { +// +// public void propertyChange(PropertyChangeEvent evt) { +// +// FilterTableHeaderRenderer headerRenderer = new FilterTableHeaderRenderer(filter); +// filter.modelChanged((TableModel) evt.getNewValue()); +// +// for( TableColumn c: Collections.list( filter.getTable().getColumnModel().getColumns()) ) { +// c.setHeaderRenderer( headerRenderer ); +// } +// }} +// +// ); + } + + private void setupHeaderRenderers( TableModel newModel, boolean fullSetup ) { + + JTable table = filter.getTable(); + + FilterTableHeaderRenderer headerRenderer = new FilterTableHeaderRenderer(filter); + filter.modelChanged( newModel ); + + for( TableColumn c: Collections.list( table.getColumnModel().getColumns()) ) { + c.setHeaderRenderer( headerRenderer ); + } + + if ( !fullSetup ) return; + + table.addPropertyChangeListener("model", new PropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent e) { + setupHeaderRenderers( (TableModel) e.getNewValue(), false ); + } + + }); + + + } + + +} diff --git a/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_en_GB.properties b/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_en_GB.properties index 9b4a80d..4651a1c 100644 --- a/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_en_GB.properties +++ b/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_en_GB.properties @@ -80,6 +80,9 @@ i18neditor.selected=Selected language \: %1$s i18neditor.unselected=Select this langage \: %1$s jaxx.error.no.convertor.coordinateDmd=Impossible de convert form (or to) a DMD coordinate (incoming value\: %s) jaxx.error.no.convertor.coordinateDms=Impossible de convert form (or to) a DMS coordinate (incoming value\: %s) +jaxx.table.filter.popup.button.apply=Apply +jaxx.table.filter.popup.button.cancel=Cancel +jaxx.table.filter.popup.button.clearAll=Clear all the filters memorywidget.memory=%d/%dMb numbereditor..=. numbereditor.0=0 diff --git a/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_es_ES.properties b/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_es_ES.properties index faf41ac..7aea668 100644 --- a/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_es_ES.properties +++ b/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_es_ES.properties @@ -80,6 +80,9 @@ i18neditor.selected=Idioma usado actualmente \: %1$s i18neditor.unselected=Para usar este idioma \: %1$s jaxx.error.no.convertor.coordinateDmd= jaxx.error.no.convertor.coordinateDms= +jaxx.table.filter.popup.button.apply= +jaxx.table.filter.popup.button.cancel= +jaxx.table.filter.popup.button.clearAll= memorywidget.memory=%d/%dMo numbereditor..=. numbereditor.0=0 diff --git a/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_fr_FR.properties b/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_fr_FR.properties index 58e0050..62069dd 100644 --- a/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_fr_FR.properties +++ b/jaxx-widgets/src/main/resources/i18n/jaxx-widgets_fr_FR.properties @@ -80,6 +80,9 @@ i18neditor.selected=Langue actuellement utilisée \: %1$s i18neditor.unselected=Pour utiliser cette langue \: %1$s jaxx.error.no.convertor.coordinateDmd=Impossible de convertir en (ou depuis) une coordonée DMD depuis la valeur %s jaxx.error.no.convertor.coordinateDms=Impossible de convertir en (ou depuis) une coordonée DMS depuis la valeur %s +jaxx.table.filter.popup.button.apply=Appliquer +jaxx.table.filter.popup.button.cancel=Annuler +jaxx.table.filter.popup.button.clearAll=Supprimer tous les filtres memorywidget.memory=%d/%dMo numbereditor..=. numbereditor.0=0 diff --git a/jaxx-widgets/src/main/resources/icons/funnel.png b/jaxx-widgets/src/main/resources/icons/funnel.png new file mode 100644 index 0000000..dd8344c Binary files /dev/null and b/jaxx-widgets/src/main/resources/icons/funnel.png differ diff --git a/jaxx-widgets/src/main/resources/icons/funnel_delete.png b/jaxx-widgets/src/main/resources/icons/funnel_delete.png new file mode 100644 index 0000000..f801e4d Binary files /dev/null and b/jaxx-widgets/src/main/resources/icons/funnel_delete.png differ diff --git a/jaxx-widgets/src/main/resources/icons/search.png b/jaxx-widgets/src/main/resources/icons/search.png new file mode 100644 index 0000000..d239597 Binary files /dev/null and b/jaxx-widgets/src/main/resources/icons/search.png differ -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.