org.wings.SList Maven / Gradle / Ivy
/*
* Copyright 2000,2005 wingS development team.
*
* This file is part of wingS (http://wingsframework.org).
*
* wingS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* Please see COPYING for the complete licence.
*/
package org.wings;
import org.wings.event.SViewportChangeEvent;
import org.wings.event.SViewportChangeListener;
import org.wings.event.SMouseEvent;
import org.wings.plaf.ListCG;
import org.wings.style.CSSAttributeSet;
import org.wings.style.CSSProperty;
import org.wings.style.CSSStyleSheet;
import org.wings.style.Selector;
import org.wings.sdnd.TextAndHTMLTransferable;
import org.wings.sdnd.CustomDragHandler;
import org.wings.sdnd.SDropMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.event.EventListenerList;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.datatransfer.Transferable;
import java.util.ArrayList;
import java.util.List;
/**
* Allows the user to select one or more objects from a list.
* CAVEAT
* A list in a form has special implications to take care of:
* Problem with a form request
* is, that we should fire the selection change events not until the states
* of all components are consistent. Unfortunately we cannot avoid events
* before that
* entirely. Problem is, that we use Swing Models for selection and they
* don't know anything about asynchronous state change. They will fire their
* events just after we set a state. But inside a form we have to change
* many states of many components, all at once. And events should arise
* first, after we set the new state of all components. So as a trade-off we
* decided to use the setValueIsAdjusting in the ListSelectionModel as an
* indicator,
* if components are consistent. That is, if you get an SelectionEvent with
* getValueIsAdjusting true, you cannot be sure, that component states are
* consistent, so don't use that events. But you will get an event without
* isValueAdjusting. You can work with that event. If you want to avoid that
* problem, just use the selection events from the list itself, register your
* listener at SList rather than at the ListSelectionModel...
*
* @author Holger Engels
* @author Armin Haaf
* @see javax.swing.ListModel
* @see SDefaultListModel
* @see javax.swing.ListSelectionModel
* @see SListCellRenderer
*/
public class SList extends SComponent implements Scrollable, LowLevelEventListener, ListDataListener {
private static final Logger LOG = LoggerFactory.getLogger(SList.class);
/**
* The type for an ordered list. See {@link #setType(String)} and ORDER_TYPE_xxx
*/
public static final String ORDERED_LIST = "ol";
/**
* The type for an unordered list. See {@link #setType(String)}
*/
public static final String UNORDERED_LIST = "ul";
/**
* The type for an menu-like list. See {@link #setType(String)}
*/
public static final String MENU_LIST = "menu";
/**
* The type for an TO-DO list. See {@link #setType(String)}
*/
public static final String DIR_LIST = "dir";
/**
* Order type for for {@link #setOrderType(String)}
*/
public static final String[] ORDER_TYPE_CIRCLE = {"ul", "circle"};
/**
* Order type for for {@link #setOrderType(String)}
*/
public static final String[] ORDER_TYPE_SQUARE = {"ul", "square"};
/**
* Order type for for {@link #setOrderType(String)}
*/
public static final String[] ORDER_TYPE_DISC = {"ul", "disc"};
/**
* Order type for for {@link #setOrderType(String)}
*/
public static final String[] ORDER_TYPE_BIG_ALPHA = {"ol", "A"};
/**
* Order type for for {@link #setOrderType(String)}
*/
public static final String[] ORDER_TYPE_SMALL_ALPHA = {"ol", "a"};
/**
* Order type for for {@link #setOrderType(String)}
*/
public static final String[] ORDER_TYPE_NUMBER = {"ol", null};
/**
* Order type for for {@link #setOrderType(String)}
*/
public static final String[] ORDER_TYPE_NORMAL = {"ul", null};
/**
* Order type for for {@link #setOrderType(String)}
*/
public static final String[] ORDER_TYPE_BIG_ROMAN = {"ol", "I"};
/**
* Order type for for {@link #setOrderType(String)}
*/
public static final String[] ORDER_TYPE_SMALL_ROMAN = {"ol", "i"};
/**
* Table selection model. See {@link SList#setSelectionMode(int)}
*/
public static final int NO_SELECTION = SListSelectionModel.NO_SELECTION;
/**
* Table selection model. See {@link SList#setSelectionMode(int)}
*/
public static final int SINGLE_SELECTION = SListSelectionModel.SINGLE_SELECTION;
/**
* Table selection model. See {@link SList#setSelectionMode(int)}
*/
public static final int SINGLE_INTERVAL_SELECTION = SListSelectionModel.SINGLE_INTERVAL_SELECTION;
/**
* Table selection model. See {@link SList#setSelectionMode(int)}
*/
public static final int MULTIPLE_SELECTION = SListSelectionModel.MULTIPLE_INTERVAL_SELECTION;
/**
* Table selection model. See {@link SList#setSelectionMode(int)}
*/
public static final int MULTIPLE_INTERVAL_SELECTION = SListSelectionModel.MULTIPLE_INTERVAL_SELECTION;
/**
* The Selector for this component.
*/
public static final Selector SELECTOR_SELECTION = new Selector("SELECTION");
/**
* The preferred extent of the list.
*/
private int visibleRowCount = 8;
private SListSelectionModel selectionModel;
private ListModel dataModel;
private SListCellRenderer cellRenderer;
/**
* Implementation of the {@link Scrollable} interface.
*/
protected Rectangle viewport;
/**
* @see LowLevelEventListener#isEpochCheckEnabled()
*/
private boolean epochCheckEnabled = true;
/**
*
*/
protected String type = UNORDERED_LIST;
/**
*
*/
protected String orderType = null;
/**
*
*/
protected int start = 0;
/**
* used to forward selection events to selection listeners of the list
*/
private final ListSelectionListener fwdSelectionEvents = e -> {
fireSelectionValueChanged(e.getFirstIndex(), e.getLastIndex(), e.getValueIsAdjusting());
if (isUpdatePossible() && SList.class.isAssignableFrom(SList.this.getClass())) {
List deselectedIndices = new ArrayList();
List selectedIndices = new ArrayList();
for (int index = e.getFirstIndex(); index <= e.getLastIndex(); ++index) {
int visibleIndex = index;
if (viewport != null) {
visibleIndex = index - viewport.y;
if (visibleIndex < 0 || visibleIndex >= viewport.height)
continue;
}
//LKoller - fboss [15.4.09]: Added index range check with model to prevent illegal or outdated indices,
// that will result in an error message on the webpage.
if (visibleIndex < dataModel.getSize()) {
if (isSelectedIndex(index)) {
selectedIndices.add(visibleIndex);
} else {
deselectedIndices.add(visibleIndex);
}
}
}
update(((ListCG) getCG()).getSelectionUpdate(SList.this, deselectedIndices, selectedIndices));
} else {
reload();
}
};
/**
* Construct a SList that displays the elements in the specified model.
*/
public SList(ListModel dataModel) {
if (dataModel == null) {
throw new IllegalArgumentException("dataModel must not be null");
}
if (this.dataModel != null) this.dataModel.removeListDataListener(this);
this.dataModel = dataModel;
this.dataModel.addListDataListener(this);
setSelectionModel(createSelectionModel());
installTransferHandler();
createActionMap();
}
/**
* Construct a SList that displays the elements in the specified
* array.
*/
public SList(final Object... listData) {
this(new AbstractListModel() {
@Override
public int getSize() {
return listData.length;
}
@Override
public Object getElementAt(int i) {
return listData[i];
}
});
}
/**
* Construct a SList that displays the elements in the specified
* Vector.
*/
public SList(final List listData) {
this(new AbstractListModel() {
@Override
public int getSize() {
return listData.size();
}
@Override
public Object getElementAt(int i) {
return listData.get(i);
}
});
}
/**
* Constructs a SList with an empty model.
*/
public SList() {
this(new AbstractListModel() {
@Override
public int getSize() {
return 0;
}
@Override
public Object getElementAt(int i) {
return "No Data Model";
}
});
}
/**
* Returns the cell renderer.
*
* @return the ListCellRenderer
* @see #setCellRenderer
*/
public final SListCellRenderer getCellRenderer() {
return cellRenderer;
}
/**
* Sets the renderer that's used to write out each cell in the list.
*
* @param cellRenderer the SListCellRenderer that paints list cells
* description: The component used to draw the cells.
* @see #getCellRenderer
*/
public void setCellRenderer(SListCellRenderer cellRenderer) {
SListCellRenderer oldValue = this.cellRenderer;
this.cellRenderer = cellRenderer;
reloadIfChange(oldValue, cellRenderer);
propertyChangeSupport.firePropertyChange("cellRenderer", oldValue, this.cellRenderer);
}
/**
* Return the background color.
*
* @return the background color
*/
public Color getSelectionBackground() {
return dynamicStyles == null || dynamicStyles.get(SELECTOR_SELECTION) == null ? null : CSSStyleSheet.getBackground((CSSAttributeSet) dynamicStyles.get(SELECTOR_SELECTION));
}
/**
* Set the foreground color.
*
* @param color the new foreground color
*/
public void setSelectionBackground(Color color) {
Color oldVal = this.getSelectionBackground();
setAttribute(SELECTOR_SELECTION, CSSProperty.BACKGROUND_COLOR, CSSStyleSheet.getAttribute(color));
propertyChangeSupport.firePropertyChange("selectionBackground", oldVal, this.getSelectionBackground());
}
/**
* Return the foreground color.
*
* @return the foreground color
*/
public Color getSelectionForeground() {
return dynamicStyles == null || dynamicStyles.get(SELECTOR_SELECTION) == null ? null : CSSStyleSheet.getForeground((CSSAttributeSet) dynamicStyles.get(SELECTOR_SELECTION));
}
/**
* Set the foreground color.
*
* @param color the new foreground color
*/
public void setSelectionForeground(Color color) {
Color oldVal = this.getSelectionForeground();
setAttribute(SELECTOR_SELECTION, CSSProperty.COLOR, CSSStyleSheet.getAttribute(color));
propertyChangeSupport.firePropertyChange("selectionForeground", oldVal, this.getSelectionForeground());
}
/**
* Set the font.
*
* @param font the new font
*/
public void setSelectionFont(SFont font) {
SFont oldVal = this.getSelectionFont();
setAttributes(SELECTOR_SELECTION, CSSStyleSheet.getAttributes(font));
propertyChangeSupport.firePropertyChange("selectionFont", oldVal, this.getSelectionFont());
}
/**
* Return the font.
*
* @return the font
*/
public SFont getSelectionFont() {
return dynamicStyles == null || dynamicStyles.get(SELECTOR_SELECTION) == null ? null : CSSStyleSheet.getFont((CSSAttributeSet) dynamicStyles.get(SELECTOR_SELECTION));
}
/**
* Return the preferred number of visible rows. If rendered as a form
* component it is used for the size-attribute.
*
* @return the preferred number of rows to display
* @see #setVisibleRowCount
*/
public final int getVisibleRowCount() {
return visibleRowCount;
}
/**
* Set the preferred number of rows in the list that can be displayed
* without a scollbar.
*
* The default value of this property is 8.
*
* @param visibleRowCount the preferred number of visible rows
* description: The preferred number of cells that can be displayed without a scrollbar.
* @see #getVisibleRowCount
*/
public void setVisibleRowCount(int visibleRowCount) {
if (this.visibleRowCount != visibleRowCount) {
int oldVal = this.visibleRowCount;
this.visibleRowCount = Math.max(0, visibleRowCount);
reload();
propertyChangeSupport.firePropertyChange("visibleRowCount", oldVal, this.visibleRowCount);
}
}
/**
* --- ListModel Support ---
*/
/**
* Returns the data model that holds the items.
*
* @return the ListModel
* @see #setModel
*/
public ListModel getModel() {
return dataModel;
}
/**
* Sets the model
*
* @param model the ListModel that provides the list of items
* description: The object that contains the data to be shownin the list.
* @see #getModel
*/
public void setModel(ListModel model) {
if (model == null) {
throw new IllegalArgumentException("model must be non null");
}
if (isDifferent(dataModel, model)) {
ListModel oldVal = this.dataModel;
clearSelection();
dataModel = model;
dataModel.addListDataListener(this);
fireViewportChanged(false);
reload();
propertyChangeSupport.firePropertyChange("model", oldVal, this.dataModel);
}
}
/**
* A convenience method that constructs a ListModel from an array of Objects
* and then applies setModel to it.
*
* @param listData an array of Objects containing the items to display
* in the list
* @see #setModel
*/
public void setListData(final Object... listData) {
setModel(new AbstractListModel() {
@Override
public int getSize() {
return listData.length;
}
@Override
public Object getElementAt(int i) {
return listData[i];
}
});
}
/**
* A convenience method that constructs a ListModel from a List
* and then applies setModel to it.
*
* @param listData a Vector containing the items to display in the list
* @see #setModel
*/
public void setListData(final List listData) {
setModel(new MyAbstractListModel(listData));
}
/**
* creates the default selection model. It uses the swing
* DefaultListSelectionModel, and wraps some methods to support
* {@link SListSelectionModel#NO_SELECTION}
*/
protected static SListSelectionModel createSelectionModel() {
return new SDefaultListSelectionModel();
}
/**
* Returns the current selection model. If selection mode is
* {@link SListSelectionModel#NO_SELECTION} it return null
*
* @return the ListSelectionModel that implements list selections.
* If selection mode is {@link SListSelectionModel#NO_SELECTION} it return
* null
* @see #setSelectionMode(int)
* @see ListSelectionModel
*/
public SListSelectionModel getSelectionModel() {
return selectionModel;
}
/**
* This method notifies all ListSelectionListeners that
* the selection model has changed.
*
* @see #addListSelectionListener
* @see #removeListSelectionListener
* @see EventListenerList
*/
protected void fireSelectionValueChanged(int firstIndex, int lastIndex,
boolean isAdjusting) {
Object[] listeners = getListenerList();
ListSelectionEvent e = null;
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ListSelectionListener.class) {
if (e == null) {
e = new ListSelectionEvent(this, firstIndex, lastIndex,
isAdjusting);
}
((ListSelectionListener) listeners[i + 1]).valueChanged(e);
}
}
}
/**
* Add a listener to the list that's notified each time a change
* to the selection occurs.
*
*
* If you want to receive immedate an event when the user clicks a new item
* on the client side you have to register additionally a Java script listener
* which triggers a form submit.
* combobox.addScriptListener(ListCG.JS_ON_CHANGE_SUBMIT)
*
* @param listener A ListSelectionListener to be added
* @see #getSelectionModel
*/
public void addListSelectionListener(ListSelectionListener listener) {
addEventListener(ListSelectionListener.class, listener);
}
/**
* Remove a listener from the list that's notified each time a
* change to the selection occurs.
*
* @param listener The ListSelectionListener to remove.
* @see #addListSelectionListener
* @see #getSelectionModel
*/
public void removeListSelectionListener(ListSelectionListener listener) {
removeEventListener(ListSelectionListener.class, listener);
}
/**
* Returns an array of all the ListSelectionListener
s added
* to this JList with addListSelectionListener().
*
* @return all of the ListSelectionListener added
* @since 1.4
*/
public ListSelectionListener[] getListSelectionListeners() {
return (ListSelectionListener[]) getListeners(ListSelectionListener.class);
}
/**
* Set the selectionModel for the list.
* The selection model keeps track of which items are selected.
*
* description: The selection model, recording which cells are selected.
*
* @see #getSelectionModel
*/
public void setSelectionModel(SListSelectionModel selectionModel) {
if (selectionModel == null) {
throw new IllegalArgumentException("selectionModel must be non null");
}
SListSelectionModel oldVal = this.selectionModel;
if (this.selectionModel != null)
this.selectionModel.removeListSelectionListener(fwdSelectionEvents);
selectionModel.addListSelectionListener(fwdSelectionEvents);
this.selectionModel = selectionModel;
propertyChangeSupport.firePropertyChange("selectionModel", oldVal, this.selectionModel);
}
/**
* Allow / permit multiple selection
*
* -
SINGLE_SELECTION
* Only one list index can be selected at a time.
* -
MULTIPLE_INTERVAL_SELECTION
* Multiple items can be selected.
*
*
* @param selectionMode single or multiple selections
* enum: SINGLE_SELECTION ListSelectionModel.SINGLE_SELECTION
* MULTIPLE_INTERVAL_SELECTION ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
* @see #getSelectionMode
*/
public void setSelectionMode(int selectionMode) {
int oldVal = selectionModel.getSelectionMode();
selectionModel.setSelectionMode(selectionMode);
propertyChangeSupport.firePropertyChange("selectionMode", oldVal, selectionModel.getSelectionMode());
}
/**
* Returns whether single-item or multiple-item selections are allowed.
*
* @return The value of the selectionMode property.
* @see #setSelectionMode
*/
public int getSelectionMode() {
return selectionModel.getSelectionMode();
}
/**
* @return The index that most recently anchored an interval selection.
* @see ListSelectionModel#getAnchorSelectionIndex
* @see #addSelectionInterval
* @see #setSelectionInterval
* @see #addListSelectionListener
*/
public int getAnchorSelectionIndex() {
return selectionModel.getAnchorSelectionIndex();
}
/**
* @return The index that most recently ended a interval selection.
* @see ListSelectionModel#getLeadSelectionIndex
* @see #addSelectionInterval
* @see #setSelectionInterval
* @see #addListSelectionListener
*/
public int getLeadSelectionIndex() {
return selectionModel.getLeadSelectionIndex();
}
/**
* @return The smallest selected cell index.
* @see ListSelectionModel#getMinSelectionIndex
* @see #addListSelectionListener
*/
public int getMinSelectionIndex() {
return selectionModel.getMinSelectionIndex();
}
/**
* @return The largest selected cell index.
* @see ListSelectionModel#getMaxSelectionIndex
* @see #addListSelectionListener
*/
public int getMaxSelectionIndex() {
return selectionModel.getMaxSelectionIndex();
}
/**
* @return True if the specified index is selected.
* @see ListSelectionModel#isSelectedIndex
* @see #setSelectedIndex
* @see #addListSelectionListener
*/
public boolean isSelectedIndex(int index) {
return selectionModel.isSelectedIndex(index);
}
/**
* @return True if nothing is selected
* @see ListSelectionModel#isSelectionEmpty
* @see #clearSelection
* @see #addListSelectionListener
*/
public boolean isSelectionEmpty() {
return selectionModel.isSelectionEmpty();
}
/**
* @see ListSelectionModel#clearSelection
* @see #isSelectionEmpty
* @see #addListSelectionListener
*/
public void clearSelection() {
if (!selectionModel.isSelectionEmpty()) {
selectionModel.clearSelection();
reload();
}
}
/**
* @param anchor The first index to select
* @param lead The last index to select
* @see ListSelectionModel#setSelectionInterval
* @see #addSelectionInterval
* @see #removeSelectionInterval
* @see #addListSelectionListener
*/
public void setSelectionInterval(int anchor, int lead) {
int[] oldVal = {this.getAnchorSelectionIndex(), this.getLeadSelectionIndex()};
selectionModel.setSelectionInterval(anchor, lead);
int[] newVal = {this.getAnchorSelectionIndex(), this.getLeadSelectionIndex()};
propertyChangeSupport.firePropertyChange("selectionInterval", oldVal, newVal);
}
/**
* @param anchor The first index to add to the selection
* @param lead The last index to add to the selection
* @see ListSelectionModel#addSelectionInterval
* @see #setSelectionInterval
* @see #removeSelectionInterval
* @see #addListSelectionListener
*/
public void addSelectionInterval(int anchor, int lead) {
selectionModel.addSelectionInterval(anchor, lead);
}
/**
* @param index0 The first index to remove from the selection
* @param index1 The last index to remove from the selection
* @see ListSelectionModel#removeSelectionInterval
* @see #setSelectionInterval
* @see #addSelectionInterval
* @see #addListSelectionListener
*/
public void removeSelectionInterval(int index0, int index1) {
selectionModel.removeSelectionInterval(index0, index1);
}
/**
* @param b the value for valueIsAdjusting
* @see ListSelectionModel#setValueIsAdjusting
*/
public void setValueIsAdjusting(boolean b) {
boolean oldVal = selectionModel.getValueIsAdjusting();
selectionModel.setValueIsAdjusting(b);
boolean newVal = selectionModel.getValueIsAdjusting();
propertyChangeSupport.firePropertyChange("valueIsAdjusting", oldVal, newVal);
}
/**
* @return the value of valueIsAdjusting
* @see ListSelectionModel#getValueIsAdjusting
*/
public boolean getValueIsAdjusting() {
return selectionModel.getValueIsAdjusting();
}
/**
* Return an array of all of the selected indices.
*
* @return all selected indices.
* @see #removeSelectionInterval
* @see #addListSelectionListener
*/
public int[] getSelectedIndices() {
ListSelectionModel sm = selectionModel;
int iMin = sm.getMinSelectionIndex();
int iMax = sm.getMaxSelectionIndex();
if ((iMin < 0) || (iMax < 0)) {
return new int[0];
}
int[] rvTmp = new int[1 + (iMax - iMin)];
int n = 0;
for (int i = iMin; i <= iMax; i++) {
if (sm.isSelectedIndex(i)) {
rvTmp[n++] = i;
}
}
int[] rv = new int[n];
System.arraycopy(rvTmp, 0, rv, 0, n);
return rv;
}
/**
* Select a single cell.
*
* @param index The index of the one cell to select
* @see ListSelectionModel#setSelectionInterval
* @see #isSelectedIndex
* @see #addListSelectionListener
*/
public void setSelectedIndex(int index) {
int oldVal = this.getSelectedIndex();
selectionModel.setSelectionInterval(index, index);
propertyChangeSupport.firePropertyChange("selectedIndex", oldVal, this.getSelectedIndex());
}
/**
* Select some cells.
*
* @param indices The indices of the cells to select
* @see ListSelectionModel#addSelectionInterval
* @see #isSelectedIndex
* @see #addListSelectionListener
*/
public void setSelectedIndices(int... indices) {
ListSelectionModel sm = selectionModel;
int[] oldVal = this.getSelectedIndices();
sm.clearSelection();
for (int indice : indices) {
sm.addSelectionInterval(indice, indice);
}
propertyChangeSupport.firePropertyChange("selectedIndices", oldVal, this.getSelectedIndices());
}
/**
* Return the values of the selected cells.
* Returns only the selected elements which are in the model.
* If the selection model indices a selection outside the the datamodel it is ignored
*
* @return the selected values
* @see #isSelectedIndex
* @see #getModel
* @see #addListSelectionListener
*/
public Object[] getSelectedValues() {
ListSelectionModel sm = selectionModel;
ListModel dm = dataModel;
int iMin = sm.getMinSelectionIndex();
int iMax = sm.getMaxSelectionIndex();
if ((iMin < 0) || (iMax < 0)) {
return new Object[0];
}
Object[] rvTmp = new Object[1 + (iMax - iMin)];
int n = 0;
for (int i = iMin; i <= iMax; i++) {
if (sm.isSelectedIndex(i) && i < dm.getSize()) {
rvTmp[n++] = dm.getElementAt(i);
}
}
Object[] rv = new Object[n];
System.arraycopy(rvTmp, 0, rv, 0, n);
return rv;
}
/**
* A convenience method that returns the first selected index.
*
* @return The first selected index.
* @see #getMinSelectionIndex
* @see #addListSelectionListener
*/
public int getSelectedIndex() {
return getMinSelectionIndex();
}
/**
* A convenience method that returns the first selected value
* or null, if nothing is selected.
*
* @return The first selected value.
* @see #getMinSelectionIndex
* @see #getModel
* @see #addListSelectionListener
*/
public Object getSelectedValue() {
int i = getMinSelectionIndex();
return (i == -1) ? null : dataModel.getElementAt(i);
}
/**
* Selects the specified object.
*
* @param anObject the Object to be selected
*/
public void setSelectedValue(Object anObject) {
if (anObject == null)
setSelectedIndex(-1);
else if (!anObject.equals(getSelectedValue())) {
int i, c;
ListModel dm = dataModel;
for (i = 0, c = dm.getSize(); i < c; i++)
if (anObject.equals(dm.getElementAt(i))) {
setSelectedIndex(i);
return;
}
setSelectedIndex(-1);
}
}
/*
* Sets the list type. Use one of the following types:
*
* - {@link SConstants#ORDERED_LIST}
*
- {@link SConstants#UNORDERED_LIST}
*
- {@link SConstants#MENU_LIST}
*
- {@link SConstants#DIR_LIST}
*
* null sets default list.
*
* @param t the type
*/
public void setType(String t) {
String oldVal = this.type;
if (t != null)
type = t;
else
type = UNORDERED_LIST;
propertyChangeSupport.firePropertyChange("type", oldVal, this.type);
}
/**
* Return the type.
*
* @return the type;
*/
public String getType() {
return type;
}
/**
*
*/
public void setOrderType(String t) {
String oldVal = this.orderType;
orderType = t;
propertyChangeSupport.firePropertyChange("orderType", oldVal, this.orderType);
}
/**
*
*/
public String getOrderType() {
return orderType;
}
/*
*
* null
is default style.
*/
public void setType(String... t) {
if (t == null) {
setType((String) null);
setOrderType(null);
} else if (t.length == 2) {
setType(t[0]);
setOrderType(t[1]);
}
}
/**
*
*/
public void setStart(int s) {
int oldVal = this.start;
start = s;
propertyChangeSupport.firePropertyChange("start", oldVal, this.start);
}
/**
*
*/
public int getStart() {
return start;
}
@Override
public void fireIntermediateEvents() {
selectionModel.fireDelayedIntermediateEvents();
}
@Override
public void fireFinalEvents() {
super.fireFinalEvents();
// fire selection events...
selectionModel.fireDelayedFinalEvents();
}
/**
* @see LowLevelEventListener#isEpochCheckEnabled()
*/
@Override
public boolean isEpochCheckEnabled() {
return epochCheckEnabled;
}
/**
* @see LowLevelEventListener#isEpochCheckEnabled()
*/
public void setEpochCheckEnabled(boolean epochCheckEnabled) {
boolean oldVal = this.epochCheckEnabled;
this.epochCheckEnabled = epochCheckEnabled;
propertyChangeSupport.firePropertyChange("epochCheckEnabled", oldVal, this.epochCheckEnabled);
}
private int lastSelectedIndex;
protected void addSelectionEvent(int index, boolean ctrlKey, boolean shiftKey) {
if(index != -1) {
if(shiftKey == false && ctrlKey == false) {
setSelectionInterval(index, index);
lastSelectedIndex = index;
} else if(ctrlKey == true && shiftKey == false) {
if(!isSelectedIndex(index))
addSelectionInterval(index, index);
else
removeSelectionInterval(index, index);
lastSelectedIndex = index;
} else if(ctrlKey == false && shiftKey == true) {
setSelectionInterval(lastSelectedIndex, index);
}
}
}
/*
* Implement {@link LowLevelEventListener} interface.
* @param action the name
* @param value the value
*/
@Override
public void processLowLevelEvent(String action, String... values) {
processKeyEvents(values);
if (action.endsWith("_keystroke"))
return;
// delay events...
selectionModel.setDelayEvents(true);
selectionModel.setValueIsAdjusting(true);
// in a form, we only get events for selected items, so for every
// selected item, which is not in values, deselect it...
if (getShowAsFormComponent()) {
ArrayList selectedIndices = new ArrayList();
for (String indexString : values) {
if (indexString.length() < 1) continue; // false format
try {
int index = Integer.parseInt(indexString);
// in a form all parameters are select parameters...
selectedIndices.add(index);
addSelectionInterval(index, index);
} catch (Exception ex) {
}
}
// remove all selected indices, which are not explicitely selected by a parameter
for (int i = 0; i < dataModel.getSize(); ++i) {
if (isSelectedIndex(i) && !selectedIndices.contains(i)) {
removeSelectionInterval(i, i);
}
}
} else {
int index = -1;
for (String value : values) {
String[] paramVals = value.split(";");
boolean shiftKey = false;
boolean ctrlKey = false;
for (String indexString : paramVals) {
if (indexString.length() < 1) continue; // false format
if (indexString.startsWith("ctrlKey=")) {
ctrlKey = Boolean.parseBoolean(indexString.substring(indexString.indexOf('=') + 1));
continue;
}
if (indexString.startsWith("shiftKey=")) {
shiftKey = Boolean.parseBoolean(indexString.substring(indexString.indexOf("=") + 1));
continue;
}
try {
index = Integer.parseInt(indexString);
// toggle selection for given index
} catch (Exception ex) {
}
}
addSelectionEvent(index, ctrlKey, shiftKey);
}
}
selectionModel.setValueIsAdjusting(false);
selectionModel.setDelayEvents(false);
SForm.addArmedComponent(this);
}
/**
* The size of the component in respect to scrollable units.
*/
@Override
public Rectangle getScrollableViewportSize() {
return new Rectangle(0, 0, 1, dataModel.getSize());
}
/**
* Returns the actual visible part of a scrollable.
*/
@Override
public Rectangle getViewportSize() {
return viewport;
}
/**
* Sets the actual visible part of a scrollable.
*/
@Override
public void setViewportSize(Rectangle newViewport) {
Rectangle oldViewport = viewport;
viewport = newViewport;
if (isDifferent(oldViewport, newViewport)) {
if (oldViewport == null || newViewport == null) {
fireViewportChanged(true);
fireViewportChanged(false);
} else {
if (newViewport.x != oldViewport.x || newViewport.width != oldViewport.width) {
fireViewportChanged(true);
}
if (newViewport.y != oldViewport.y || newViewport.height != oldViewport.height) {
fireViewportChanged(false);
}
}
reload();
}
propertyChangeSupport.firePropertyChange("biewPortSize", oldViewport, this.viewport);
}
/**
* Adds the given SViewportChangeListener
to the scrollable.
*
* @param l the listener to be added
*/
@Override
public void addViewportChangeListener(SViewportChangeListener l) {
addEventListener(SViewportChangeListener.class, l);
}
/**
* Removes the given SViewportChangeListener
from the scrollable.
*
* @param l the listener to be removed
*/
@Override
public void removeViewportChangeListener(SViewportChangeListener l) {
removeEventListener(SViewportChangeListener.class, l);
}
/**
* Notifies all listeners that have registered interest for notification
* on changes to this scrollable's viewport in the specified direction.
*
* @see EventListenerList
*/
protected void fireViewportChanged(boolean horizontal) {
Object[] listeners = getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == SViewportChangeListener.class) {
SViewportChangeEvent event = new SViewportChangeEvent(this, horizontal);
((SViewportChangeListener) listeners[i + 1]).viewportChanged(event);
}
}
}
@Override
public void setParent(SContainer p) {
super.setParent(p);
if (cellRendererPane != null) {
cellRendererPane.setParent(p);
}
}
@Override
protected void setParentFrame(SFrame f) {
super.setParentFrame(f);
if (cellRendererPane != null) {
cellRendererPane.setParentFrame(f);
}
}
// do not initalize with null!
private SCellRendererPane cellRendererPane = new SCellRendererPane();
public SCellRendererPane getCellRendererPane() {
return cellRendererPane;
}
public void removeCellRendererPane() {
cellRendererPane.setParent(null);
cellRendererPane = null;
}
public void setCG(ListCG cg) {
super.setCG(cg);
}
public String getToggleSelectionParameter(int index) {
return isSelectedIndex(index) ? getDeselectionParameter(index) :
getSelectionParameter(index);
}
public static String getSelectionParameter(int index) {
return Integer.toString(index);
}
public static String getDeselectionParameter(int index) {
return Integer.toString(index);
}
// Changes to the model should force a reload.
@Override
public void contentsChanged(javax.swing.event.ListDataEvent e) {
fireViewportChanged(false);
reload();
}
@Override
public void intervalAdded(javax.swing.event.ListDataEvent e) {
fireViewportChanged(false);
clearSelection();
reload();
}
@Override
public void intervalRemoved(javax.swing.event.ListDataEvent e) {
fireViewportChanged(false);
clearSelection();
reload();
}
/**
* Drag and Drop stuff
*/
private SDropMode dropMode = null;
private boolean dragEnabled = false;
protected void createActionMap() {
ActionMap map = getActionMap();
map.put(STransferHandler.getCutAction().getValue(Action.NAME), STransferHandler.getCutAction());
map.put(STransferHandler.getCopyAction().getValue(Action.NAME), STransferHandler.getCopyAction());
map.put(STransferHandler.getPasteAction().getValue(Action.NAME), STransferHandler.getPasteAction());
}
public static final class DropLocation extends STransferHandler.DropLocation {
private int index;
public DropLocation(SList list, SPoint point) {
super(point);
try {
index = Integer.parseInt(point.getCoordinates());
Rectangle currentViewport = list.getViewportSize();
if(currentViewport != null)
index += currentViewport.y;
} catch(Exception e) {
index = 0;
}
}
public int getIndex() {
return index;
}
}
public void setDropMode(SDropMode dropMode) {
this.dropMode = dropMode;
getSession().getSDragAndDropManager().addDropTarget(this);
}
public SDropMode getDropMode() {
return this.dropMode;
}
@Override
protected DropLocation dropLocationForPoint(SPoint p) {
if(p.getCoordinates() == null)
return null;
return new SList.DropLocation(this, p);
}
private void installTransferHandler() {
if(getTransferHandler() == null) {
setTransferHandler(new DefaultTransferHandler());
}
}
public void setDragEnabled(boolean dragEnabled) {
if(selectionModel == null && dragEnabled == true)
throw new IllegalStateException("Unable to enable DND - no selection mode set in " + this);
if(getShowAsFormComponent() && dragEnabled == true) {
LOG.warn("NOTE: setDragEnabled(true) called when getShowAsFormComponent was false - dragging won't work in internet explorer");
return;
}
if(dragEnabled != this.dragEnabled) {
if(dragEnabled) {
this.getSession().getSDragAndDropManager().addDragSource(this);
} else {
this.getSession().getSDragAndDropManager().removeDragSource(this);
}
this.dragEnabled = dragEnabled;
}
}
public static class DefaultTransferHandler extends STransferHandler implements CustomDragHandler {
public DefaultTransferHandler() {
super(null);
}
@Override
protected Transferable createTransferable(SComponent component) {
SList list = (SList)component;
String htmlData = "";
String plainTextData = "";
for(Object obj:list.getSelectedValues()) {
plainTextData += obj.toString() + '\n';
htmlData += "- " + obj.toString() + "
";
}
htmlData += "
";
return new TextAndHTMLTransferable(plainTextData, htmlData);
}
@Override
public int getSourceActions(SComponent component) {
return COPY;
}
@Override
public boolean dragStart(SComponent source, SComponent target, int action, SMouseEvent event) {
try {
String[] coords = event.getPoint().getCoordinates().split(":");
int index = Integer.parseInt(coords[0]);
if(coords.length < 3)
return false;
boolean ctrlKey = false;
boolean shiftKey = false;
for(int i=1; i