I have created chess board using JButton and for pieces I used ASCII values.
I added ActionListener to all the buttons. When i clicked source(first) button actionPerformed event is called and i stored the value of the button in the variable.
But the problem is when i clicked the destination button actionPerformed event is called and value is replaced with source button value.
I want source button value and destination button value in different variables. How it is possible?
public void actionPerformed(ActionEvent ae)
{
JButton o = (JButton) ae.getSource();
value = o.getText();
}
Then you need 2 variables to store the two values and a third variable to tell you which click is happening.
if(isSource){
source = o.getText();
isSource = false;
}else{
destination = o.getText();
isSource = true;
}
This way, on the first click you know the value of the source, and on teh second click you know the value of the destination. Then on the next click it is a source again etc.
You have to manage the state of your application in some way. At the first click, the state of the application is that it waits for information of the source of the move. Then it waits for information of the destination of the move.
You could create an enum in your class like this:
enum ActionState {
SOURCE,
DESTINATION
}
And store the action state and the button variables as members of the class:
ActionState state = SOURCE;
String source;
String destination;
Then actionPerformed would be like this:
public void actionPerformed(ActionEvent ae)
{
JButton o = (JButton) ae.getSource();
String value = o.getText();
if (state == SOURCE){
source = value;
state = DESTINATION;
}
else if (state == DESTINATION){
destination = value;
state = SOURCE;
// you probably want to call something here to perform the move itself.
}
}
Instead of an enum, you could just use a boolean to represent your state. But using an enum allows for more extensibility if you need other states in the future.
Related
When I receive a ChangeEvent from my JSpinner, I'd like to detect if user used the arrows to increase/decrease the number value, or directly typed a new number.
What would be the best approach to do this ?
EDIT: for now my non-reliable solution is just to save the last JSpinner changed value, and if new change value is +/- equals to the step value, then I assume user clicked on an arrow. It works except if user typed a value which is equals to (oldValue +/- step).
EDIT: why ?
I want to reproduce the behavior found in Midi editors of several famous DAWs. The JSpinner represents the velocity (0-127) of selected notes. It shows the velocity of the first selected note. Usually notes velocity differ. When you increase with arrow, you want to increase all selected notes by the same amount. When you type in a new value, you want all velocities to be reset to this value.
Distinguishing the trigger of a value change is not supported - the only value-related event fired by JSpinner is a ChangeEvent which carries no state except the source. We need another type of listener or a combination of listeners.
First thought: listen to changes of the editor's textField, f.i. an actionListener or a propertyChangeListener on the value property. This doesn't work, mainly because
both change- and propertyChangeListener are fired always (change before property)
actionListener is not fired on focusLost
Second thought: go dirty and dig into implementation details to hook into the action fired by the arrow buttons.
The idea:
look up the action for increment/decrement from the spinner's actionMap: this is the same as the arrows' actions and also used by up/down keys (which I assume not counting a "editing")
for each, create a wrapper that sets a flag before delegating to super
put that wrapper into the spinner's actionMap
look up the arrow buttons in the spinner's children and replace their respective actionListener with the wrapper
Client code would change the tweak's changeListener to acts according to the flag as appropriate.
Some code doing the tweaking (beware: not formally tested!):
public static class SpinnerTweaker {
private JSpinner spinner;
private boolean wasButtonAction;
private Object oldValue;
public SpinnerTweaker(JSpinner spinner) {
this.spinner = spinner;
AbstractAction incrementDelegate = createDelegate("increment");
spinner.getActionMap().put("increment", incrementDelegate);
AbstractAction decrementDelegate = createDelegate("decrement");
spinner.getActionMap().put("decrement", decrementDelegate);
// replacing arrow button's action
Component[] components = spinner.getComponents();
for (Component component : components) {
if (component instanceof JButton) {
if (component.getName() == "Spinner.nextButton") {
ActionListener[] actions = ((JButton) component).getActionListeners();
ActionListener uiAction = actions[0];
((JButton) component).removeActionListener(uiAction);
((JButton) component).addActionListener(incrementDelegate);
}
if (component.getName() == "Spinner.previousButton") {
ActionListener[] actions = ((JButton) component).getActionListeners();
ActionListener uiAction = actions[0];
((JButton) component).removeActionListener(uiAction);
((JButton) component).addActionListener(decrementDelegate);
}
}
}
spinner.addChangeListener(e -> {
if (wasButtonAction) {
System.out.println("value changed by button: " + spinner.getValue());
} else {
System.out.println("value changed by editing: " + spinner.getValue());
}
wasButtonAction = false;
});
}
protected AbstractAction createDelegate(String actionCommand) {
// hooking into original button action to set button flag
AbstractAction action = (AbstractAction) spinner.getActionMap().get(actionCommand);
AbstractAction delegate = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
oldValue = spinner.getValue();
wasButtonAction = true;
action.actionPerformed(e);
// hit min/max - TBD: needs testing!
if (oldValue.equals(spinner.getValue())) {
wasButtonAction = false;
}
}
};
return delegate;
}
}
#kleopatra solution works but I found a simpler solution.
The trick is that commitEdit() is only called internally by JSpinner when change results from the increment or decrement action.
public class Spinner2 extends JSpinner
{
// To be checked by ChangeListener after receiving the ChangeEvent
public boolean wasManualEdit=true;
#Override
public void commitEdit() throws ParseException
{
wasManualEdit = false;
super.commitEdit();
}
#Override
protected void fireStateChanged()
{
super.fireStateChanged();
wasManualEdit = true;
}
}
i had two classes, one is Main.java another is Button.java
So in Main.java, i need to confirm while Jbutton in Button.java is clicked, then Main.java will do something
in Button.java
public void actionPerformed(ActionEvent e) {
if(e.getSource()==Jsend)
{
String userinput = Jusertxt.getText();
setUserText(userinput);//setUserText is a method for Main.java can get a String from user input.
Jusertxt.setText("");
}
}
what is the statement i need to write in Main.java to check button is clicked in Button.java?
here is some code in Main.java
while(true){
if(//in here i need to ensure button is clicked){
} }
To receive notification button is pressed, you must register listener in Main.java. With it you can synchronize a local variable to store the status of the button (if it is clicked). This variable can use later to determine whether the button is pressed (once).
Edit Another option is the variable part of Button.java and to implement a public method isClicked()
Put a boolean value within the button, use a getter to retrieve the value of the boolean e.g
Example :
//Within main
Button button = new Button(); // I wouldn't recommend using button as a
class name by the way, will get very confusing
if(button.getPressed){
// do stuff
}
Your button code:
public void actionPerformed(ActionEvent e) {
if(e.getSource()==Jsend)
{
String userinput = Jusertxt.getText();
setUserText(userinput);//setUserText is a method for Main.java can get a String from user input.
Jusertxt.setText("");
isPressed = true;
}
}
public boolean getPressed(){
return isPressed;
}
I have a class that extends BasicTabbedPaneUI and does some paint component overriding.
I want to be able to add a addMouseListener to the class I use it in to check when the user selects a tab the current tab index and the previous tab index.
NOTE: The user is able to navigate to tabs via the keyboard and not just clicking on a tab and I want to be able to make sure the previous index tracks this. So in the example below preIndex would equal the previous index regardless to whether the user navigated to it via the keyboard or mouse.
Any ideas please?
tabbedPane.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JTabbedPane tabP = (JTabbedPane) e.getSource();
int currIndex = tabP.indexAtLocation(e.getX(), e.getY());
int prevIndex = ?????
}
});
Many thanks!!!!
I would use the change listener instead of mouse listener (it's called in both cases: for mouse and key event triggered tab change). If you cannot determine previously selected tab you can use following approach: save currently selected tab index as client property of the tabbed pane.
private static final String OLD_TAB_INDEX_PROPERTY = "oldTabIdx";
tabbedPane.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JTabbedPane tabP = (JTabbedPane) e.getSource();
int currIndex = tabP.getSelectedIndex();
int oldIdx = 0;
Object old = tabP.getClientProperty(OLD_TAB_INDEX_PROPERTY);
if (old instanceof Integer) {
oldIdx = (Integer) old;
}
tabP.putClientProperty(OLD_TAB_INDEX_PROPERTY, currIndex);
// now we can use old and current index
}
});
I am creating a GUI in which you first click on a radio button to define a variable, and then you click on a button. This button then tests for that variable and then performs two different actions depending on what the integer equals. My problem is that, when I click on the button, the variable which is defined with the radio buttons is not registered. I need to make this variable be able to be found by the action listener which the button takes advantage of. Here is a segment of my code:
radioButtonYes.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
int a = 1;
}
});
radioButtonNo.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
int a = 2;
}
});
submitOne.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
if(a == 1){
frame.setVisible(false);
JPanel panelFour = new JPanel();
JLabel labelTwo = new JLabel("Stuff");
panelFour.add(labelTwo);
frame.remove(panelOne);
frame.remove(panelTwo);
frame.remove(submitOne);
frame.add(panelFour);
frame.setVisible(true);
}else{
frame.setVisible(false);
JPanel panelFive = new JPanel();
JLabel labelThree = new JLabel("Alternate stuff");
panelFive.add(labelThree);
frame.remove(panelOne);
frame.remove(panelTwo);
frame.remove(submitOne);
frame.add(panelFive);
frame.setVisible(true);
}
}
});
Action Listeners are static method and can only target static variables if they are not created inside the methods themselves. If this is how you want to go about doing this I suggest the following:
Create a global variable
static int a;
Then with all the methods remove the int in front of the variable like so, so that they target the global variable and set it to the correct value you are looking for
radioButtonNo.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
a = 2;
}
});
Ok. Try:
radioButtonYes.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
if( e.getSource() == radioButtonYes) {
int a = 1;
}
}
});
And change radioButtonYes to whatever...
You cannot access local variables from outside of the block that it was declared. The fact that you are even allowed to declare int a = 1; is a clear indication that it is a strictly local variable. You must have some other int declared somewhere else in your code, otherwise, if(a == 1){ would not execute.
Either way, you do not need to create listeners for the radio buttons. For your test just see if the yesRadioButton is selected:
if(radioButtonYes.isSelected()){
...
}
Of course, yesRadioButton must be declared final before Java 8.
If you are using Java 8, it only needs to be effectively final, meaning that it is not modified anywhere.
In Java 8, declaring ActionListener can be simplified as well:
submitOne.addActionListener( e -> {
...
}):
there is no way of making local variable global as scope of variable 'a' is lost when submitOne button is clicked so declare variable 'a' globally.so that it will retain value of last clicked button.
I am showing some results in a JTable that consists of 2 columns.
File - Result
I implemented a JPopupMenu which displays a copy entry, and I try to copy the value of the cell, where I right-clicked.
filelistTable.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if(SwingUtilities.isRightMouseButton(e))
{
TablePopupMenu popup = new TablePopupMenu(filelistTable, e.getPoint());
filelistTable.setComponentPopupMenu(popup);
}
}
});
--
public TablePopupMenu(JTable table, Point p) {
this.table = table;
this.p = p;
JMenuItem mntmKopieren = new JMenuItem("Kopieren");
mntmKopieren.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
copyCellToClipboard();
}
});
add(mntmKopieren);
}
public void copyCellToClipboard()
{
int r = table.rowAtPoint(p);
int c = table.columnAtPoint(p);
System.out.println(table.getValueAt(table.convertRowIndexToView(r),
table.convertRowIndexToView(c)));
StringSelection entry = new StringSelection(table.getValueAt(table.convertRowIndexToView(r),
table.convertRowIndexToView(c)).toString());
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents( entry, this );
}
Anyhow, this only works for a small number of tests.
Did I do something wrong or something missing? It looks to me, as if the cell will not even get choosen correctly.
Two thingies are slightly off:
setting the componentPopup in the clicked is too late in the sequence of mouseEvents (popups are typically triggered on pressed or released which happen before the click)
the value is taken from the incorrect cell: all coordinates in a JTable are in view coordinate system, converting them to view coordinates will be completely off
That said: getting cell-coordinate related context is poorly supported. Often, the best bet is to (code snippet below)
override getPopupLocation(MouseEvent) and store the location somewhere
implement a popup/action to access the location
Fails if (as should be done in a well-behaved application), the popup could be triggered by keyboard: if that's the case, you'll need to provide some other marker (f.i. the focused cell) to act on.
final String popupLocation = "table.popupLocation";
final JTable table = new JXTable(new AncientSwingTeam()) {
#Override
public Point getPopupLocation(MouseEvent event) {
// event may be null if triggered by keyboard, f.i.
// thanks to #Mad for the heads up!
((JComponent) event.getComponent()).putClientProperty(
popupLocation, event != null ? event.getPoint() : null);
return super.getPopupLocation(event);
}
};
JPopupMenu popup = new JPopupMenu();
Action printLocation = new AbstractAction("print cell") {
#Override
public void actionPerformed(ActionEvent e) {
Point p = (Point) table.getClientProperty(popupLocation);
if (p != null) { // popup triggered by mouse
int row = table.rowAtPoint(p);
int column = table.columnAtPoint(p);
LOG.info("" + table.getValueAt(row, column));
} else { // popup triggered otherwise
// could choose f.i. by leadRow/ColumnSelection
...
}
}
};
popup.add(printLocation);
table.setComponentPopupMenu(popup);
Edit (triggered by Mad's comment):
You should be checking MouseEvent.isPopupTrigger as the trigger point is platform dependent. This does mean you need to monitor mousePressed, mouseReleased and mouseClicked
No, that's not needed (just checked :-): the mechanism that shows the componentPopup in response to a mouseEvent - happens in BasicLookAndFeel.AWTEventHelper - only does so if it is a popupTrigger.
By reading the api doc (should have done yesterday ;-) again, it turns out that the method is called always before showing the componentPopup, that is also if triggered by other means, f.i. keyboard. In that case the event param is null - and the original code would blow. On the bright side, with that guarantee, all the logic of finding the target cell/s could be moved into that method. Didn't try though, so it might not be feasable (f.i. if then the location should be based on the leadRow/ColumnSelection that might not yet be fully handled at that time)