/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.bearsoft.gui.grid.columns;
import com.bearsoft.gui.grid.constraints.LinearConstraint;
import com.bearsoft.gui.grid.events.constraints.ConstraintChangeListener;
import com.bearsoft.gui.grid.events.constraints.ConstraintMaximumChangedEvent;
import com.bearsoft.gui.grid.events.constraints.ConstraintMinimumChangedEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.ListSelectionModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
/**
* This column model implementation intended to constraint delegate columns
* space. It raises events from itself and from delegate model as if they
* appeared straight in this model.
*
* @author Gala
*/
public class ConstrainedColumnModel implements TableColumnModel, ListSelectionListener {
protected TableColumnModel delegate;
protected int columnMargin;
protected boolean columnSelectionAllowed;
protected LinearConstraint constraint;
protected ListSelectionModel selectionModel;
protected Set<TableColumnModelListener> listeners = new HashSet<>();
protected class ConstraintListener implements ConstraintChangeListener {
@Override
public void constraintMinimumChanged(ConstraintMinimumChangedEvent anEvent) {
if (anEvent.getOldValue() != anEvent.getNewValue()) {
for (int colIndex = 0; colIndex < Math.abs(anEvent.getNewValue() - anEvent.getOldValue()); colIndex++) {
if (anEvent.getOldValue() < anEvent.getNewValue()) {
fireColumnRemoved(0);
} else {
assert anEvent.getNewValue() < anEvent.getOldValue();
fireColumnAdded(0);
}
}
}
}
@Override
public void constraintMaximumChanged(ConstraintMaximumChangedEvent anEvent) {
if (anEvent.getOldValue() != anEvent.getNewValue()) {
if (anEvent.getOldValue() < anEvent.getNewValue()) {
for (int colIndex = anEvent.getOldValue() + 1; colIndex <= anEvent.getNewValue(); colIndex++) {
fireColumnAdded(colIndex);
}
} else {
assert anEvent.getNewValue() < anEvent.getOldValue();
for (int colIndex = anEvent.getOldValue(); colIndex > anEvent.getNewValue(); colIndex--) {
fireColumnRemoved(colIndex);
}
}
}
}
}
protected class DelegatedColumnModelListener implements TableColumnModelListener {
@Override
public void columnAdded(TableColumnModelEvent e) {
if (constraint.inConstraint(e.getToIndex())) {
fireColumnAdded(constraint.constraint(e.getToIndex()));
}
}
@Override
public void columnRemoved(TableColumnModelEvent e) {
if (constraint.inConstraint(e.getFromIndex())) {
fireColumnRemoved(constraint.constraint(e.getFromIndex()));
}
}
@Override
public void columnMoved(TableColumnModelEvent e) {
if (constraint.inConstraint(e.getFromIndex()) && constraint.inConstraint(e.getToIndex())) {
fireColumnMoved(constraint.constraint(e.getFromIndex()), constraint.constraint(e.getToIndex()));
} else {
if (constraint.inConstraint(e.getFromIndex())) {
fireColumnRemoved(constraint.constraint(e.getFromIndex()));
}
if (constraint.inConstraint(e.getToIndex())) {
fireColumnAdded(constraint.constraint(e.getToIndex()));
}
}
}
@Override
public void columnMarginChanged(ChangeEvent e) {
Object oSource = e.getSource();
assert oSource == delegate;
int oldMargin = columnMargin;
columnMargin = delegate.getColumnMargin();
// Swing fires columnMarginChanged event when any column's width changes
// this fact has no any reason. It seems that it's simply bad work of swing's programmers.
// So, we have no any outgo, but unconditionally fire this event too.
//if (oldMargin != columnMargin) {
fireColumnMarginChanged(columnMargin);
//}
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
fireSelectionModelChanged();
}
}
protected class ColumnsEnumerator implements Enumeration<TableColumn> {
protected int columnIndex = 0;
public ColumnsEnumerator() {
super();
}
@Override
public boolean hasMoreElements() {
return columnIndex < getColumnCount();
}
@Override
public TableColumn nextElement() {
return getColumn(columnIndex++);
}
}
/**
* ConstrainedColumnModel constructor.
*
* @param aDelegate A <code>TableColumnModel</code> instance all significant
* work is delegated to.
* @param aConstraint A <code>LinearConstraint</code> instacne defining the
* constraint.
* @see TableColumnModel
*/
public ConstrainedColumnModel(TableColumnModel aDelegate, LinearConstraint aConstraint) {
super();
delegate = aDelegate;
constraint = aConstraint;
columnSelectionAllowed = delegate.getColumnSelectionAllowed();
columnMargin = delegate.getColumnMargin();
delegate.addColumnModelListener(new DelegatedColumnModelListener());
constraint.addConstraintChangeListener(new ConstraintListener());
}
public TableColumnModel getDelegate() {
return delegate;
}
public LinearConstraint getConstraint() {
return constraint;
}
/**
* {@inheritDoc}
*/
@Override
public void addColumn(TableColumn aColumn) {
delegate.addColumn(aColumn);
// the event will raise by itself
}
/**
* {@inheritDoc}
*/
@Override
public void removeColumn(TableColumn aColumn) {
delegate.removeColumn(aColumn);
// the event will raise by itself
}
/**
* {@inheritDoc}
*/
@Override
public void moveColumn(int oldIndex, int newIndex) {
int colCount = getColumnCount();
if (oldIndex >= 0 && oldIndex < colCount
&& newIndex >= 0 && newIndex < colCount) {
int uncOldIndex = constraint.unconstraint(oldIndex);
int uncNewIndex = constraint.unconstraint(newIndex);
delegate.moveColumn(uncOldIndex, uncNewIndex);
// the event will raise by itself
}
}
/**
* {@inheritDoc}
*/
@Override
public Enumeration<TableColumn> getColumns() {
return new ColumnsEnumerator();
}
/**
* {@inheritDoc}
*/
@Override
public void setColumnMargin(int aMargin) {
columnMargin = aMargin;
delegate.setColumnMargin(aMargin);
// the event will raise by itself
}
/**
* {@inheritDoc}
*/
@Override
public int getColumnCount() {
int delegatedMaxColumn = delegate.getColumnCount() - 1;
int ajMax = Math.min(delegatedMaxColumn, constraint.getMax());
int ajMin = Math.max(0, constraint.getMin());
return ajMax - ajMin + 1;
}
/**
* {@inheritDoc}
*/
@Override
public int getColumnIndex(Object columnId) {
int delegatedColIndex = delegate.getColumnIndex(columnId);
if (constraint.inConstraint(delegatedColIndex)) {
return constraint.constraint(delegatedColIndex);
} else {
return -1;
}
}
/**
* {@inheritDoc}
*/
@Override
public TableColumn getColumn(int columnIndex) {
return delegate.getColumn(constraint.unconstraint(columnIndex));
}
/**
* {@inheritDoc}
*/
@Override
public int getColumnMargin() {
return columnMargin;
}
/**
* {@inheritDoc}
*/
@Override
public int getColumnIndexAtX(int xPosition) {
int width = 0;
for (int i = 0; i < getColumnCount(); i++) {
TableColumn col = getColumn(i);
if (xPosition >= width && xPosition < width + col.getWidth()) {
return i;
}
width += col.getWidth();
}
return -1;
}
/**
* {@inheritDoc}
*/
@Override
public int getTotalColumnWidth() {
int width = 0;
for (int i = 0; i < getColumnCount(); i++) {
TableColumn col = getColumn(i);
width += col.getWidth();
}
return width;
}
/**
* {@inheritDoc}
*/
@Override
public void setColumnSelectionAllowed(boolean aAllowed) {
columnSelectionAllowed = aAllowed;
delegate.setColumnSelectionAllowed(aAllowed);
// the event will raise by itself
}
/**
* {@inheritDoc}
*/
@Override
public boolean getColumnSelectionAllowed() {
return columnSelectionAllowed;
}
/**
* {@inheritDoc}
*/
@Override
public int[] getSelectedColumns() {
int[] selectedInDelegate = delegate.getSelectedColumns();
List<Integer> selected = new ArrayList<>();
for (int i = 0; i < selectedInDelegate.length; i++) {
if (selectedInDelegate[i] >= constraint.getMin()
&& selectedInDelegate[i] <= constraint.getMax()) {
selected.add(selectedInDelegate[i] - constraint.getMin());
}
}
int[] arrSelected = new int[selected.size()];
for (int i = 0; i < arrSelected.length; i++) {
arrSelected[i] = selected.get(i);
}
return arrSelected;
}
/**
* {@inheritDoc}
*/
@Override
public int getSelectedColumnCount() {
int[] selectedColumnsIndexes = getSelectedColumns();
if (selectedColumnsIndexes != null) {
return selectedColumnsIndexes.length;
} else {
return 0;
}
}
/**
* {@inheritDoc}
*/
@Override
public void setSelectionModel(ListSelectionModel newModel) {
if (selectionModel != null) {
selectionModel.removeListSelectionListener(this);
}
selectionModel = newModel;
selectionModel.addListSelectionListener(this);
fireSelectionModelChanged();
}
/**
* {@inheritDoc}
*/
@Override
public ListSelectionModel getSelectionModel() {
return selectionModel;
}
/**
* {@inheritDoc}
*/
@Override
public void addColumnModelListener(TableColumnModelListener l) {
listeners.add(l);
}
/**
* {@inheritDoc}
*/
@Override
public void removeColumnModelListener(TableColumnModelListener l) {
listeners.remove(l);
}
@Override
public void valueChanged(ListSelectionEvent e) {
fireColumnSelectionChanged(e);
}
protected void fireSelectionModelChanged() {
ListSelectionEvent selEvent = new ListSelectionEvent(this, 0, getColumnCount() - 1, false);
fireColumnSelectionChanged(selEvent);
}
protected void fireColumnSelectionChanged(ListSelectionEvent e) {
for(TableColumnModelListener l : listeners.toArray(new TableColumnModelListener[]{})){
l.columnSelectionChanged(e);
}
}
public void fireColumnMarginChanged(int aMargin) {
ChangeEvent event = new ChangeEvent(this);
for(TableColumnModelListener l : listeners.toArray(new TableColumnModelListener[]{})){
l.columnMarginChanged(event);
}
}
protected void fireColumnRemoved(int aPosition) {
TableColumnModelEvent event = new TableColumnModelEvent(this, aPosition, 0);
for(TableColumnModelListener l : listeners.toArray(new TableColumnModelListener[]{})){
l.columnRemoved(event);
}
}
protected void fireColumnAdded(int aPosition) {
TableColumnModelEvent event = new TableColumnModelEvent(this, 0, aPosition);
for(TableColumnModelListener l : listeners.toArray(new TableColumnModelListener[]{})){
l.columnAdded(event);
}
}
protected void fireColumnMoved(int sourceIndex, int destIndex) {
TableColumnModelEvent event = new TableColumnModelEvent(this, sourceIndex, destIndex);
for(TableColumnModelListener l : listeners.toArray(new TableColumnModelListener[]{})){
l.columnMoved(event);
}
}
}