Pushing a button (from far away) - java

So what I am doing is, I am checking if the player right clicks air using the PlayerInteractEvent, then I am getting the player's target block and saving it as a variable named "block".
This all is working fine but what i want to do with this is check if that block is a button and if so "push" it, but it isn't doing that right.
I have tried casting the block to org.bukkit.material.Button after checking if its type was indeed a button, then I set it to powered like so:
((Button) block.getState().getData()).setPowered(true);, but that didn't do anything so I then tried: block.setData((byte) (block.getData() | 0x8)));, and that did turn it on but it stayed on.
So how do I do this correctly?
Here is the full code:
#SuppressWarnings("deprecation")
#EventHandler
public void onPlayerInteraction(PlayerInteractEvent e) {
if (e.getAction().equals(Action.RIGHT_CLICK_AIR)) {
Block block = e.getPlayer().getTargetBlock((Set<Material>) null, 200);
if (block.getType().equals(Material.STONE_BUTTON) || block.getType().equals(Material.WOOD_BUTTON)) {
block.setData((byte) (block.getData() | 0x8));
}
}
}
PS: I am using the Spigot 1.12.2 API.

First of all, try to trigger RIGHT_CLICK_BLOCK action, because Button is a block. Secondly, event called before you push the button, so if you want to change the state of buttom after player clicked, you should run task later to change it a bit later after event passed.
Also, you don't need to check hand item type point to null. If ItemStack is not null, Material will never be null, and in case if ItemStack will be null, you will catch NPE, trying to get Material. And in versions 1.9 and above you might also check EquipmentSlot, because PlayerInteractEvent calls twice (as hand and offhand).
ItemStack item = e.getPlayer().getInventory().getItemInMainHand();
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getHand() == EquipmentSlot.HAND
&& (item == null || item.getType() == Material.AIR)) {
// Your code
}
To change button state in new way, get MaterialData from the block state, cast it to Button, change power state, write all back to block and update:
Button button = (Button) block.getState().getData();
button.setPowered(true);
block.getState().setData(button);
block.getState().update();
Furthermore, you don't really need to check facing at block, if Action is RIGHT_CLICK_BLOCK, then you can get it directly from event.
Block block = e.getClickedBlock();
So, I think, the final code may looks like that:
Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin("YourPluginName");
ItemStack item = e.getPlayer().getInventory().getItemInMainHand();
if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK) && e.getHand() == EquipmentSlot.HAND
&& (item == null || item.getType() == Material.AIR)) {
Block block = e.getClickedBlock();
Bukkit.getServer().getScheduler().runTaskLater(plugin, new Runnable() {
public void run() {
Button button = (Button) block.getState().getData();
button.setPowered(true);
block.getState().setData(button);
block.getState().update();
}
}, 2L);
}

Related

Is there a way to update a variable outside a method that is used as a parameter in said method?

I have 62 check boxes on 2 different panels.
30 of them on each panel are for selecting PC names while 1 on each selects all.
So on each of those 60 that are just selecting items I have this code:
private void HP04ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
setComponent(HP04, blrS,all_blr);
HP04 is the name of a checkbox for one of the PCs, blrS is the name of an int that counts how many checkboxes are selected(there is another int for the boxes on the other panel), all_blr is the name of the checkbox that selects all on the blr panel.
This is the method I used:
public void setComponent(javax.swing.JCheckBox component, int room,javax.swing.JCheckBox all)
{
System.out.println("Inside Component: "+component.isSelected()); //testing to see if the method is actually being called
if (component.isSelected()){ //if checkbox is checked
room++; //records how many boxes are checked
if (room == 30)
{
all.setSelected(true);//autochecks "all" check box if all PCs are selected
}
if (!messagearea.getText().equals(""))
{
sendb.setEnabled(true);//if any boxes are checked and the message area is populated, enable the button
}
}
else
{
room--;//records how many boxes are checked
all.setSelected(false);//unchecks the "all" check box if not all of them are checked
if (room == 0)
{
sendb.setEnabled(false);//disables button if no PCs are selected to be messaged
}
}
System.out.println(room); //testing int updates based on UI changes
}
Now the problem is room is only fetching the value of whatever variable is put in that parameter and then room itself is being modified. So in the example setComponent(HP04, blrS,all_blr); blrS is never changed.
I know the code inside the method works, it worked before I decided to cut down on code by using a method, as before I had the code inside the method in the actionperformed for every checkbox before hand... Like this:
private void HP04ActionPerformed(java.awt.event.ActionEvent evt) {
if (HP04.isSelected()){
blrS++;
if (blrS == 30)
{
all_blr.setSelected(true);
}
if (!messagearea.getText().equals(""))
{
sendb.setEnabled(true);
}
}
else
{
blrS--;
all_blr.setSelected(false);
if (blrS == 0)
{
sendb.setEnabled(false);
}
}
System.out.println(blrS);
}
It's just understanding how parameters and methods works that is tripping me up.
It feels obvious that what I'm doing shouldn't work, room is a variable inside the method... But how do I change that?
Solution found:
"Why not return room from the method, and assign it to the variable when you call it? blrS = setComponent(HP04, blrS, all_blr);"
– Rob Spoor (in the comments to this post)

JPanel not visible

I've been having a problem with my program that has been driving me crazy and I cannot understand why this is happening. I have a GUI that when the "Add" button is pressed, a new system listing appears (Which is a class called SystemPanel that extends JPanel and cotains system details, which is created and then put into the Frame's main panel.)
To put it shortly, when I try to add a new SystemPanel, it does not appear for whatever reason. I have code using JSch that connects to the system and verifies whether its processes are online or not, but the line of code that does this is after the creation of the SystemPanel. It is only after the code for testing the processes of the system are executed that the SystemPanel becomes visible, and I can't understand why this is the case. Here is the code for adding a new SystemPanel:
public void actionPerformed(ActionEvent e) {
//If the "Add" button is pressed
if (e.getActionCommand() == "Add") {
PopupWindow popup = new PopupWindow(this);
popup.setVisible(true);
String[] results = popup.getResults();
if (results[0] != null && results[1] != null && results[2] != null && results[3] != null && results[4] != null) {
SystemPanel newSystem = new SystemPanel(this, results[0], results[1], results[2], results[3], results[4]);
systemsPanel.add(newSystem);
revalidate();
systemsList.add(newSystem);
System.out.println("Did the stuff");
boolean[] status = SystemChecker.checkOnline(results[0], results[1], results[2], results[3]);
}
}
}
The PopupWindow is a custom JDialog that allows the user to enter the required information which is returned in a String array and is used to create a new SystemPanel. The checkOnline function grabs the user's inputs and uses them to connect to the system and determine whether the processes are working or not, and returns the results into a boolean array, true for working, false for not.
What's even weirder is that I have another part of my program that reads from an .ini file to obtain existing systems and then creates SystemPanels based on the data that it reads. Through this method, the SystemPanels are added the way I want and work perfectly for some reason, even though the code for adding the panels is hardly any different. Code:
for (int i = 0; i < systems.size(); i++) {
SystemPanel newSystem = new SystemPanel(this, systems.get(i)[0], systems.get(i)[1], systems.get(i)[2], systems.get(i)[3], systems.get(i)[4]);
systemsPanel.add(newSystem);
revalidate();
systemsList.add(newSystem);
}
for (int i = 0; i < lineNum; i++) {
boolean[] status = SystemChecker.checkOnline(systems.get(i)[0], systems.get(i)[1], systems.get(i)[2], systems.get(i)[3]);
systemsList.get(i).updateIcons(status);
}
This code grabs the details from the file and then makes the SystemPanels based on those details. Here, all of the SystemPanels are added and show up before the connection is tested, which is what I want to happen when I add one normally.
Why is it that the SystemPanel doesn't appear until the connection is tested, even though the code for displaying the SystemPanel is executed before the connection test? Any help would be greatly appreciated, thanks.
Try it of the current event queue handling, on which actionPerformed is done.
public void actionPerformed(ActionEvent e) {
EventQueue.invokeLater(() -> { ... your code here ... });
}
Also you cannot add the same component to two parents, every component object has a single parent (container).
(Java 8 notation)

JavaFX Dialog returns same result over and over again

Solved: I figured that it isn't a bug of JavaFX, it is made like that on purpose, so you can request the result multiple times from the dialog if you don't wanna save it into a variable. To flush the cache i used setResult(null) on the dialog. That did the job for me :)
I've created a Dialog to edit/create a Person using JavaFX. But i guess i found a bug. I've added two ButtonType-objects to my dialog. One for saving and one for aborting the actions. When i use those buttons it works just fine. But if i press the 'X' to close the dialog-window, the dialog automaticly returns the last result again. That means if i aborted my last action and in my current one i press 'X' to close the window, the dialog returns no result since the abort-button didn't have one last time. But if i pressed the save-button on my last action and i press 'X' in my current one it returns the same person again, since save-button had this person in its result last time. How can i make the dialog changing to no result on closing?
Here is the action i've created:
this.createPersonAction = new Callback<ButtonType, PersonSession>() {
#Override
public PersonSession call(final ButtonType param) {
if (param.equals(PersonDialogController.this.saveButton)) {
final String firstName = PersonDialogController.this.firstNameField.getText();
final String lastName = PersonDialogController.this.lastNameField.getText();
final Person p = BeanFactory.createPerson(firstName, lastName);
if (p != null) {
return new PersonSession(p);
}
}
return null;
}
};
And here you have my two ButtonType-Objects:
private final ButtonType saveButton = new ButtonType(GuiStringRresource.LABEL_SAVE_BUTTON, ButtonData.OK_DONE);
private final ButtonType abortButton = new ButtonType(GuiStringRresource.LABEL_ABORT_BUTTON,
ButtonData.CANCEL_CLOSE);
Use the Dialog.setOnCloseRequest method to handle this case.

UndoableEditListener never called in JTextPane/JTextArea

I am trying to attach an UndoableEditListener to a JTextPane or JTextArea that queues up edits into an UndoManager.
textPane.getDocument().addUndoableEditListener(new UndoableEditListener() {
#Override
public void undoableEditHappened(UndoableEditEvent event) {
undoQueue.addEdit(event.getEdit());
}
});
But undoableEditHappened is never called when I type "aaa" in the text window.
Thinking it's Java's fault, not mine, I crack AbstractDocument.class open with Eclipse debugger to watch the event trigger. It has a private listeners array. AbstractDocument stores all its listeners in odd indices in the listeners array, with the listeners' type Class<>'s in the even indices.
protected void fireUndoableEditUpdate(UndoableEditEvent e) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == UndoableEditListener.class) {
// Lazily create the event:
// if (e == null)
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
((UndoableEditListener) listeners[i + 1]).undoableEditHappened(e);
}
}
}
See the line if (listeners[i] == UndoableEditListener.class)? When I add the undo change listener, the debugger shows listeners containing my listener, along with UndoableEditListener.class in the index before it. But, when the debugger comes to that if-statement, all the even indices in the array listeners show as DocumentListener.class in the debugger. Consequently, the if-statement is always false and the listener never called.
What the heck? Is this a Java 8 bug? Or am I missing a step the examples forgot to mention?
The problem was in the JTextPane. I was overriding its setText method to force it to call read, the alternative to setText that normalizes all kinds of newline while remembering them. But JTextPane.read appears to not trigger an UndoableEditEvent on the document.
If I leave setText alone, then UndoManager.undo works.

How to provide feedback to the user than the button has been pressed while UI thread goes about its business

I have a JButton which launches a JFileChooser. However the JFileChooser often takes a few seconds to come up, during which time the user might think that nothing is happening. I tried to make the button become disabled until the JFileChooser is finished with, but the disabling of the button doesn't even happen until the JFileChooser is loaded. Is there something I can do?
My code:
public void actionPerformed(ActionEvent e) {
System.err.println("clicked");
((JButton) e.getSource()).setEnabled(false);
System.err.println("set");
JFileChooser b = new JFileChooser("C:\\");
b.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int res = b.showOpenDialog((Component) e.getSource());
if (res == JFileChooser.APPROVE_OPTION) {
try {
//Blah
}
catch (Exception err) {
JDialog j = new JDialog(window, "An error occured:\n" + err.getMessage());
}
}
((JButton) e.getSource()).setEnabled(true);
}
Move these lines..
JFileChooser b = new JFileChooser("C:\\");
b.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
..from the action performed method to the constructor of the action listener, change them to..
b = new JFileChooser("C:\\");
b.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
..then declare..
private JFileChooser b;
..as a class attribute (so it is visible to the action performed method).
You might also want to give it a better name.
The chooser will be constructed when the class is created, and be ready for use when needed. This has the added benefit that the chooser will remember the position, size, path & file display type, the subsequent times the user activates the button.
Yet another strategy is to declare the chooser as a class attribute, don't instantiate it in the constructor, but check in the action performed if it is null, and if so, create and configure it.
Continued..
I might want to do something a bit slower like this at some point, that doesn't quite deserve its own thread.
With the last strategy I outlined, I was considering adding something that I will add now.
..Continued
..Of course, that last method will give exactly the same problem you describe, but just once when the user 1st clicks the button. For that situation, you should probably be looking to pop a JOptionPane with an indeterminate JProgressBar from inside a SwingWorker.
Obviously, a SwingWorker creates a new Thread, but OTOH, Thread objects in Java are cheap. There are a number of them running for any app. with a GUI. A couple more will not hurt.

Categories