All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdesktop.swingx.decorator.HighlightPredicate Maven / Gradle / Ivy

The newest version!
/*
 * $Id: HighlightPredicate.java 3935 2011-03-02 19:06:41Z kschaefe $
 *
 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jdesktop.swingx.decorator;

import java.awt.Component;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

import org.jdesktop.swingx.rollover.RolloverProducer;
import org.jdesktop.swingx.util.Contract;

/**
 * A controller which decides whether or not a visual decoration should
 * be applied to the given Component in the given ComponentAdapter state. 
 * This is a on/off decision only, the actual decoration is
 * left to the AbstractHighlighter which typically respects this predicate. 

* * Note: implementations should be immutable because Highlighters * guarantee to notify listeners on any state change which might effect the highlight. * They can't comply to that contract if predicate internal state changes under their * feet. If dynamic predicate state is required, the safe alternative is to create * and set a new predicate.

* * * @author Jeanette Winzenburg * * @see AbstractHighlighter */ public interface HighlightPredicate { /** * Returns a boolean to indicate whether the component should be * highlighted.

* * Note: both parameters should be considered strictly read-only! * * @param renderer the cell renderer component that is to be decorated, * must not be null * @param adapter the ComponentAdapter for this decorate operation, * most not be null * @return a boolean to indicate whether the component should be highlighted. */ boolean isHighlighted(Component renderer, ComponentAdapter adapter); //--------------------- implemented Constants /** * Unconditional true. */ public static final HighlightPredicate ALWAYS = new HighlightPredicate() { /** * {@inheritDoc}

* * Implemented to return true always. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return true; } }; /** * Unconditional false. */ public static final HighlightPredicate NEVER = new HighlightPredicate() { /** * {@inheritDoc}

* * Implemented to return false always. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return false; } }; /** * Rollover Row. */ public static final HighlightPredicate ROLLOVER_ROW = new HighlightPredicate() { /** * @inheritDoc * Implemented to return true if the adapter's component is enabled and * the row of its rollover property equals the adapter's row, returns * false otherwise. * * @see org.jdesktop.swingx.rollover.RolloverProducer */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { if (!adapter.getComponent().isEnabled()) return false; Point p = (Point) adapter.getComponent().getClientProperty( RolloverProducer.ROLLOVER_KEY); return p != null && p.y == adapter.row; } }; /** * Rollover Column. */ public static final HighlightPredicate ROLLOVER_COLUMN = new HighlightPredicate() { /** * @inheritDoc * Implemented to return true if the adapter's component is enabled and * the column of its rollover property equals the adapter's columns, returns * false otherwise. * * @see org.jdesktop.swingx.rollover.RolloverProducer */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { if (!adapter.getComponent().isEnabled()) return false; Point p = (Point) adapter.getComponent().getClientProperty( RolloverProducer.ROLLOVER_KEY); return p != null && p.x == adapter.column; } }; /** * Rollover Cell. */ public static final HighlightPredicate ROLLOVER_CELL = new HighlightPredicate() { /** * @inheritDoc * Implemented to return true if the adapter's component is enabled and * the column of its rollover property equals the adapter's columns, returns * false otherwise. * * @see org.jdesktop.swingx.rollover.RolloverProducer */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { if (!adapter.getComponent().isEnabled()) return false; Point p = (Point) adapter.getComponent().getClientProperty( RolloverProducer.ROLLOVER_KEY); return p != null && p.y == adapter.row && p.x == adapter.column; } }; /** * Is editable. */ public static final HighlightPredicate EDITABLE = new HighlightPredicate() { /** * {@inheritDoc}

* * Implemented to return true is the given adapter isEditable, false otherwise. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return adapter.isEditable(); } }; /** * Convenience for read-only (same as !editable). */ public static final HighlightPredicate READ_ONLY = new HighlightPredicate() { /** * {@inheritDoc}

* * Implemented to return false is the given adapter isEditable, true otherwise. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return !adapter.isEditable(); } }; /** * Leaf predicate. */ public static final HighlightPredicate IS_LEAF = new HighlightPredicate() { /** * {@inheritDoc}

* * Implemented to return true if the given adapter isLeaf, false otherwise. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return adapter.isLeaf(); } }; /** * Folder predicate - convenience: same as !IS_LEAF. */ public static final HighlightPredicate IS_FOLDER = new HighlightPredicate() { /** * {@inheritDoc}

* * Implemented to return false if the given adapter isLeaf, true otherwise. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return !adapter.isLeaf(); } }; /** * Selected predicate. */ public static final HighlightPredicate IS_SELECTED = new HighlightPredicate() { @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return adapter.isSelected(); } }; /** * Determines if the displayed text is truncated. * * @author Karl Schaefer */ public static final HighlightPredicate IS_TEXT_TRUNCATED = new HighlightPredicate() { @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { JComponent c = renderer instanceof JComponent ? (JComponent) renderer : null; String text = adapter.getString(); Icon icon = null; //defaults from JLabel int verticalAlignment = SwingConstants.CENTER; int horizontalAlignment = SwingConstants.LEADING; int verticalTextPosition = SwingConstants.CENTER; int horizontalTextPosition = SwingConstants.TRAILING; int gap = 0; if (renderer instanceof JLabel) { icon = ((JLabel) renderer).getIcon(); gap = ((JLabel) renderer).getIconTextGap(); } else if (renderer instanceof AbstractButton) { icon = ((AbstractButton) renderer).getIcon(); gap = ((AbstractButton) renderer).getIconTextGap(); } Rectangle cellBounds = adapter.getCellBounds(); if (c != null && c.getBorder() != null) { Insets insets = c.getBorder().getBorderInsets(c); cellBounds.width -= insets.left + insets.right; cellBounds.height -= insets.top + insets.bottom; } String result = SwingUtilities.layoutCompoundLabel(c, renderer .getFontMetrics(renderer.getFont()), text, icon, verticalAlignment, horizontalAlignment, verticalTextPosition, horizontalTextPosition, cellBounds, new Rectangle(), new Rectangle(), gap); return !text.equals(result); } }; /** * Focus predicate. */ public static final HighlightPredicate HAS_FOCUS = new HighlightPredicate() { /** * {@inheritDoc}

* * Implemented to return truw if the given adapter hasFocus, false otherwise. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return adapter.hasFocus(); } }; /** * Even rows. * * PENDING: this is zero based (that is "really" even 0, 2, 4 ..), differing * from the old AlternateRowHighlighter. * */ public static final HighlightPredicate EVEN = new HighlightPredicate() { @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return adapter.row % 2 == 0; } }; /** * Odd rows. * * PENDING: this is zero based (that is 1, 3, 4 ..), differs from * the old implementation which was one based? * */ public static final HighlightPredicate ODD = new HighlightPredicate() { @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return !EVEN.isHighlighted(renderer, adapter); } }; /** * Negative BigDecimals. */ public static final HighlightPredicate BIG_DECIMAL_NEGATIVE = new HighlightPredicate() { @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return (adapter.getValue() instanceof BigDecimal) && ((BigDecimal) adapter.getValue()).compareTo(BigDecimal.ZERO) < 0; } }; /** * Negative Number. */ public static final HighlightPredicate INTEGER_NEGATIVE = new HighlightPredicate() { @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return (adapter.getValue() instanceof Number) && ((Number) adapter.getValue()).intValue() < 0; } }; // PENDING: these general type empty arrays don't really belong here? public static final HighlightPredicate[] EMPTY_PREDICATE_ARRAY = new HighlightPredicate[0]; public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; public static final Integer[] EMPTY_INTEGER_ARRAY = new Integer[0]; //----------------- logical implementations amongst HighlightPredicates /** * Negation of a HighlightPredicate. */ public static class NotHighlightPredicate implements HighlightPredicate { private HighlightPredicate predicate; /** * Instantiates a not against the given predicate. * @param predicate the predicate to negate, must not be null. * @throws NullPointerException if the predicate is null */ public NotHighlightPredicate(HighlightPredicate predicate) { if (predicate == null) throw new NullPointerException("predicate must not be null"); this.predicate = predicate; } /** * {@inheritDoc} * Implemented to return the negation of the given predicate. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return !predicate.isHighlighted(renderer, adapter); } /** * @return the contained HighlightPredicate. */ public HighlightPredicate getHighlightPredicate() { return predicate; } } /** * Ands a list of predicates. */ public static class AndHighlightPredicate implements HighlightPredicate { private List predicate; /** * Instantiates a predicate which ands all given predicates. * @param predicate zero or more not null predicates to and * @throws NullPointerException if the predicate is null */ public AndHighlightPredicate(HighlightPredicate... predicate) { this.predicate = Arrays.asList(Contract.asNotNull(predicate, "predicate must not be null")); } /** * Instantiates a predicate which ANDs all contained predicates. * @param list a collection with zero or more not null predicates to AND * @throws NullPointerException if the collection is null */ public AndHighlightPredicate(Collection list) { this.predicate = new ArrayList(Contract.asNotNull(list, "predicate list must not be null")); } /** * {@inheritDoc} * Implemented to return false if any of the contained predicates is * false or if there are no predicates. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { for (HighlightPredicate hp : predicate) { if (!hp.isHighlighted(renderer, adapter)) return false; } return !predicate.isEmpty(); } /** * @return the contained HighlightPredicates. */ public HighlightPredicate[] getHighlightPredicates() { if (predicate.isEmpty()) return EMPTY_PREDICATE_ARRAY; return predicate.toArray(new HighlightPredicate[predicate.size()]); } } /** * Or's a list of predicates. */ public static class OrHighlightPredicate implements HighlightPredicate { private List predicate; /** * Instantiates a predicate which ORs all given predicates. * @param predicate zero or more not null predicates to OR * @throws NullPointerException if the predicate is null */ public OrHighlightPredicate(HighlightPredicate... predicate) { this.predicate = Arrays.asList(Contract.asNotNull(predicate, "predicate must not be null")); } /** * Instantiates a predicate which ORs all contained predicates. * @param list a collection with zero or more not null predicates to OR * @throws NullPointerException if the collection is null */ public OrHighlightPredicate(Collection list) { this.predicate = new ArrayList(Contract.asNotNull(list, "predicate list must not be null")); } /** * {@inheritDoc} * Implemented to return true if any of the contained predicates is * true. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { for (HighlightPredicate hp : predicate) { if (hp.isHighlighted(renderer, adapter)) return true; } return false; } /** * @return all registered predicates */ public HighlightPredicate[] getHighlightPredicates() { if (predicate.isEmpty()) return EMPTY_PREDICATE_ARRAY; return predicate.toArray(new HighlightPredicate[predicate.size()]); } } //------------------------ coordinates public static class RowGroupHighlightPredicate implements HighlightPredicate { private int linesPerGroup; /** * Instantiates a predicate with the given grouping. * * @param linesPerGroup number of lines constituting a group, must * be > 0 * @throws IllegalArgumentException if linesPerGroup < 1 */ public RowGroupHighlightPredicate(int linesPerGroup) { if (linesPerGroup < 1) throw new IllegalArgumentException("a group contain at least 1 row, was: " + linesPerGroup); this.linesPerGroup = linesPerGroup; } /** * {@inheritDoc} * Implemented to return true if the adapter's row falls into a * odd group number. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { // JW: oddness check is okay - adapter.row must be a valid view coordinate return (adapter.row / linesPerGroup) % 2 == 1; } /** * * @return the number of lines per group. */ public int getLinesPerGroup() { return linesPerGroup; } } /** * A HighlightPredicate based on column index. * */ public static class ColumnHighlightPredicate implements HighlightPredicate { List columnList; /** * Instantiates a predicate which returns true for the * given columns in model coordinates. * * @param columns the columns to highlight in model coordinates. */ public ColumnHighlightPredicate(int... columns) { columnList = new ArrayList(); for (int i = 0; i < columns.length; i++) { columnList.add(columns[i]); } } /** * {@inheritDoc} * * This implementation returns true if the adapter's column * is contained in this predicates list. * */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { int modelIndex = adapter.convertColumnIndexToModel(adapter.column); return columnList.contains(modelIndex); } /** * PENDING JW: get array of int instead of Integer? * * @return the columns indices in model coordinates to highlight */ public Integer[] getColumns() { if (columnList.isEmpty()) return EMPTY_INTEGER_ARRAY; return columnList.toArray(new Integer[columnList.size()]); } } /** * A HighlightPredicate based on column identifier. * */ public static class IdentifierHighlightPredicate implements HighlightPredicate { List columnList; /** * Instantiates a predicate which returns true for the * given column identifiers. * * @param columns the identitiers of the columns to highlight. */ public IdentifierHighlightPredicate(Object... columns) { columnList = new ArrayList(); for (int i = 0; i < columns.length; i++) { columnList.add(columns[i]); } } /** * {@inheritDoc} * * This implementation returns true if the adapter's column * is contained in this predicates list. * */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { int modelIndex = adapter.convertColumnIndexToModel(adapter.column); Object identifier = adapter.getColumnIdentifierAt(modelIndex); return identifier != null ? columnList.contains(identifier) : false; } /** * @return the identifiers */ public Object[] getIdentifiers() { if (columnList.isEmpty()) return EMPTY_OBJECT_ARRAY; return columnList.toArray(new Object[0]); } } /** * A {@code HighlightPredicate} based on adapter depth. * * @author Karl Schaefer */ public static class DepthHighlightPredicate implements HighlightPredicate { private List depthList; /** * Instantiates a predicate which returns true for the * given depths. * * @param depths the depths to highlight */ public DepthHighlightPredicate(int... depths) { depthList = new ArrayList(); for (int i = 0; i < depths.length; i++) { depthList.add(depths[i]); } } /** * {@inheritDoc} * * This implementation returns true if the adapter's depth is contained * in this predicates list. * */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { int depth = adapter.getDepth(); return depthList.contains(depth); } /** * @return array of numbers representing different depths */ public Integer[] getDepths() { if (depthList.isEmpty()) return EMPTY_INTEGER_ARRAY; return depthList.toArray(new Integer[depthList.size()]); } } //--------------------- value testing /** * Predicate testing the componentAdapter value against a fixed * Object. */ public static class EqualsHighlightPredicate implements HighlightPredicate { private Object compareValue; /** * Instantitates a predicate with null compare value. * */ public EqualsHighlightPredicate() { this(null); } /** * Instantiates a predicate with the given compare value. * PENDING JW: support array? * @param compareValue the fixed value to compare the * adapter against. */ public EqualsHighlightPredicate(Object compareValue) { this.compareValue = compareValue; } /** * {@inheritDoc} * * Implemented to return true if the adapter value equals the * this predicate's compare value. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { if (compareValue == null) return adapter.getValue() == null; return compareValue.equals(adapter.getValue()); } /** * @return the value this predicate checks against. */ public Object getCompareValue() { return compareValue; } } /** * Predicate testing the componentAdapter value type against a given * Class. */ public static class TypeHighlightPredicate implements HighlightPredicate { private Class clazz; /** * Instantiates a predicate with Object.clazz. This is essentially the * same as testing the adapter's value against null. * */ public TypeHighlightPredicate() { this(Object.class); } /** * Instantiates a predicate with the given compare class.

* * PENDING JW: support array? * * @param compareValue the fixed class to compare the * adapter value against, must not be null * * @throws NullPointerException if the class is null. */ public TypeHighlightPredicate(Class compareValue) { this.clazz = Contract.asNotNull(compareValue, "compare class must not be null"); } /** * {@inheritDoc} * * Implemented to return true if the adapter value is an instance * of this predicate's class type. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return adapter.getValue() != null ? clazz.isAssignableFrom(adapter.getValue().getClass()) : false; } /** * @return type of predicate compare class */ public Class getType() { return clazz; } } /** * Predicate testing the componentAdapter column type against a given * Class. */ public static class ColumnTypeHighlightPredicate implements HighlightPredicate { private Class clazz; /** * Instantitates a predicate with Object.class.

* * PENDING JW: this constructor is not very useful ... concrete implementations of * ComponentAdapter are required to return a not-null from their * getColumnClass() methods). * */ public ColumnTypeHighlightPredicate() { this(Object.class); } /** * Instantitates a predicate with the given compare class. * * @param compareValue the fixed class to compare the * adapter's column class against, must not be null * * @throws NullPointerException if the class is null. * */ public ColumnTypeHighlightPredicate(Class compareValue) { this.clazz = Contract.asNotNull(compareValue, "compare class must not be null"); } /** * @inheritDoc * * Implemented to return true if the adapter value is an instance * of this predicate's class type. */ @Override public boolean isHighlighted(Component renderer, ComponentAdapter adapter) { return clazz.isAssignableFrom(adapter.getColumnClass()); } public Class getType() { return clazz; } } }