Tracking document position changes - java

I am using the Document method createPosition to add Positions to an array. These Positions change depending on the user inserting/removing lines.
How do I hook changes in my array? I need to know the new value and the value which it has changed from.

Interface Position has no method to add listener to monitor position change. So you need do it manually using wrapper for Position object.
public class PositionHolder {
private int oldPosition;
private Position position;
private PropertyChangeSupport propertySupport = new PropertyChangeSupport(this);
public PositionHolder(Position aPos) {
position = aPos;
oldPosition = aPos.getOffset();
}
public void addPropertyChangeListener(PropertyChangeListener aListener) {
propertySupport.addPropertyChangeListener(aListener);
}
// same for remove listener
public void verifyPosition() {
// no custom if statement required - event is fiered only when old != new
propertySupport.firePropertyChangeEvent("position", oldPosition, position.getOffset());
oldPosition = position.getOffset();
}
public Position getPosition() {
return position;
}
}
public class PositionUpdater implements DocumentListener {
private List<PositionHolder> holders;
public PositionUpdater(List<PositionHolder> someHolders) {
holders = someHolders;
}
public void insertUpdate(DocumentEvent e) {
for (PositionHolder h : holders) {
h.verifyPosition();
}
}
public void removeUpdate(DocumentEvent e) {
insertUpdate(e);
}
public void changeUpdate(DocumentEvent e) {
insertUpdate(e);
}
}
JTextComponent textComp = // your text component
List<PositionHolder> holderList = new ArrayList<>(); // your list of positions
textComp.getDocument().addDocumentListener(new PositionUpdater(holderList));
Now you will get notification when position is changed. You only need to fill the holderList with wrappers for your positions and register your event listener on these wrappers.

Related

Custom Callback<TableView<T>, TableRow<T>> to execute callback on double click in javafx

I have the following implementation:
class CustomTableRow<T> implements CallBack <Tableview<T> TableRow<T>>{
private T selectedItem;
public CustomTableRow(T selectedItem){
this.selectedItem = selectedItem;
}
#Override
public TableRow<T> call(TableView<T> param){
TableRow<T> row = new TableRow<>΄
row.setOnMouseClicked(event ->{
if (!row.isEmpty()){
try{
//Using BeanUtils.copyProperties();
}catch (Exception e){
}
}
});
}
}
I have a class that uses this CustomTableRow
class SomeClass{
private Tableview myTableView;
private MyCustomModelClass selectedItem;
private setUpTableView(){
selectedItem = new MyCustomModelClass();
tableView.setRowFactory(new CustomTableRow<MyCustomModelClass>(selectedItem));
}
private thisNeedsToBeExecutedOnRowDoubleClick(){
}
}
To make things worst this CustomTableRow is used by other TableViews that don't need to implement the double click function.
Should I create two different CustomTableRow classes or is it possible to pass a callback function (like javascript) to execute when double click is detected and selectedItem is of MyCustomModelClass type? I am using java version 8 Update 45.
Define your CustomTableRow to take a Consumer<T> as a parameter, and invoke that object's accept(...) method when the double-click occurs. Here's the basic idea (I renamed CustomTableRow to DoubleClickTableRowFactory as I think it's a better description of what this does):
public class MouseClickTableRowFactory<T>
implements Callback<TableView<T>, TableRow<T>> {
private final Consumer<T> doubleClickHandler ;
private final int numClicks ;
public MouseClickTableRowFactory(Consumer<T> doubleClickHandler, int numClicks) {
this.doubleClickHandler = doubleClickHandler ;
this.numClicks = numClicks ;
}
public MouseClickTableRowFactory(Consumer<T> doubleClickHandler) {
// default to double-click...
this(doubleClickHandler, 2);
}
#Override
public TableRow<T> call(TableView<T> tv) {
TableRow<T> row = new TableRow<>();
row.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
if (event.getClickCount() == numClicks && ! row.isEmpty()) {
doubleClickHandler.accept(row.getItem());
}
});
return row ;
}
}
Now you can do
public class SomeClass {
private TableView<MyCustomModelClass> myTableView ;
// ...
private void setupTable() {
myTableView.setRowFactory(
// invokes rowDoubleClicked when user double-clicks on a row:
new MouseClickTableRowFactory<>(this::rowDoubleClicked));
// for single click, do
// myTableView.setRowFactory(
// new MouseClickTableRowFactory(item -> { ... }, 1));
// ...
}
private void rowDoubleClicked(MyCustomModelClass item) {
// handle double-click on row displaying item
}
}

JFrame - JList specific actions depending on selection

I created a list basically copying the 'ListDemo.java' from the oracle site. I included a picture of what I have.
public static class BackpackList extends JPanel implements ListSelectionListener {
private JList list;
private DefaultListModel listModel;
private static final String useString = "Use";
private JButton useButton;
public BackpackList() {
super(new BorderLayout());
listModel = new DefaultListModel();
listModel.addElement("Flashlight");
listModel.addElement("Health potion");
listModel.addElement("Snacks");
//Create the list and put it in a scroll pane.
list = new JList(listModel);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setSelectedIndex(0);
list.addListSelectionListener(this);
list.setVisibleRowCount(10);
JScrollPane listScrollPane = new JScrollPane(list);
useButton = new JButton(useString);
useButton.setActionCommand(useString);
useButton.addActionListener(new UseListener());
//Create a panel that uses BoxLayout.
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane,
BoxLayout.LINE_AXIS));
buttonPane.add(useButton);
buttonPane.add(Box.createHorizontalStrut(5));
buttonPane.add(new JSeparator(SwingConstants.VERTICAL));
buttonPane.add(Box.createHorizontalStrut(5));
buttonPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
add(listScrollPane, BorderLayout.CENTER);
add(buttonPane, BorderLayout.PAGE_END);
}
class UseListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//This method can be called only if
//there's a valid selection
//so go ahead and remove whatever's selected.
int index = list.getSelectedIndex();
listModel.remove(index);
int size = listModel.getSize();
if (size == 0) { //Nobody's left, disable firing.
useButton.setEnabled(false);
}
else { //Select an index.
if (index == listModel.getSize()) {
//removed item in last position
index--;
}
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
}
}
#Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false) {
if (list.getSelectedIndex() == -1) {
//No selection, disable fire button.
useButton.setEnabled(false);
}
else {
//Selection, enable the fire button.
useButton.setEnabled(true);
}
}
}
}
Question 1: I am setting up a backpack for a basic text based game. I want to set up specific actions depending on what item you have selected in the list. What would be the code to make it so the health potion would do something different than the snacks?
Question 2: How could I make it so it would say something along the lines of "x2 Snacks" if you have 2 snacks or "x3 Snacks" if you have 3 snacks, etc.
The backpack needs to keep track of items that are defined elsewhere. JList can hold a list of any kind of object so what you need to do is create objects for the inventory items. Below shows an example using an enum:
public class InventoryManager {
public enum InventoryItem {
LIGHT("Flashlight") {
boolean isOn;
#Override public void doAction() {
isOn = !isOn;
}
#Override public String toString() {
return name;
}
},
POTION("Health Potions") {
#Override public void doAction() {
Game.getPlayer().setHealth(Game.getPlayer().getHealth() + 25);
remove(1);
}
},
SNACK("Snacks") {
#Override public void doAction() {
Game.getPlayer().setEnergy(Game.getPlayer().getEnergy() + 10);
remove(1);
}
};
private final String name;
private int quantity = 0;
private InventoryItem(String n) {
name = n;
}
public abstract void doAction();
public void add(int q) {
if ((quantity += q) < 0) quantity = 0;
}
public void remove(int q) {
add(-q);
}
#Override public String toString() {
return name + " x" + quantity;
}
}
public static InventoryItem[] getHeldItems() {
EnumSet<InventoryItem> items = EnumSet.allOf(InventoryItem.class);
Iterator<InventoryItem> it = items.iterator();
while (it.hasNext()) {
if (it.next().quantity < 1) {
it.remove();
}
}
return items.toArray(new InventoryItem[items.size()]);
}
}
The enum example is entirely static so there are some problems with actually doing it this way (I chose it primarily because it's the shortest code). But ultimately what you'll have is a superclass Item with abstract methods that the subclasses implement differently. Then you will populate the JList with the items held. When the user selects an item from the list, list.getSelectedValue() returns an Item object you can use in the game.
// or Item can be an interface classes implement
public abstract class Item {
public void doAction() {
Game.updateState();
}
}
public class Light extends InventoryItem {
boolean lighted;
#Override public void doAction() {
lighted = !lighted;
super.doAction();
}
}
public class Potion extends InventoryItem {
#Override public void doAction() {
player.hp++;
super.doAction();
}
}
public class Snack extends InventoryItem {
#Override public void doAction() {
player.energy++;
super.doAction();
}
}
The other way to do this is to use straight program logic, for example:
switch (list.getSelectedItem()) {
case "Flashlight": {
toggleFlashlight();
break;
}
case "Health Potion": {
usePotion();
break;
}
case "Snack": {
useSnack();
break;
}
}
But I have a feeling trying to do it all with logic like that will ultimately turn out to be more complicated.

How to update a JSlider's maximum value without dispatching a change event

I have wee bit of code that updates a JSlider; and at another point in time, the JSlider's maximum value needs to updated. The problem is when I call setMaximum() on the slider, it also dispatches a ChangeEvent. To avoid that I'm doing this:
slider.removeChangeListener(this);
slider.setMaximum(newMax);
slider.addChangeListener(this);
Is there a cleaner/more elegant way of doing this?
A clean way might be (depending a bit on what you actually need) to implement a custom BoundedRangeModel which fires a custom ChangeEvent that can carry the actually changed properties:
/**
* Extended model that passes the list of actually changed properties
* in an extended changeEvent.
*/
public static class MyBoundedRangeModel extends DefaultBoundedRangeModel {
public MyBoundedRangeModel() {
}
public MyBoundedRangeModel(int value, int extent, int min, int max) {
super(value, extent, min, max);
}
#Override
public void setRangeProperties(int newValue, int newExtent, int newMin,
int newMax, boolean adjusting) {
int oldMax = getMaximum();
int oldMin = getMinimum();
int oldValue = getValue();
int oldExtent = getExtent();
boolean oldAdjusting = getValueIsAdjusting();
// todo: enforce constraints of new values for all
List<String> changedProperties = new ArrayList<>();
if (oldMax != newMax) {
changedProperties.add("maximum");
}
if (oldValue != newValue) {
changedProperties.add("value");
}
// todo: check and add other properties
changeEvent = changedProperties.size() > 0 ?
new MyChangeEvent(this, changedProperties) : null;
super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
}
}
/**
* Extended ChangeEvent that provides a list of actually
* changed properties.
*/
public static class MyChangeEvent extends ChangeEvent {
private List<String> changedProperties;
/**
* #param source
*/
public MyChangeEvent(Object source, List<String> changedProperties) {
super(source);
this.changedProperties = changedProperties;
}
public List<String> getChangedProperties() {
return changedProperties;
}
}
Its usage something like:
final JSlider slider = new JSlider();
slider.setModel(new MyBoundedRangeModel(0, 0, -100, 100));
ChangeListener l = new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if (e instanceof MyChangeEvent) {
MyChangeEvent me = (MyChangeEvent) e;
if (me.getChangedProperties().contains("value")) {
System.out.println("new value: " +
((BoundedRangeModel) e.getSource()).getValue());
}
if (me.getChangedProperties().contains("maximum")) {
System.out.println("new max: " +
((BoundedRangeModel) e.getSource()).getMaximum());
}
} else {
// do something else or nothing
}
}
};
slider.getModel().addChangeListener(l);
Note that you have to register the listener with the model, not with the slider (reason being that the slider creates a new changeEvent of the plain type)
You could check who triggered the change in the listener. It's still pretty dirty but you won't have to remove the change listener.
It looks like the same problem, in essence, as this question. In which case, I fear the answer is "no"
if you just need your slider to fire events when value is changing you can simplify kleopatra's answer in the following way:
// make sure slider only fires changEvents when value changes
slider.setModel(new DefaultBoundedRangeModel() {
final ChangeEvent theOne=new ChangeEvent(this);
#Override
public void setRangeProperties(int newValue, int newExtent, int newMin,int newMax, boolean adjusting)
changeEvent= (getValue() != newValue ? theOne:null);
super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
}
#Override
protected void fireStateChanged()
{
if(changeEvent==null) return;
super.fireStateChanged();
}
});
Then all you need to do is register a "Standard" ChangeListener on the model and it will only get called when the value is changing not when maximum or minimum changes.
slider.getModel().addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
// do something here when value changes
});
});

Column moved [finished] event in JTable

How should I detect that column moved action is finished in JTable? I've added columnModeListener to my column model but the problem is columnMoved method is called every time a column moves (by certain pixels). I don't want this behavior. I just want to detect when the column dragging is finished.
columnModel.addColumnModelListener(new TableColumnModelListener() {
public void columnAdded(TableColumnModelEvent e) {
}
public void columnRemoved(TableColumnModelEvent e) {
}
public void columnMoved(TableColumnModelEvent e) {
//this is called so many times
//I don't want this, but something like column moved finished event
System.out.println("Moved "+e.getFromIndex()+", "+e.getToIndex());
}
public void columnMarginChanged(ChangeEvent e) {
}
public void columnSelectionChanged(ListSelectionEvent e) {
}
});
I hope it is clear what I'm looking for. Thanks.
This is what I ended up doing. I know it is dirty, but it fits for what I'm looking:
boolean dragComplete = false;
apTable.getTableHeader().addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
if (dragComplete) {
System.out.println("Drag completed");
}
dragComplete = false;
}
});
columnModel.addColumnModelListener(new TableColumnModelListener() {
public void columnAdded(TableColumnModelEvent e) {
}
public void columnRemoved(TableColumnModelEvent e) {
}
public void columnMoved(TableColumnModelEvent e) {
dragComplete = true;
}
public void columnMarginChanged(ChangeEvent e) {
}
public void columnSelectionChanged(ListSelectionEvent e) {
}
});
Here's an inner class I use to determine when the column ordering has changed. Note that the user may not have let go of the mouse at this point, so the dragging may continue further.
private class ColumnUpdateListener implements TableColumnModelListener {
int lastFrom = 0;
int lastTo = 0;
private void verifyChange(int from, int to) {
if (from != lastFrom || to != lastTo) {
lastFrom = from;
lastTo = to;
///////////////////////////////////////
// Column order has changed! Do something here
///////////////////////////////////////
}
}
public void columnMoved(TableColumnModelEvent e) {
verifyChange(e.getFromIndex(), e.getToIndex());
}
public void columnAdded(TableColumnModelEvent e) {
verifyChange(e.getFromIndex(), e.getToIndex());
}
public void columnRemoved(TableColumnModelEvent e) {
verifyChange(e.getFromIndex(), e.getToIndex());
}
public void columnMarginChanged(ChangeEvent e) {}
public void columnSelectionChanged(ListSelectionEvent e) {}
}
It's worked well for me.
This might be a better approach:
table.setTableHeader(new JTableHeader(table.getColumnModel()) {
#Override
public void setDraggedColumn(TableColumn column) {
boolean finished = draggedColumn != null && column == null;
super.setDraggedColumn(column);
if (finished) {
onColumnChange(table); // Handle the event here...
}
}
});
This is what works for me (both column movements and margin resizes):
I extend the table and override the columnMoved and columnMarginChanged
methods in the following way:
... first add some variables for state keeping
private int lastMovementDistance = 0;
private boolean bigMove = false;
private boolean resizeBegan = false;
...
#Override
public void columnMarginChanged(ChangeEvent e) {
super.columnMarginChanged(e);
if (isShowing()){
resizeBegan = true;
}
}
#Override
public void columnMoved(TableColumnModelEvent e) {
super.columnMoved(e);
//this will be set to 0 when the column is dragged
//to where it should begin if released
lastMovementDistance = Math.abs(getTableHeader().getDraggedDistance());
if (e.getFromIndex() != e.getToIndex()){
//note, this does not guarantee that the columns will be eventually
//swapped - the user may move the column back.
//but it prevents us from reacting to movements where
//the user hasn't even moved the column further then its nearby region.
//Works for me, because I don't care if the columns stay the same
//I only need the updates to be infrequent and don't want to miss
//changes to the column order
bigMove = true;
}
}
... then in the constructor of my table i do this:
public MyTable(){
...
getTableHeader().addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent evt) {
if (bigMove && lastMovementDistance == 0 ){
//react! the tables have possibly switched!
}
else if (resizeBegan){
//react! columns resized
}
resizeBegan = false;
bigMove = false;
}
});
...
}
It is kinda like a hack, but it works for me.
Nice answer on your own question ashokgelal. Just a little improvement I think. Your code also trigger on single click on the header. Using one more flag you can prevent the 'dragComplete' trigger when the column haven't really changed.
Modified code:
boolean mDraggingColumn = false;
boolean mColumnCHangedIndex = false;
tblObjects.getTableHeader().addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
if (mDraggingColumn && mColumnCHangedIndex) {
System.out.println("Column changed");
}
mDraggingColumn = false;
mColumnCHangedIndex = false;
}
});
tblObjects.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
#Override
public void columnAdded(TableColumnModelEvent e) {}
#Override
public void columnRemoved(TableColumnModelEvent e) {}
#Override
public void columnMoved(TableColumnModelEvent e) {
mDraggingColumn = true;
if (e.getFromIndex() != e.getToIndex()) {
mColumnCHangedIndex = true;
}
}
#Override
public void columnMarginChanged(ChangeEvent e) {}
#Override
public void columnSelectionChanged(ListSelectionEvent e) {}
});
If I understand you correctly, maybe you want to look at the mouse listeners. Maybe the MOUSE_RELEASED event?
All answers fail at one use-case: if the table is in a layout filling up the entire window, then resizing the window will resize the table and thus its columns. By watching for mouse events on the column headers, we fail to receive the event when the user resize the window.
I looked at the JTable&friends source-code, and the columnMarginChanged() method is always called in a sub-sub-sub...-function called by JTable.doLayout().
Then, my solution is to watch for doLayout() calls that trigger at least one columnMarginChanged().
In fact, columnMarginChanged() is called for every columns.
Here is my solution:
private class ResizableJTable extends JTable {
private TableColumnModelListener columnModelListener;
private boolean columnsWereResized;
#Override
public void setColumnModel(TableColumnModel columnModel) {
if (getColumnModel() != null) {
getColumnModel().removeColumnModelListener(columnModelListener);
columnModelListener = null;
}
if (columnModel != null) {
columnModelListener = new TableColumnModelListener() {
public void columnSelectionChanged(ListSelectionEvent e) {
// Nothing to do
}
public void columnRemoved(TableColumnModelEvent e) {
// Nothing to do
}
public void columnMoved(TableColumnModelEvent e) {
// Nothing to do
}
public void columnMarginChanged(ChangeEvent e) {
columnsWereResized = true;
}
public void columnAdded(TableColumnModelEvent e) {
// Nothing to do
}
};
columnModel.addColumnModelListener(columnModelListener);
}
super.setColumnModel(columnModel);
}
#Override
public void doLayout() {
columnsWereResized = false;
super.doLayout();
if (columnsWereResized) {
onColumnsResized();
}
}
/**
* Sub-classes can override this method to
* get the columns-were-resized event.
* By default this method must be empty,
* but here we added debug code.
*/
protected void onColumnsResized() {
int[] columnSizes = getColumnSizes();
String sizes = "";
for (int i : columnSizes) {
sizes += i + " ";
}
System.out.println("COLUMNS RESIZED: [ " + sizes + "]");
}
protected int[] getColumnSizes() {
TableColumnModel columnModel = getTableHeader().getColumnModel();
int columnCount = columnModel.getColumnCount();
int[] columnSizes = new int[columnCount];
for(int i = 0; i < columnCount; i++) {
TableColumn column = columnModel.getColumn(i);
columnSizes[i] = column.getWidth();
}
return columnSizes;
}
}

Can i use JCheckbox to show "mixed state"

In windows it is possible to show a grayed out JCheckbox, to show that the collection of data which it represents not all items have the same value.
Is this even possible with a JCheckBox?
How do i go about this?
(Hoping there's a way to not override it)
Thanks
JIDE Common Layer has a TristateCheckBox.
It's possible with some of work.
I have this code from some years ago. Is based in some examples I found in internet, but I cannot find any reference to the original creator, so I apologize
import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import java.awt.event.*;
/**
* Maintenance tip - There were some tricks to getting this code
* working:
*
* 1. You have to overwite addMouseListener() to do nothing
* 2. You have to add a mouse event on mousePressed by calling
* super.addMouseListener()
* 3. You have to replace the UIActionMap for the keyboard event
* "pressed" with your own one.
* 4. You have to remove the UIActionMap for the keyboard event
* "released".
* 5. You have to grab focus when the next state is entered,
* otherwise clicking on the component won't get the focus.
* 6. You have to make a TristateDecorator as a button model that
* wraps the original button model and does state management.
*/
public class TristateCheckBox extends JCheckBox {
/** This is a type-safe enumerated type */
public static class State { private State() { } }
public static final State NOT_SELECTED = new State();
public static final State SELECTED = new State();
public static final State DONT_CARE = new State();
private final TristateDecorator model;
public TristateCheckBox(String text, Icon icon, State initial){
super(text, icon);
// Add a listener for when the mouse is pressed
super.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
grabFocus();
model.nextState();
}
});
// Reset the keyboard action map
ActionMap map = new ActionMapUIResource();
map.put("pressed", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
grabFocus();
model.nextState();
}
});
map.put("released", null);
SwingUtilities.replaceUIActionMap(this, map);
// set the model to the adapted model
model = new TristateDecorator(getModel());
setModel(model);
setState(initial);
}
public TristateCheckBox(String text, State initial) {
this(text, null, initial);
}
public TristateCheckBox(String text) {
this(text, DONT_CARE);
}
public TristateCheckBox() {
this(null);
}
/** No one may add mouse listeners, not even Swing! */
public void addMouseListener(MouseListener l) { }
/**
* Set the new state to either SELECTED, NOT_SELECTED or
* DONT_CARE. If state == null, it is treated as DONT_CARE.
*/
public void setState(State state) { model.setState(state); }
/** Return the current state, which is determined by the
* selection status of the model. */
public State getState() { return model.getState(); }
public void setSelected(boolean b) {
if (b) {
setState(SELECTED);
} else {
setState(NOT_SELECTED);
}
}
/**
* Exactly which Design Pattern is this? Is it an Adapter,
* a Proxy or a Decorator? In this case, my vote lies with the
* Decorator, because we are extending functionality and
* "decorating" the original model with a more powerful model.
*/
private class TristateDecorator implements ButtonModel {
private final ButtonModel other;
private TristateDecorator(ButtonModel other) {
this.other = other;
}
private void setState(State state) {
if (state == NOT_SELECTED) {
other.setArmed(false);
setPressed(false);
setSelected(false);
} else if (state == SELECTED) {
other.setArmed(false);
setPressed(false);
setSelected(true);
} else { // either "null" or DONT_CARE
other.setArmed(true);
setPressed(true);
setSelected(true);
}
}
/**
* The current state is embedded in the selection / armed
* state of the model.
*
* We return the SELECTED state when the checkbox is selected
* but not armed, DONT_CARE state when the checkbox is
* selected and armed (grey) and NOT_SELECTED when the
* checkbox is deselected.
*/
private State getState() {
if (isSelected() && !isArmed()) {
// normal black tick
return SELECTED;
} else if (isSelected() && isArmed()) {
// don't care grey tick
return DONT_CARE;
} else {
// normal deselected
return NOT_SELECTED;
}
}
/** We rotate between NOT_SELECTED, SELECTED and DONT_CARE.*/
private void nextState() {
State current = getState();
if (current == NOT_SELECTED) {
setState(SELECTED);
} else if (current == SELECTED) {
setState(DONT_CARE);
} else if (current == DONT_CARE) {
setState(NOT_SELECTED);
}
}
/** Filter: No one may change the armed status except us. */
public void setArmed(boolean b) {
}
/** We disable focusing on the component when it is not
* enabled. */
public void setEnabled(boolean b) {
setFocusable(b);
other.setEnabled(b);
}
/** All these methods simply delegate to the "other" model
* that is being decorated. */
public boolean isArmed() { return other.isArmed(); }
public boolean isSelected() { return other.isSelected(); }
public boolean isEnabled() { return other.isEnabled(); }
public boolean isPressed() { return other.isPressed(); }
public boolean isRollover() { return other.isRollover(); }
public void setSelected(boolean b) { other.setSelected(b); }
public void setPressed(boolean b) { other.setPressed(b); }
public void setRollover(boolean b) { other.setRollover(b); }
public void setMnemonic(int key) { other.setMnemonic(key); }
public int getMnemonic() { return other.getMnemonic(); }
public void setActionCommand(String s) {
other.setActionCommand(s);
}
public String getActionCommand() {
return other.getActionCommand();
}
public void setGroup(ButtonGroup group) {
other.setGroup(group);
}
public void addActionListener(ActionListener l) {
other.addActionListener(l);
}
public void removeActionListener(ActionListener l) {
other.removeActionListener(l);
}
public void addItemListener(ItemListener l) {
other.addItemListener(l);
}
public void removeItemListener(ItemListener l) {
other.removeItemListener(l);
}
public void addChangeListener(ChangeListener l) {
other.addChangeListener(l);
}
public void removeChangeListener(ChangeListener l) {
other.removeChangeListener(l);
}
public Object[] getSelectedObjects() {
return other.getSelectedObjects();
}
}
}
My colleague who this question came from thought of this;
Create a dummy JCheckBox which is disabled and selected. set the same size as the real one.
Create an Icon which' paint method actually paints the dummy JCheckbox.
Set the original JCheckBox' Icon to the one painting the dummy.
Remove the icon as soon as the JCheckBox is clicked.
++ No overridden JCheckBox
-- not a real tri-state Combo
I think he's satisfied.
Thanks for the help

Categories