[jaxx] branch develop updated (2e96c9f -> e8990c6)
This is an automated email from the git hooks/post-receive script. New change to branch develop in repository jaxx. See http://git.nuiton.org/jaxx.git from 2e96c9f refs #3513, introduce a SwingListValidatior constructors new e8990c6 fixes #3515 Create a table filter The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit e8990c6406975264c112c84222e2b09b2474e182 Author: Kevin Morin <morin@codelutin.com> Date: Thu Sep 18 11:53:42 2014 +0200 fixes #3515 Create a table filter Summary of changes: .../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(+) create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/CompoundIcon.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/JSearchTextField.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/ActionCheckListModel.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckList.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListAction.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListEditor.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListModel.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/CheckListRenderer.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/DefaultCheckListModel.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/CheckListFilterType.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/DefaultFilterableCheckListModel.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableActionCheckListModel.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableCheckList.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/list/filter/FilterableCheckListModel.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/AbstractTableFilter.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/FilterChangeListener.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/FilterTableHeaderRenderer.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/JTableFilter.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableAwareCheckListRenderer.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilter.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilterColumnPopup.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableFilterState.java create mode 100644 jaxx-widgets/src/main/java/jaxx/runtime/swing/table/filter/TableRowFilterSupport.java create mode 100644 jaxx-widgets/src/main/resources/icons/funnel.png create mode 100644 jaxx-widgets/src/main/resources/icons/funnel_delete.png create mode 100644 jaxx-widgets/src/main/resources/icons/search.png -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.
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>.
participants (1)
-
nuiton.org scm