package com.bc.ceres.swing;
import com.bc.ceres.core.Assert;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JList;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Map;
public class ListControlBar extends JToolBar {
public static final String ID_ADD = "add";
public static final String ID_REMOVE = "remove";
public static final String ID_MOVE_UP = "moveUp";
public static final String ID_MOVE_DOWN = "moveDown";
//public static final String ID_EDIT = "edit";
//public static final String ID_COPY = "copy";
private final Map<String, Action> actionMap;
private ListModelAdapter listModelAdapter;
public ListControlBar(int orientation, String... actionIds) {
super(orientation);
setFloatable(false);
if (actionIds == null || actionIds.length == 0) {
actionIds = new String[]{ID_ADD, ID_REMOVE, ID_MOVE_UP, ID_MOVE_DOWN};
}
actionMap = new HashMap<>();
for (String actionId : actionIds) {
Action action;
action = createAction(actionId);
actionMap.put(action.getValue(Action.ACTION_COMMAND_KEY).toString(), action);
add(action);
}
updateState();
}
public ListModelAdapter getListModelAdapter() {
return listModelAdapter;
}
public void setListModelAdapter(ListModelAdapter listModelAdapter) {
Assert.notNull(listModelAdapter, "listModelAdapter");
ListModelAdapter oldListModelAdapter = this.listModelAdapter;
this.listModelAdapter = listModelAdapter;
firePropertyChange("listModelAdapter", oldListModelAdapter, this.listModelAdapter);
updateState();
}
public static ListControlBar create(int orientation, JList list, ListController listController, String... actionIds) {
ListControlBar listControlBar = new ListControlBar(orientation, actionIds);
listControlBar.setListModelAdapter(new JListModelAdapter(listControlBar, list, listController));
return listControlBar;
}
public static ListControlBar create(int orientation, JTable table, ListController listController, String... actionIds) {
ListControlBar listControlBar = new ListControlBar(orientation, actionIds);
listControlBar.setListModelAdapter(new JTableModelAdapter(listControlBar, table, listController));
return listControlBar;
}
public static ListControlBar create(int orientation, Grid grid, ListController listController, String... actionIds) {
ListControlBar listControlBar = new ListControlBar(orientation, actionIds);
listControlBar.setListModelAdapter(new GridAdapter(listControlBar, grid, listController));
return listControlBar;
}
public Action getAction(String actionId) {
return actionMap.get(actionId);
}
public AbstractButton getActionComponent(String actionId) {
Action action = getAction(actionId);
return action != null ? getActionComponent(action) : null;
}
public AbstractButton getActionComponent(Action action) {
Component[] components = getComponents();
for (Component component : components) {
if (component instanceof AbstractButton) {
AbstractButton abstractButton = (AbstractButton) component;
if (abstractButton.getAction() == action) {
return abstractButton;
}
}
}
return null;
}
protected Action createAction(String actionId) {
switch (actionId) {
case ID_ADD:
return new AddAction();
case ID_REMOVE:
return new RemoveAction();
case ID_MOVE_UP:
return new MoveUpAction();
case ID_MOVE_DOWN:
return new MoveDownAction();
}
throw new IllegalArgumentException("actionId");
}
public void addRow() {
checkValidState();
if (listModelAdapter.addRow(listModelAdapter.getMaxSelectedRowIndex())) {
updateState();
}
}
public void removeSelectedRows() {
checkValidState();
if (listModelAdapter.removeRows(listModelAdapter.getSelectedRowIndices())) {
updateState();
}
}
public void moveSelectedRowUp() {
checkValidState();
if (listModelAdapter.moveRowUp(listModelAdapter.getMinSelectedRowIndex())) {
updateState();
}
}
public void moveSelectedRowDown() {
checkValidState();
if (listModelAdapter.moveRowDown(listModelAdapter.getMaxSelectedRowIndex())) {
updateState();
}
}
public void updateState() {
Action addAction = getAction(ID_ADD);
Action removeAction = getAction(ID_REMOVE);
Action moveUpAction = getAction(ID_MOVE_UP);
Action moveDownAction = getAction(ID_MOVE_DOWN);
if (listModelAdapter != null) {
int[] selectedRowIndices = listModelAdapter.getSelectedRowIndices();
int selectedRowCount = selectedRowIndices.length;
if (addAction != null) {
addAction.setEnabled(true);
}
if (removeAction != null) {
removeAction.setEnabled(selectedRowCount > 0);
}
if (moveUpAction != null) {
int minSelectionIndex = listModelAdapter.getMinSelectedRowIndex();
moveUpAction.setEnabled(selectedRowCount == 1 && minSelectionIndex >= 0 && minSelectionIndex > 0);
}
if (moveDownAction != null) {
int maxSelectionIndex = listModelAdapter.getMaxSelectedRowIndex();
moveDownAction.setEnabled(selectedRowCount == 1 && maxSelectionIndex >= 0 && maxSelectionIndex < listModelAdapter.getRowCount() - 1);
}
listModelAdapter.updateState(this);
} else {
if (addAction != null) {
addAction.setEnabled(false);
}
if (removeAction != null) {
removeAction.setEnabled(false);
}
if (moveUpAction != null) {
moveUpAction.setEnabled(false);
}
if (moveDownAction != null) {
moveDownAction.setEnabled(false);
}
}
}
private void checkValidState() {
Assert.state(listModelAdapter != null, "listModelAdapter != null");
}
private class AddAction extends AbstractAction {
private AddAction() {
putValue(Action.ACTION_COMMAND_KEY, ID_ADD);
putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource("icons/Plus16.gif")));
}
@Override
public void actionPerformed(ActionEvent e) {
addRow();
}
}
private class RemoveAction extends AbstractAction {
private RemoveAction() {
putValue(Action.ACTION_COMMAND_KEY, ID_REMOVE);
putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource("icons/Minus16.gif")));
}
@Override
public void actionPerformed(ActionEvent e) {
removeSelectedRows();
}
}
private class MoveUpAction extends AbstractAction {
private MoveUpAction() {
putValue(Action.ACTION_COMMAND_KEY, ID_MOVE_UP);
putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource("icons/MoveUp16.gif")));
}
@Override
public void actionPerformed(ActionEvent e) {
moveSelectedRowUp();
}
}
private class MoveDownAction extends AbstractAction {
private MoveDownAction() {
putValue(Action.ACTION_COMMAND_KEY, ID_MOVE_DOWN);
putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource("icons/MoveDown16.gif")));
}
@Override
public void actionPerformed(ActionEvent e) {
moveSelectedRowDown();
}
}
public interface ListController {
boolean addRow(int index);
boolean removeRows(int[] indices);
boolean moveRowUp(int index);
boolean moveRowDown(int index);
void updateState(ListControlBar listControlBar);
}
public static abstract class AbstractListController implements ListController {
@Override
public void updateState(ListControlBar listControlBar) {
}
}
public interface ListModelAdapter extends ListController {
int getRowCount();
int getMinSelectedRowIndex();
int getMaxSelectedRowIndex();
int[] getSelectedRowIndices();
}
private static abstract class AbstractListModelAdapter implements ListModelAdapter {
private final ListController controller;
protected AbstractListModelAdapter(ListController controller) {
this.controller = controller;
}
@Override
public boolean addRow(int index) {
return controller.addRow(index);
}
@Override
public boolean removeRows(int[] indices) {
return controller.removeRows(indices);
}
@Override
public boolean moveRowUp(int index) {
return controller.moveRowUp(index);
}
@Override
public boolean moveRowDown(int index) {
return controller.moveRowDown(index);
}
@Override
public void updateState(ListControlBar listControlBar) {
controller.updateState(listControlBar);
}
}
private static class JListModelAdapter extends AbstractListModelAdapter implements ListDataListener, ListSelectionListener {
private final ListControlBar listControlBar;
private final JList list;
public JListModelAdapter(ListControlBar listControlBar, JList list, ListController listController) {
super(listController);
this.listControlBar = listControlBar;
this.list = list;
this.list.addPropertyChangeListener("model", new ModelChangeListener());
this.list.addListSelectionListener(this);
}
@Override
public int getRowCount() {
return list.getModel().getSize();
}
@Override
public int getMinSelectedRowIndex() {
return list.getMinSelectionIndex();
}
@Override
public int getMaxSelectedRowIndex() {
return list.getMaxSelectionIndex();
}
@Override
public int[] getSelectedRowIndices() {
return list.getSelectedIndices();
}
@Override
public void intervalAdded(ListDataEvent e) {
listControlBar.updateState();
}
@Override
public void intervalRemoved(ListDataEvent e) {
listControlBar.updateState();
}
@Override
public void contentsChanged(ListDataEvent e) {
listControlBar.updateState();
}
@Override
public void valueChanged(ListSelectionEvent e) {
listControlBar.updateState();
}
private void installModelListener(ListModel oldModel, ListModel newModel) {
if (oldModel != null) {
oldModel.removeListDataListener(this);
}
if (newModel != null) {
newModel.addListDataListener(this);
}
}
private class ModelChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
ListModel oldModel = (ListModel) evt.getOldValue();
ListModel newModel = (ListModel) evt.getNewValue();
installModelListener(oldModel, newModel);
}
}
}
private static class JTableModelAdapter extends AbstractListModelAdapter implements TableModelListener, ListSelectionListener {
private final ListControlBar listControlBar;
private final JTable table;
public JTableModelAdapter(ListControlBar listControlBar, JTable table, ListController listController) {
super(listController);
this.listControlBar = listControlBar;
this.table = table;
installModelListener(null, table.getModel());
installSelectionModelListener(null, table.getSelectionModel());
this.table.addPropertyChangeListener("model", new ModelChangeListener());
this.table.addPropertyChangeListener("selectionModel", new SelectionModelChangeListener());
}
@Override
public int getRowCount() {
return table.getModel().getRowCount();
}
@Override
public int getMinSelectedRowIndex() {
return table.getSelectionModel().getMinSelectionIndex();
}
@Override
public int getMaxSelectedRowIndex() {
return table.getSelectionModel().getMaxSelectionIndex();
}
@Override
public int[] getSelectedRowIndices() {
return table.getSelectedRows();
}
@Override
public void tableChanged(TableModelEvent e) {
listControlBar.updateState();
}
@Override
public void valueChanged(ListSelectionEvent e) {
listControlBar.updateState();
}
private void installModelListener(TableModel oldModel, TableModel newModel) {
if (oldModel != null) {
oldModel.removeTableModelListener(this);
}
if (newModel != null) {
newModel.addTableModelListener(this);
}
}
private void installSelectionModelListener(ListSelectionModel oldModel, ListSelectionModel newModel) {
if (oldModel != null) {
oldModel.removeListSelectionListener(this);
}
if (newModel != null) {
newModel.addListSelectionListener(this);
}
}
private class ModelChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
TableModel oldModel = (TableModel) evt.getOldValue();
TableModel newModel = (TableModel) evt.getNewValue();
installModelListener(oldModel, newModel);
}
}
private class SelectionModelChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
ListSelectionModel oldModel = (ListSelectionModel) evt.getOldValue();
ListSelectionModel newModel = (ListSelectionModel) evt.getNewValue();
installSelectionModelListener(oldModel, newModel);
}
}
}
private static class GridAdapter extends AbstractListModelAdapter implements GridSelectionModel.Listener {
private final ListControlBar listControlBar;
private final Grid grid;
public GridAdapter(ListControlBar listControlBar, Grid grid, ListController listController) {
super(listController);
this.listControlBar = listControlBar;
this.grid = grid;
installSelectionModelListener(null, grid.getSelectionModel());
this.grid.addPropertyChangeListener("selectionModel", new SelectionModelChangeListener());
updateState(listControlBar);
this.grid.addPropertyChangeListener("showSelectionColumn", new ShowSelectionColumnChangeListener());
}
@Override
public int getRowCount() {
return grid.getDataRowCount();
}
@Override
public int getMinSelectedRowIndex() {
return grid.getSelectionModel().getMinSelectedRowIndex();
}
@Override
public int getMaxSelectedRowIndex() {
return grid.getSelectionModel().getMinSelectedRowIndex();
}
@Override
public int[] getSelectedRowIndices() {
return grid.getSelectionModel().getSelectedRowIndices();
}
@Override
public void gridSelectionChanged(GridSelectionModel.Event event) {
listControlBar.updateState();
}
@Override
public void updateState(ListControlBar listControlBar) {
super.updateState(listControlBar);
hideActionIfSelectionRowInvisible(ListControlBar.ID_REMOVE);
hideActionIfSelectionRowInvisible(ListControlBar.ID_MOVE_UP);
hideActionIfSelectionRowInvisible(ListControlBar.ID_MOVE_DOWN);
}
private void hideActionIfSelectionRowInvisible(String actionId) {
Action action = listControlBar.getAction(actionId);
AbstractButton actionComponent = listControlBar.getActionComponent(action);
actionComponent.setVisible(grid.getShowSelectionColumn());
}
private void installSelectionModelListener(GridSelectionModel oldModel, GridSelectionModel newModel) {
if (oldModel != null) {
oldModel.removeListener(this);
}
if (newModel != null) {
newModel.addListener(this);
}
}
private class SelectionModelChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
GridSelectionModel oldModel = (GridSelectionModel) evt.getOldValue();
GridSelectionModel newModel = (GridSelectionModel) evt.getNewValue();
installSelectionModelListener(oldModel, newModel);
}
}
private class ShowSelectionColumnChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
updateState(listControlBar);
}
}
}
}