I need to write a Java Swing application that will run and act about the same on Linux, Windows and Mac. And for virtually all Widgets I create, I need to add some special handling, including, everything needs to be able to pop up a menu when asked. Even widgets like labels and buttons.
I'm able to catch menu requests by adding:
MouseListener m = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e)
{
if (e.isPopupTrigger())
{
onMenuSummons(); //do my thing
}
}
};
addMouseListener(m);
to the constructor on my extended widget.
But I'm concerned that this is a Linux-only solution. Do I only need to check isPopupEvent() in MousePressed? What about MouseClicked? I could hook both to be sure, but do I run a risk that some platform someday would have isPopupEvent() be true in both functions for the same mouse action?
Ideally MouseAdapter would spare me the question by offering an overridable popupRequest(MouseEvent e), but it doesn't. So what is the always right, works everywhere on all platforms and always will forever solution?
Also, some of the widgets I'm extending may have their own popup menus; I need to suppress those and implement my own. How do I make sure only my own menu is displayed? TIA.
Read the section from the Swing tutorial on Bringing up a Popup Menu for the basic of using menus and popup menus.
To write a MouseListener the basic code is:
class PopupListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(),
e.getX(), e.getY());
}
}
}
some of the widgets I'm extending may have their own popup menus; I need to suppress those and implement my own.
Also note the the example from the tutorial is older. The newer approach for adding a popup menu to a component is to use:
component.setComponentPopupMenu(....);
Not sure, but since this method only allows a single popup it may replace the default popup?
Related
I have a program that should issue some action when it gets activated after loosing focus to some other application before. I wrote a Focus-Listener to achieve this:
frame.addWindowFocusListener(new WindowFocusListener() {
#Override
public void windowGainedFocus(WindowEvent e) {
<do something when we gain focus>
}
#Override
public void windowLostFocus(WindowEvent e) {
<do something else when we lose focus>
}
});
Problem is, the gain-focus-action is also called when a modal dialog (for instance an authentication dialog) closes - the main window gains focus again. So I somehow would need to detect, if I gain focus from within my program or from the outside. Or, differently put, the focus action should not be located on the main window but on the application itself. What would be a simple way to do this?
Thanks #Aelop for helping me find an answer. e.getOppositeWindow() is null for windows of other applications, so I can neatly distinguish from where I'm coming from:
frame.addWindowFocusListener(new WindowFocusListener() {
#Override
public void windowGainedFocus(WindowEvent e) {
if (e.getOppositeWindow()==null) {
<do something when we gain focus>
}
}
#Override
public void windowLostFocus(WindowEvent e) {
<do something else when we lose focus>
}
});
if you didn't find a good solution use static variable when the focus is lost from a dialog in your application set a static boolean variable to true so when the focus is gained and the variable is true means that the focus is from some window in the application else the focus is from somewhere else i hope you get the idea try it if you didn't find some application or check the type of the source who lost the focus
I want to be able to receive input from the keyboard by the user but I've added everything I thought would allow my program to do this and still it does not work. What am I doing wrong?
class KeyInput implements KeyListener {
public void keyPressed(KeyEvent e) {
System.out.println("keyPressed");
}
public void keyReleased(KeyEvent e) {
System.out.println("keyReleased");
}
public void keyTyped(KeyEvent e) {
System.out.println("keyTyped");
}
}
public GameView() {
this.addKeyListener(new KeyInput());
}
The constructor works fine and KeyInput is an inner class of the GameView object.
When running the game, if I press a key nothing gets printed to the system output.
Am I missing something? Thanks!
KeyListener is fickle mistress, it wants a lot of attention all the time. Basically, it will only raise key events if the component it is registered to has focus AND is focusable.
Generally, you want to avoid using it and use key bindings API instead, How to Use Key Bindings, but this will depend on whether you MUST use pure AWT APIs or not....
I'm developing an application where I want something to be triggered both by the user updating the contents of a JTextArea, or manually via pressing a JButton.
I have done the first part using a DocumentListener and putting the relevant code in its insertUpdate method.
I haven't used Actions before, but I've heard they are useful for situations where you need something to be triggered by multiple controls. Is it possible to trigger the action from the DocumentListener? Is it a good idea to use Actions at all, or should I just put my code in a normal method?
(in the constructor):
textAreaInput.getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent e) {
// do something
}
public void removeUpdate(DocumentEvent e) {}
public void changedUpdate(DocumentEvent e) {}
});
and the Action, which is a field:
Action doSomething = new AbstractAction("Do Something!") {
#Override
public void actionPerformed(ActionEvent e) {
// do it
}
};
clarification:
The JTextArea will receive text that is pasted in by the user, which I want to parse automatically. The parsing depends on other values set elsewhere in the GUI; if the user changes these other values, he may want to re-parse the text, hence the need to perform the same action by pressing a button.
I want something to be triggered both by the user updating the contents of a JTextArea, or manually via pressing a JButton.
This doesn't make sense to me.
Why would clicking a button invoke the same Action as a user typing text into a text area?
I haven't used Actions before, but I've heard they are useful for situations where you need something to be triggered by multiple controls
That statement is meant for controls that the user clicks, like JMenuItems, JButtons or hitting Enter on a text field. In general they can be used when you use an ActionListner.
A DocumentListener is not an ActionListener so as I stated earlier the use of an Action doesn't seem appropriate.
I think you need to clarify your requirement.
Edit, based on clarification
if the user changes these other values, he may want to re-parse the text
Why does the user have a choice? If you change the font, text, foreground, background of a text area, the component it automatically repainting, you don't have to ask for this to be done. If you look at the code for these methods they always end up invoking the revalidate() and repaint() methods.
The parsing depends on other values set elsewhere in the GUI;
Sounds like you need a custom class. Maybe a ParsedTextArea or ParsedDocument. This class would contain the "properties" that can be set elsewhere in the GUI. It would implmenent the DocumentListener. It would also support your "parseTheText" method. So whenever a property is changed or a DocumentEvent is generated you automatically invoked the "parseTheText" method. This way you don't need a separate button and the component will always be in sync because the parsing is automatic.
You can invoke the actionPerformed() method, whether it's in an Action or not. There's an example here.
I think you need not create the Action object. You can add ActionListener to the Button just like you have added DocumentListener to the Document of the input. If I correctly understand your problem, may be you should do something like this:
textInput.getDocument().addDocumentListener(new DocumentListener(){
#Override
public void insertUpdate(DocumentEvent e) {
doIt();
}
#Override
public void removeUpdate(DocumentEvent e) {}
#Override
public void changedUpdate(DocumentEvent e) {}
});
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
doIt();
}
});
doIt() is a method in which you will do what you wanted to do.
I'm trying to listen for a window close event on the parent JFrame of a JPanel. In the WindowClosing event I'd like to de-register a listener to a different component.
Unfortunately the only code I can gaurantee to have run is the constructor for the panel. What this means is that the panel itself doesn't have an ancestor window yet, so simply calling SwingUtilities.getWindowAncestor doesn't work. So what I do is register a hierarchy listener, and in the hierarchyChanged event look for SHOWING_CHANGED event. When that even fires, now I can look for the window ancestor of the panel.
So basically I have the following:
class ShapeControlPanel extends JPanel{
public ShapeControlPanel(){
final ShapeControlPanel me = this;
me.addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
if((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) == HierarchyEvent.SHOWING_CHANGED){
SwingUtilities.getWindowAncestor(me).addWindowListener(new WindowListener() {
/* Snipped some empty handlers */
#Override
public void windowClosing(WindowEvent e) {
/* Finally get to remove the handler. */
me.getApparent().removeLocationSelectionListener(me.GUID(), me);
}
});
}
}
});
}
}
Is this sane? Is there a more reasonable way of getting a handle on the frame closing event?
It's not the ugliest thing I've seen (I wouldn't even say it's all that bad), but you have to ask yourself: why does your panel really need to know when the window is closed? It seems to be an odd coupling that would best be removed.
I don't know enough about your context and what you are truly trying to accomplish to suggest an alternative right now. But if a panel needs to know about the container in which it resides, there is probably some bad design with harmful coupling.
I'm currently trying to receive key events during a drag and drop, but it seems to me that the focus is taken away while dragging so that I can't listen to any key events.
I'm dragging a JComponent subclass that implements KeyListener and requests the focus in the DragSourceListener's dragEnter method, but my assumption is that the focus is taken away from it afterwards.
Now, who's got the focus and how can I take it away back to my JComponent. Or is there a different approach that is more suitable for dnd?
Thank you in advance.
UPDATE:
It's a lot of code necessary to make this work so I'm only going to post some snippets to show you what I'm trying to do:
public class Stone extends JComponent implements Serializable, KeyListener {
public Stone(...) {
//...
setFocusable(true);
addKeyListener(this);
this.dragSource = DragSource.getDefaultDragSource();
this.dgListener = new StoneDGListener();
this.dsListener = new StoneDSListener();
this.dragSource.createDefaultDragGestureRecognizer(
this,
DnDConstants.ACTION_MOVE,
this.dgListener
);
//...
}
//...
public void keyPressed(KeyEvent e) {
System.out.println("Stone: "+e.getKeyCode());
}
//...
public class StoneDSListener implements DragSourceListener, Serializable {
//...
#Override
public void dragEnter(DragSourceDragEvent dsde) {
//...
Stone.this.requestFocus();
addKeyListener(Stone.this);
}
//...
}
}
What happens is that before I'm dragging the Stone component my JPanel has the focus so it receives any keys I'm pressing.
During the drag I can't listen to any pressed keys(so I don't know who's got the focus) even though I'm requesting it when in dragEnter() and after I release the Stone any key events are send to the Stone.
It's probably not important for the question but to illustrate what I'm doing here's a screenshot:
image showing the "drag" http://img685.imageshack.us/img685/1884/pico.png
(Here I'm dragging the Stone from the collection below to the game field on the top). In this state I don't know how to find out what keys are pressed. I need to figure this out in order to be able to rotate the Stone.
Not sure who has focus during a drag and drop. But an alternative solution to your problem would be to add a KeyEventDispatcher for your Stone class to the KeyboardFocusManager. From the JavaDoc:
The KeyboardFocusManager is both a centralized location for client code to query for the focus owner and initiate focus changes, and an event dispatcher for all FocusEvents, WindowEvents related to focus, and KeyEvents+.
+ my emphasis.
Basically we use similar sort of code to intercept KeyEvents before they hit the Component that has focus.
Just gave it a quick test for your particular drag and drop context and it seems to work alright (as long as your application has focus within the operating system). Essentially something along the lines of:
Public Stone(...) {
// ...
KeyboardFocusManager fm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
fm.addKeyEventDispatcher(
new KeyEventDispatcher() {
public boolean dispatchKeyEvent(KeyEvent e) {
System.out.println("Key Press: " + e.getKeyChar());
return false;
}
}
);
// ...
}
You will need to do a bit of leg-work on enabling and disabling when the user is no longer dragging and dropping as my test currently prints all the time.
I also wonder if it is possible to use the KeyboardFocusManager to determine who actually ends up with focus during a drag and drop?
Anyway, I hope this gives you a few new ideas to try.