/**
* Copyright (C) 2015 Valkyrie RCP
*
* 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 org.valkyriercp.list;
import org.valkyriercp.binding.value.support.AbstractValueModel;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
* Class to adapt the selection model of a list into a value model. This allows
* it to be used in conjunction with various Guard implementations.
*
* @author Larry Streepy
* @see ListSingleSelectionGuard
* @see ListMultipleSelectionGuard
*/
public class ListSelectionValueModelAdapter extends AbstractValueModel implements ListSelectionListener {
private ListSelectionModel model;
private int[] currentSelection = new int[0];
private boolean skipSelectionModelUpdate = false;
/**
* Constructor.
*
* @param model selection model to adapt
*/
public ListSelectionValueModelAdapter( ListSelectionModel model ) {
this.model = model;
this.model.addListSelectionListener(this);
}
/*
* (non-Javadoc)
*
* @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
*/
public void valueChanged( ListSelectionEvent e ) {
if( !e.getValueIsAdjusting() ) {
// We need to install this new value into the value model, but
// we don't want to propogate it back down to the adapted selection
// model (since we are responding to a change in that model).
skipSelectionModelUpdate = true;
setValue(getSelectedRows());
skipSelectionModelUpdate = false;
}
}
/*
* (non-Javadoc)
*
* @see org.springframework.binding.value.ValueModel#getValue()
*/
public Object getValue() {
return currentSelection;
}
/**
* Set the selection value.
*
* @param newValue must be an integer array (int[])
*/
public void setValue( Object newValue ) {
int[] newSelection = (int[]) newValue;
if( hasChanged(currentSelection, newSelection) ) {
int[] oldValue = currentSelection;
currentSelection = newSelection;
fireValueChange(oldValue, currentSelection);
if( !skipSelectionModelUpdate) {
// Don't want notifications while we do this
model.removeListSelectionListener(this);
// Install the selection on the adapted model
model.clearSelection();
int i = 0;
int len = newSelection.length;
while( i < len ) {
int start = newSelection[i];
while( i < len - 1 && newSelection[i] == newSelection[i + 1] - 1 ) {
i++;
}
int end = newSelection[i];
model.addSelectionInterval(start, end);
i++;
}
// Reinstall listener
model.addListSelectionListener(this);
}
}
}
/**
* See if two arrays are different.
*/
private boolean hasChanged( int[] oldValue, int[] newValue ) {
if( oldValue.length == newValue.length ) {
for( int i = 0; i < newValue.length; i++ ) {
if( oldValue[i] != newValue[i] ) {
return true;
}
}
return false;
}
return true;
}
/**
* Returns the indices of all selected rows in the model.
*
* @return an array of integers containing the indices of all selected rows,
* or an empty array if no row is selected
*/
private int[] getSelectedRows() {
int iMin = model.getMinSelectionIndex();
int iMax = model.getMaxSelectionIndex();
if( (iMin == -1) || (iMax == -1) ) {
return new int[0];
}
int[] rvTmp = new int[1 + (iMax - iMin)];
int n = 0;
for( int i = iMin; i <= iMax; i++ ) {
if( model.isSelectedIndex(i) ) {
rvTmp[n++] = i;
}
}
int[] rv = new int[n];
System.arraycopy(rvTmp, 0, rv, 0, n);
return rv;
}
}