package org.limewire.ui.swing.filter; import java.awt.Dimension; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.miginfocom.swing.MigLayout; import org.limewire.core.api.search.SearchCategory; import org.limewire.ui.swing.components.RangeSlider; import org.limewire.ui.swing.util.I18n; import ca.odell.glazedlists.matchers.Matcher; /** * Filter to select items according to a range of values. */ class RangeFilter<E extends FilterableItem> extends AbstractFilter<E> { private final JPanel panel = new JPanel(); private final JLabel headerLabel = new JLabel(); private final JLabel valueLabel = new JLabel(); private final RangeSlider slider = new RangeSlider(); private final RangeFilterFormat<E> rangeFormat; private boolean resetAdjusting; /** * Constructs a RangeFilter with the specified range format. */ public RangeFilter(RangeFilterFormat<E> rangeFormat) { this.rangeFormat = rangeFormat; FilterResources resources = getResources(); panel.setLayout(new MigLayout("insets 0 0 0 0, gap 0!", "[left,grow]", "[top][top]3[top]")); panel.setOpaque(false); headerLabel.setFont(resources.getHeaderFont()); headerLabel.setForeground(resources.getHeaderColor()); headerLabel.setText(rangeFormat.getHeaderText()); valueLabel.setFont(resources.getRowFont()); valueLabel.setForeground(resources.getRowColor()); slider.setMinimum(0); slider.setMaximum(rangeFormat.getValues().length - 1); slider.setOpaque(false); slider.setPreferredSize(new Dimension(resources.getFilterWidth(), slider.getPreferredSize().height)); slider.setFocusable(false); slider.setRequestFocusEnabled(false); slider.setUpperThumbEnabled(rangeFormat.isUpperLimitEnabled()); // Set initial values. resetSliderRange(); // Add listener to update filter. slider.addChangeListener(new SliderListener()); panel.add(headerLabel, "gap 6 6, wrap"); panel.add(valueLabel , "gap 6 6, wrap"); panel.add(slider , "gap 6 6, growx"); } @Override public JComponent getComponent() { return panel; } @Override public void reset() { // Reset slider values. We temporarily set resetAdjusting to true so // the slider listener can avoid handling the events. resetAdjusting = true; resetSliderRange(); resetAdjusting = false; // Deactivate filter. deactivate(); } @Override public void dispose() { // Do nothing. } /** * Returns a text description of the filter state. */ @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append(getClass().getSimpleName()).append("["); buf.append("header=").append(rangeFormat.getHeaderText()); buf.append(", active=").append(isActive()); buf.append(", selectedRange=").append(createRangeText()); buf.append("]"); return buf.toString(); } /** * Updates the range values based on the specified filter category. */ public void updateRange(SearchCategory filterCategory) { // Get current values. int oldLower = slider.getValue(); int oldUpper = slider.getUpperValue(); long[] oldValues = rangeFormat.getValues(); // Determine lower and upper values to be included. long lowerValue = (oldLower > 0) ? oldValues[oldLower] : -1; long upperValue = (oldUpper < oldValues.length - 1) ? oldValues[oldUpper] : -1; // Update range values for category. By definition, the new range // must include the specified lower and upper values. if (rangeFormat.updateValues(filterCategory, lowerValue, upperValue)) { // Get new value array. long[] newValues = rangeFormat.getValues(); // Find lower and upper slider inputs. int newLower = 0; int newUpper = newValues.length - 1; if ((lowerValue > -1) || (upperValue > -1)) { for (int i = 0; i < newValues.length; i++) { if (newValues[i] == lowerValue) { newLower = i; } if (newValues[i] == upperValue) { newUpper = i; } } } // Disable listener and re-initialize slider values. resetAdjusting = true; try { slider.setMaximum(newValues.length - 1); slider.setValue(newLower); slider.setUpperValue(newUpper); slider.repaint(); } finally { resetAdjusting = false; } } } /** * Creates a text string describing the current selected range. */ protected String createRangeText() { if (rangeFormat.isUpperLimitEnabled()) { // Get text for lower and upper values. String minText = rangeFormat.getValueText(slider.getValue()); String maxText = rangeFormat.getValueText(slider.getUpperValue()); if (slider.getUpperValue() == slider.getMaximum()) { if (rangeFormat.isMaximumAbsolute()) { return minText; } // {0}: lower bound of open-ended numeric range return I18n.tr("{0} or above", minText); } else { // {0}: lower bound of numeric range, {1}: upper bound of numeric range return I18n.tr("{0} to {1}", minText, maxText); } } else { // Get text for lower value only. String minText = rangeFormat.getValueText(slider.getValue()); if (slider.getValue() == slider.getMinimum()) { return I18n.tr("all"); } else if ((slider.getValue() == slider.getMaximum()) && rangeFormat.isMaximumAbsolute()) { return minText; } else { return I18n.tr("{0} or above", minText); } } } /** * Resets the range values to the minimum and maximum. */ private void resetSliderRange() { // Set lower value to minimum. slider.setValue(slider.getMinimum()); // Set upper value to maximum if enabled. if (rangeFormat.isUpperLimitEnabled()) { slider.setUpperValue(slider.getMaximum()); } // Set range text. valueLabel.setText(createRangeText()); } /** * Listener to handle slider change events. */ private class SliderListener implements ChangeListener { @Override public void stateChanged(ChangeEvent e) { // Skip event if filter is being reset. if (resetAdjusting) { return; } // Update range display. RangeSlider slider = (RangeSlider) e.getSource(); String rangeText = createRangeText(); valueLabel.setText(rangeText); // When slider movement has ended, apply filter for selected range. if (!slider.getValueIsAdjusting()) { long[] values = rangeFormat.getValues(); // Determine if filter is activated. This is true when the // slider values are set to anything other than the min/max. boolean activate = false; if (rangeFormat.isUpperLimitEnabled()) { activate = (slider.getValue() > 0) || (slider.getUpperValue() < values.length - 1); } else { activate = (slider.getValue() > 0); } if (activate) { // Get selected range. long minValue = values[slider.getValue()]; long maxValue = values[slider.getUpperValue()]; // Create new matcher and activate. Matcher<E> newMatcher = rangeFormat.getMatcher(minValue, maxValue); activate(rangeText, newMatcher); } else { // Deactivate to clear matcher. deactivate(); } // Notify filter listeners. fireFilterChanged(RangeFilter.this); } } } }