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

io.guise.framework.component.AbstractListSelectContainerControl Maven / Gradle / Ivy

There is a newer version: 0.5.3
Show newest version
/*
 * Copyright © 2005-2008 GlobalMentor, Inc. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.guise.framework.component;

import static java.util.Objects.*;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.util.*;

import com.globalmentor.event.EventListenerManager;

import io.guise.framework.component.layout.*;
import io.guise.framework.event.*;
import io.guise.framework.model.*;
import io.guise.framework.validator.*;

/**
 * An abstract list select control that is also a container. The component valid status is updated before a change in the {@link #VALUE_PROPERTY} or the
 * {@link #VALIDATOR_PROPERTY} is fired.
 * 

* This implementation installs a default value representation strategy that simply passes through the represented component. *

* @author Garret Wilson */ public abstract class AbstractListSelectContainerControl extends AbstractContainerControl implements ListSelectControl { /** The static representation strategy to represent component values as themselves. */ public static final ComponentRepresentationStrategy COMPONENT_REPRESENTATION_STRATEGY = new ComponentRepresentationStrategy(); //TODO make sure we listen for enabled status changing on the layout and send an index enabled property change, maybe @Override public AbstractValueLayout getLayout() { return (AbstractValueLayout)super.getLayout(); } /** The strategy used to generate a component to represent each value in the model. */ private ValueRepresentationStrategy valueRepresentationStrategy; @Override public ValueRepresentationStrategy getValueRepresentationStrategy() { return valueRepresentationStrategy; } @Override public void setValueRepresentationStrategy(final ValueRepresentationStrategy newValueRepresentationStrategy) { if(valueRepresentationStrategy != newValueRepresentationStrategy) { //if the value is really changing final ValueRepresentationStrategy oldValueRepresentationStrategy = valueRepresentationStrategy; //get the old value valueRepresentationStrategy = requireNonNull(newValueRepresentationStrategy, "Value representation strategy cannot be null."); //actually change the value firePropertyChange(VALUE_REPRESENTATION_STRATEGY_PROPERTY, oldValueRepresentationStrategy, newValueRepresentationStrategy); //indicate that the value changed } } /** * {@inheritDoc} *

* This version returns the given component to fulfill the interface contract of {@link ListSelectControl}. *

*/ @Override public Component getComponent(final Component object) { return object; //return the given component } /** * Layout constructor. * @param layout The layout definition for the container. * @throws NullPointerException if the given layout is null. */ protected AbstractListSelectContainerControl(final AbstractValueLayout layout) { super(layout); //construct the parent class this.valueRepresentationStrategy = COMPONENT_REPRESENTATION_STRATEGY; //use the shared component representation strategy layout.addPropertyChangeListener(getRepeatPropertyChangeListener()); //listen an repeat all property changes of the card layout value model TODO make sure the card constraint values are passed on, too---right now they probably aren't as the property change event subclass isn't recognized in the repeater listener class layout.addVetoableChangeListener(getRepeatVetoableChangeListener()); //listen and repeat all vetoable changes of the card layout value model addPropertyChangeListener(VALUE_PROPERTY, new PropertyChangeListener() { //listen for the value changing @Override public void propertyChange(final PropertyChangeEvent propertyChangeEvent) { //if the value changes fireSelectionChanged(null, null); //indicate that the selection changed } }); } /** * {@inheritDoc} *

* This version first updates the valid status if the value is reported as being changed. *

*/ @Override protected void firePropertyChange(final String propertyName, final VV oldValue, final VV newValue) { if(VALUE_PROPERTY.equals(propertyName) || VALIDATOR_PROPERTY.equals(propertyName)) { //if the value property or the validator property is being reported as changed updateValid(); //update the valid status based upon the new property, so that any listeners will know whether the new property is valid } super.firePropertyChange(propertyName, oldValue, newValue); //fire the property change event normally } /** * {@inheritDoc} *

* This version only checks the validity of the selected card. *

*/ @Override protected boolean determineChildrenValid() { final Component selectedComponent = getValue(); //get the selected card return selectedComponent == null || selectedComponent.isValid(); //the children will only be invalid if the selected card is invalid } /** * {@inheritDoc} *

* This version only validates the selected card. *

*/ @Override public boolean validateChildren() { final Component selectedComponent = getValue(); //get the selected card return selectedComponent != null ? selectedComponent.validate() : false; //only validate the selected card if there is one } //ValueModel delegations @Override public Component getDefaultValue() { return getLayout().getDefaultValue(); } @Override public Component getValue() { return getLayout().getValue(); } @Override public void setValue(final Component newValue) throws PropertyVetoException { getLayout().setValue(newValue); } @Override public void clearValue() { getLayout().clearValue(); } @Override public void resetValue() { getLayout().resetValue(); } @Override public Validator getValidator() { return getLayout().getValidator(); } @Override public void setValidator(final Validator newValidator) { getLayout().setValidator(newValidator); } @Override public boolean isValidValue() { return getLayout().isValidValue(); } @Override public void validateValue() throws ValidationException { getLayout().validateValue(); } @Override public Class getValueClass() { return getLayout().getValueClass(); } //SelectModel delegations @Override public boolean replace(final Component oldValue, final Component newValue) { throw new UnsupportedOperationException("replace() not yet supported"); } @Override public Component getSelectedValue() { return getValue(); } @Override public Component[] getSelectedValues() { final Component selectedValue = getValue(); //get the selected value, if any return selectedValue != null ? new Component[] {selectedValue} : new Component[] {}; //return an array with the component, if there is one selected } @Override public void setSelectedValues(final Component... values) throws PropertyVetoException { setValue(values.length > 0 ? values[0] : null); //select the first or no value } /** The shared single component selection policy. */ private static final ListSelectionPolicy SINGLE_COMPONENT_SELECTION_POLICY = new SingleListSelectionPolicy(); //ListSelectModel delegations @Override public ListSelectionPolicy getSelectionPolicy() { return SINGLE_COMPONENT_SELECTION_POLICY; } @Override public int getSelectedIndex() { return getLayout().getSelectedIndex(); } @Override public int[] getSelectedIndexes() { final int selectedIndex = getLayout().getSelectedIndex(); //get the selected index return selectedIndex >= 0 ? new int[] {selectedIndex} : new int[] {}; //return the selected index in an array, if there is a selected index } @Override public void setSelectedIndexes(final int... indexes) throws PropertyVetoException { getLayout().setSelectedIndex(indexes.length > 0 ? indexes[0] : -1); //select the first index if there are indexes to select } @Override public void addSelectedIndexes(int... indexes) throws PropertyVetoException { if(getSelectedIndex() < 0) { //only if there are no selected indexes setSelectedIndexes(indexes); //set the new index } } @Override public void removeSelectedIndexes(int... indexes) throws PropertyVetoException { final int selectedIndex = getLayout().getSelectedIndex(); //get the selected index for(int i = indexes.length - 1; i >= 0; --i) { //for each index index final int index = indexes[i]; //get this index if(index == selectedIndex) { //if this index is selected getLayout().setSelectedIndex(-1); //clear the selected index break; //there can only have been one selected index } } } @Override public boolean isValueDisplayed(final Component value) { return getLayout().getConstraints(value).isDisplayed(); } @Override public void setValueDisplayed(final Component value, final boolean newDisplayed) { getLayout().getConstraints(value).setDisplayed(newDisplayed); } //TODO fix property change event @Override public boolean isIndexDisplayed(final int index) { return isValueDisplayed(get(index)); } @Override public void setIndexDisplayed(final int index, final boolean newDisplayed) { setValueDisplayed(get(index), newDisplayed); } //TODO fix property change event @Override public boolean isValueEnabled(final Component value) { return getLayout().getConstraints(value).isEnabled(); } @Override public void setValueEnabled(final Component value, final boolean newEnabled) { getLayout().getConstraints(value).setEnabled(newEnabled); } //TODO fix property change event @Override public boolean isIndexEnabled(final int index) { return isValueEnabled(get(index)); } @Override public void setIndexEnabled(final int index, final boolean newEnabled) { setValueEnabled(get(index), newEnabled); } //TODO fix property change event @Override public void addListListener(final ListListener listListener) { getEventListenerManager().add(ListListener.class, listListener); //add the listener } @Override public void removeListListener(final ListListener listListener) { getEventListenerManager().remove(ListListener.class, listListener); //remove the listener } @Override public void addListSelectionListener(final ListSelectionListener selectionListener) { getEventListenerManager().add(ListSelectionListener.class, selectionListener); //add the listener } @Override public void removeListSelectionListener(final ListSelectionListener selectionListener) { getEventListenerManager().remove(ListSelectionListener.class, selectionListener); //remove the listener } /** * {@inheritDoc} *

* This implementation also fires a list modified event to all registered list listeners, if any. *

*/ @Override protected void fireChildComponentAdded(final ComponentEvent childComponentEvent) { //TODO it might be better to listen for the composite component events and act accordingly super.fireChildComponentAdded(childComponentEvent); //fire the component added event normally if(getEventListenerManager().hasListeners(ListListener.class)) { //if there are appropriate listeners registered final Component childComponent = childComponentEvent.getComponent(); //get the added child component final ListEvent listEvent = new ListEvent(this, indexOf(childComponent), childComponent, null); //create a new list event for(final ListListener listListener : getEventListenerManager().getListeners(ListListener.class)) { //for each list listener listListener.listModified(listEvent); //dispatch the list event to the listener } } } /** * Fires a given component removed event to all registered composite component listeners. This implementation also fires a list modified event to all * registered list listeners, if any. * @param childComponentEvent The child component event to fire. */ protected void fireChildComponentRemoved(final ComponentEvent childComponentEvent) { //TODO it might be better to listen for the composite component events and act accordingly super.fireChildComponentRemoved(childComponentEvent); //fire the component removed event normally if(getEventListenerManager().hasListeners(ListListener.class)) { //if there are appropriate listeners registered final Component childComponent = childComponentEvent.getComponent(); //get the removed child component final ListEvent listEvent = new ListEvent(this, -1, null, childComponent); //create a new list event for(final ListListener listListener : getEventListenerManager().getListeners(ListListener.class)) { //for each list listener listListener.listModified(listEvent); //dispatch the list event to the listener } } } /** * Fires an event to all registered selection listeners indicating the selection changed. * @param addedIndex The index that was added to the selection, or null if no index was added or it is unknown whether or which indices were * added. * @param removedIndex The index that was removed from the list, or null if no index was removed or it is unknown whether or which indices were * removed. * @see ListSelectionListener * @see ListSelectionEvent */ protected void fireSelectionChanged(final Integer addedIndex, final Integer removedIndex) { final EventListenerManager eventListenerManager = getEventListenerManager(); //get the event listener manager if(eventListenerManager.hasListeners(ListSelectionListener.class)) { //if there are appropriate listeners registered final ListSelectionEvent selectionEvent = new ListSelectionEvent(this, addedIndex, removedIndex); //create a new event for(final ListSelectionListener listener : eventListenerManager.getListeners(ListSelectionListener.class)) { //for each registered event listeners listener.listSelectionChanged(selectionEvent); //dispatch the event } } } //List delegations @Override public Object[] toArray() { return getComponentList().toArray(); } @Override public T[] toArray(final T[] array) { return getComponentList().toArray(array); } @Override public boolean containsAll(final Collection collection) { return getComponentList().containsAll(collection); } @Override public boolean addAll(final Collection collection) { throw new UnsupportedOperationException("addAll(Collection) not yet supported"); } //TODO add all these to container @Override public synchronized boolean addAll(final int index, final Collection collection) { throw new UnsupportedOperationException("addAll(index, Collection) not yet supported"); } @Override public boolean removeAll(final Collection collection) { throw new UnsupportedOperationException("removeAll(Collection) not yet supported"); } @Override public boolean retainAll(final Collection collection) { throw new UnsupportedOperationException("retainAll(Collection) not yet supported"); } @Override public Component set(final int index, final Component value) { throw new UnsupportedOperationException("set(index, value) not yet supported"); } @Override public ListIterator listIterator() { return getComponentList().listIterator(); } @Override public ListIterator listIterator(final int index) { return getComponentList().listIterator(index); } @Override public List subList(final int fromIndex, final int toIndex) { return getComponentList().subList(fromIndex, toIndex); } /** * Adds a component to the container along with a label. This convenience method creates new card layout constraints from the given label model and adds the * component. * @param component The component to add. * @param labelModel The label associated with an individual component. * @throws NullPointerException if the given label is null. * @throws IllegalArgumentException if the component already has a parent. */ /*TODO del if not wanted public void add(final Component component, final LabelModel labelModel) { add(component, new CardLayout.Constraints(labelModel)); //create card layout constraints for the label and add the component to the container } */ /** * Convenience method to determine whether a card is displayed based upon its associated constraints. * @param component The component for which the card should be displayed or not displayed. * @return Whether the card is displayed or has no representation, taking up no space. * @throws IllegalStateException if the given component has no associated constraints. * @see ControlConstraints#isDisplayed() */ public boolean isDisplayed(final Component component) { final ControlConstraints cardConstraints = getLayout().getConstraints(component); //get constraints of the component if(cardConstraints == null) { //if there are no constraints throw new IllegalStateException("Component " + component + " has no associated constraints."); } return cardConstraints.isDisplayed(); //return the displayed status of the constraints } /** * Sets a card displayed or not displayed. This convenience method changes the displayed status of the component's associated constraints. * @param component The component for which the card should be displayed or not displayed. * @param newDisplayed true if the card should be displayed. * @throws IllegalStateException if the given component has no associated constraints. * @see ControlConstraints#setDisplayed(boolean) */ public void setDisplayed(final Component component, final boolean newDisplayed) { final ControlConstraints cardConstraints = getLayout().getConstraints(component); //get constraints of the component if(cardConstraints == null) { //if there are no constraints throw new IllegalStateException("Component " + component + " has no associated constraints."); } cardConstraints.setDisplayed(newDisplayed); //change the displayed status of the constraints } /** * Convenience method to determine whether a card is enabled based upon its associated constraints. * @param component The component for which the card should be enabled or disabled. * @return Whether the card is enabled and can receive user input. * @throws IllegalStateException if the given component has no associated constraints. * @see CardConstraints#isEnabled() */ public boolean isEnabled(final Component component) { final ControlConstraints cardConstraints = getLayout().getConstraints(component); //get constraints of the component if(cardConstraints == null) { //if there are no constraints throw new IllegalStateException("Component " + component + " has no associated constraints."); } return cardConstraints.isEnabled(); //return the enabled status of the constraints } /** * Enables or disables a card. This convenience method changes the enabled status of the component's associated constraints. * @param component The component for which the card should be enabled or disabled. * @param newEnabled true if the card can be selected. * @throws IllegalStateException if the given component has no associated constraints. * @see CardConstraints#setEnabled(boolean) */ public void setEnabled(final Component component, final boolean newEnabled) { final ControlConstraints cardConstraints = getLayout().getConstraints(component); //get constraints of the component if(cardConstraints == null) { //if there are no constraints throw new IllegalStateException("Component " + component + " has no associated constraints."); } cardConstraints.setEnabled(newEnabled); //change the enabled status of the constraints } /** * A strategy for to represent components in a list select model as themselves. * @author Garret Wilson */ public static class ComponentRepresentationStrategy implements ValueRepresentationStrategy { /** * {@inheritDoc} *

* This implementation returns the component value itself. *

*/ @Override public Component createComponent(final ListSelectModel model, final Component value, final int index, final boolean selected, final boolean focused) { return value; //return the component to represent itself } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy