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 }