001    /*
002     * Copyright 2002-2006 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.list;
017    
018    import javax.swing.ListSelectionModel;
019    import javax.swing.event.ListSelectionEvent;
020    import javax.swing.event.ListSelectionListener;
021    
022    import org.springframework.binding.value.support.AbstractValueModel;
023    
024    /**
025     * Class to adapt the selection model of a list into a value model. This allows
026     * it to be used in conjunction with various Guard implementations.
027     * 
028     * @author Larry Streepy
029     * @see ListSingleSelectionGuard
030     * @see ListMultipleSelectionGuard
031     */
032    public class ListSelectionValueModelAdapter extends AbstractValueModel implements ListSelectionListener {
033    
034        private ListSelectionModel model;
035        private int[] currentSelection = new int[0];
036        private boolean skipSelectionModelUpdate = false;
037    
038        /**
039         * Constructor.
040         * 
041         * @param model selection model to adapt
042         */
043        public ListSelectionValueModelAdapter( ListSelectionModel model ) {
044            this.model = model;
045            this.model.addListSelectionListener(this);
046        }
047    
048        /*
049         * (non-Javadoc)
050         * 
051         * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
052         */
053        public void valueChanged( ListSelectionEvent e ) {
054            if( !e.getValueIsAdjusting() ) {
055    
056                // We need to install this new value into the value model, but
057                // we don't want to propogate it back down to the adapted selection
058                // model (since we are responding to a change in that model).
059                skipSelectionModelUpdate = true;
060                setValue(getSelectedRows());
061                skipSelectionModelUpdate = false;
062            }
063        }
064    
065        /*
066         * (non-Javadoc)
067         * 
068         * @see org.springframework.binding.value.ValueModel#getValue()
069         */
070        public Object getValue() {
071            return currentSelection;
072        }
073    
074        /**
075         * Set the selection value.
076         * 
077         * @param newValue must be an integer array (int[])
078         */
079        public void setValue( Object newValue ) {
080            int[] newSelection = (int[]) newValue;
081    
082            if( hasChanged(currentSelection, newSelection) ) {
083    
084                int[] oldValue = currentSelection;
085                currentSelection = newSelection;
086                fireValueChange(oldValue, currentSelection);
087    
088                if( !skipSelectionModelUpdate) {
089                    // Don't want notifications while we do this
090                    model.removeListSelectionListener(this);
091    
092                    // Install the selection on the adapted model
093                    model.clearSelection();
094    
095                    int i = 0;
096                    int len = newSelection.length;
097                    while( i < len ) {
098                        int start = newSelection[i];
099                        while( i < len - 1 && newSelection[i] == newSelection[i + 1] - 1 ) {
100                            i++;
101                        }
102                        int end = newSelection[i];
103                        model.addSelectionInterval(start, end);
104                        i++;
105                    }
106    
107                    // Reinstall listener
108                    model.addListSelectionListener(this);
109                }
110            }
111        }
112    
113        /**
114         * See if two arrays are different.
115         */
116        private boolean hasChanged( int[] oldValue, int[] newValue ) {
117            if( oldValue.length == newValue.length ) {
118                for( int i = 0; i < newValue.length; i++ ) {
119                    if( oldValue[i] != newValue[i] ) {
120                        return true;
121                    }
122                }
123                return false;
124            }
125            return true;
126        }
127    
128        /**
129         * Returns the indices of all selected rows in the model.
130         * 
131         * @return an array of integers containing the indices of all selected rows,
132         *         or an empty array if no row is selected
133         */
134        private int[] getSelectedRows() {
135            int iMin = model.getMinSelectionIndex();
136            int iMax = model.getMaxSelectionIndex();
137    
138            if( (iMin == -1) || (iMax == -1) ) {
139                return new int[0];
140            }
141    
142            int[] rvTmp = new int[1 + (iMax - iMin)];
143            int n = 0;
144            for( int i = iMin; i <= iMax; i++ ) {
145                if( model.isSelectedIndex(i) ) {
146                    rvTmp[n++] = i;
147                }
148            }
149            int[] rv = new int[n];
150            System.arraycopy(rvTmp, 0, rv, 0, n);
151            return rv;
152        }
153    }