Listen to the paste events JTextArea - java

I want to call a function when the user pastes text in my JTextArea. Is there any event generated when the text is pasted to the JTextArea and which listener can I use to trigger my function on this event?

One possible solution (and I hope some one has a better one) would be to replace the key binding Action responsible for actually performing the paste operation.
Now, before you do this, the default paste operation is not trivial, instead, I would replace the default paste Action with a proxy, which could call the original, but would allow you to intercept the operation, but not have to re-implement the functionality yourself, for example...
public class ProxyAction extends AbstractAction {
private Action action;
public ProxyAction(Action action) {
this.action = action;
}
#Override
public void actionPerformed(ActionEvent e) {
action.actionPerformed(e);
System.out.println("Paste Occured...");
}
}
Then you would simply need to look up the default Action and replace it...
JTextArea ta = new JTextArea(10, 10);
Action action = ta.getActionMap().get("paste-from-clipboard");
ta.getActionMap().put("paste-from-clipboard", new ProxyAction(action));
The problem here is, this won't tell you if the operation failed or succeeded or what was actually pasted. For that, you could use a DocumentListener, registered before you call the default Action which could record the changes to the document. Obviously, you'd want to deregister this after the default action ;)...
Now, equally, you could just override the paste method of the JTextArea, which equates to about the same thing, but, the first option would be more portable...
As an idea...
Take a look at How to Use Actions and How to Use Key Bindings for more details

you can have something like below, whenever you paste something in the textarea, then 'Pasted!' is printed out on your console. It prints only on paste !
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
public class TextAreaDemo extends JFrame {
JTextArea _resultArea = new JTextArea(6, 20);
public TextAreaDemo() {
_resultArea.setText("");
JScrollPane scrollingArea = new JScrollPane(_resultArea);
JPanel content = new JPanel();
content.setLayout(new BorderLayout());
content.add(scrollingArea, BorderLayout.CENTER);
this.setContentPane(content);
this.setTitle("TextAreaDemo B");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
_resultArea.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if ((e.getKeyCode() == KeyEvent.VK_V) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
System.out.println("Pasted!");
}
}
#Override
public void keyReleased(KeyEvent e) {
}
});
}
public static void main(String[] args) {
JFrame win = new TextAreaDemo();
win.setVisible(true);
}
}

You can also check out Wrapping Actions which is basically the same suggestion as MadProgrammer except that the WrapperAction will delegate all the methods of the Action to the original Action. This will allow you to pick up the text and Icons associated with the original Action in case you ever want to add your custom Action to a JMenuItem or JButton.

Related

.add() in java AWT not working after actionPerformed

I am fairly new to coding and have encountered this issue within my code.
I create a button using the Java AWT import. I then check for a response using a while loop and wish to create another button after, however .add() seems to no longer function.
import java.awt.*;
import java.awt.event.*;
public class Main1
{
public static void main(String[] args)
{
Frame f = new Frame();
f.setSize(500, 500);
f.setVisible(true);
ButtonPanel bp = new ButtonPanel(f);
bp.x = null;
while (bp.x == null)
{
}
System.out.println(bp.x);
//THE ISSUE- THIS WILL NOT APPEAR AFTER BUTTON PRESS
f.add("South", new Button("REEE"));
}
}
class ButtonPanel extends Panel implements ActionListener
{
volatile String x;
public ButtonPanel(Frame f)
{
Button b = new Button("Hi");
b.addActionListener(this);
f.add("North", b);
}
public void actionPerformed(ActionEvent e)
{
x = e.getActionCommand();
}
}
I have been trying solutions for this for the last day or so and nothing seems to be working. I've seen in other posts people have said to use Wait/Notify however I am not too sure how those work and I would like to know explicitly what is going wrong in my program (though I am still open to using Wait/Notify in my solution).
Any help would be appreciated, thank you very much
So, they're a number of issues at play here.
The first is the fact that layout managers are generally lazy. This means that you can add and/or remove a number of components quickly and then do a single layout and paint pass.
To do this, you need to revalidate the Container which was updated.
Next, AWT (and Swing by extension) is based on Model-View-Controller concept, one aspect of this is the "observer pattern". This is basically a callback concept that allows you to be notified when something of interest happens.
Button makes use of an ActionListener to generate events when the button is "actioned". This is the "observer pattern" in action.
Why is this important? You really want to think about what information is needed to be passed where and who's actually responsible for doing what.
For example, is it really the ButtonPanel's responsibility to update the frame? Is giving ButtonPanel unfettered control over the frame really a good idea?
Instead, ButtonPanel "should" be providing some kind of notification when some action has occurred and then any interested parties should be able to do what ever they need to.
As a "basic" example...
import java.awt.Button;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventListener;
import javax.swing.event.EventListenerList;
public class Test {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
public Test() {
Frame f = new Frame();
f.setPreferredSize(new Dimension(500, 500));
f.pack();
ButtonPanel bp = new ButtonPanel(f);
bp.addObsever(new Observer() {
#Override
public void hiWasPerformed() {
f.add("South", new Button("REEE"));
f.revalidate();
}
});
f.setVisible(true);
}
public interface Observer extends EventListener {
public void hiWasPerformed();
}
class ButtonPanel extends Panel {
private EventListenerList eventListener = new EventListenerList();
public ButtonPanel(Frame f) {
Button b = new Button("Hi");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Observer[] listeners = eventListener.getListeners(Observer.class);
for (Observer observer : listeners) {
observer.hiWasPerformed();
}
}
});
f.add("North", b);
}
public void addObsever(Observer observer) {
eventListener.add(Observer.class, observer);
}
public void removeObsever(Observer observer) {
eventListener.remove(Observer.class, observer);
}
}
}
It looks like nothing is happening because the buttons are being added below the bottom of the window. You should consider using a layout manager to solve this issue.
However, in the meantime the simple solution is to move this line f.add("South", new Button("REEE")); inside the action event and to make use of Frame.pack();:
public class Main1
{
public static void main(String[] args)
{
Frame f = new Frame();
//set minimums rather than a fixed size
f.setMinimumSize(new Dimension(500, 500));
f.setVisible(true);
CustomButton b = new CustomButton(f);
//Add this line to update/size/show the UI
f.pack();
//Don't place any more code inside the main method. Future events should be triggered by interacting with the UI/buttons
}
}
Then for the button we don't need to extend Panel, we can do something like this:
class CustomButton implements ActionListener
{
Frame parentFrame;
public CustomButton(Frame f)
{
parentFrame = f;
Button b = new Button("Hi");
b.addActionListener(this);
f.add("North", b);
}
public void actionPerformed(ActionEvent e)
{
//Add button here instead of the main class
parentFrame.add("South", new Button("REEE"));
//The buttons are being added below the bottom of your window, this will force them to be shown.
//Using a layout manager will solve this ploblem and you will not need to do this:
parentFrame.pack();
}
}
Note: clicking on the "Hi" button multiple times will have interesting results of the "REEE" buttons overlapping or doing odd things if you resize the window.

Get all existing JTextField components in a window and add popup menu to each?

I need to get all existing TextFields in an app window so that I can be able to automatically add popup menus to all of them. How do you do that?
The code is below. How come when I call:
JTextFieldRegularPopupMenu.addToAll(jpanel) it works fine and affects all relevant JTextFields. But when I do it with a JDialog from a regular JDialog netbeans class - JTextFieldRegularPopupMenu.addToAll(this), it doesn't work. What could be the problem?
import java.awt.Component;
import java.awt.Container;
import javax.swing.*;
import java.awt.event.ActionEvent;
import javax.swing.JPopupMenu;
import javax.swing.undo.*;
public class JTextFieldRegularPopupMenu {
public static void addToAll(Container frm) {
JTextField txtTmp = null;
Component[] components = frm.getComponents();
for(int i=0;i<components.length;i++){
if( components[i] instanceof JTextField ){
txtTmp = (JTextField)components[i];
addTo(txtTmp);
}
}
}
public static void addTo(JTextField txtField)
{
JPopupMenu popup = new JPopupMenu();
UndoManager undoManager = new UndoManager();
txtField.getDocument().addUndoableEditListener(undoManager);
Action undoAction = new AbstractAction("Undo") {
#Override
public void actionPerformed(ActionEvent ae) {
if (undoManager.canUndo()) {
undoManager.undo();
}
else {
// No Undo
}
}
};
Action copyAction = new AbstractAction("Copy") {
#Override
public void actionPerformed(ActionEvent ae) {
txtField.copy();
}
};
Action cutAction = new AbstractAction("Cut") {
#Override
public void actionPerformed(ActionEvent ae) {
txtField.cut();
}
};
Action pasteAction = new AbstractAction("Paste") {
#Override
public void actionPerformed(ActionEvent ae) {
txtField.paste();
}
};
Action selectAllAction = new AbstractAction("Select All") {
#Override
public void actionPerformed(ActionEvent ae) {
txtField.selectAll();
}
};
cutAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control X"));
copyAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control C"));
pasteAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control V"));
selectAllAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("control A"));
popup.add (undoAction);
popup.addSeparator();
popup.add (cutAction);
popup.add (copyAction);
popup.add (pasteAction);
popup.addSeparator();
popup.add (selectAllAction);
txtField.setComponentPopupMenu(popup);
}
}
Netbeans JFrame Class
public class FrmAddNewUser extends javax.swing.JDialog {
/**
* Creates new form FrmAddNewUser
*/
public FrmAddNewUser(java.awt.Frame parent, boolean modal) {
super(parent, modal);
initComponents();
myInitComponents();
}
private void myInitComponents()
{
JTextFieldRegularPopupMenu.AddToAll(this); //this doesn't work... but works on jpanel objects..
}
It sounds like you are trying make a uniform change to all existing JTextFields, correct? This really depends, but storing all of the objects in a data structure like an ArrayList might be your best bet. Then use a for loop to apply the same change at each index.
Well you can't really do that in advance because windows and components are not all created at the start of an application. So you would need to manage this dynamically as an application creates and displays a window.
One way might be to use a KeyboardFocusManager to listen for focus changes.
When focus changes you can then invoke the getPopupMenuComponent() method to get the popup menu of the component. If the menu is null, then you need to add your popup menu.
Check out Global Event Listeners for a simple example using this concepts that shows how to select all the text when a text field gains focus.
So you would need to modify all your applications to add this listener when you start the application.
Edit:
Using your current approach you would want to pass in a Window object to a method. Then you can use the getContentPane() method to get the contain holding the components.
Then you can use a class like Swing Utils. This will do a recursive search of the content pane to find all components. Then you iterate through the List and add your logic.
Then this code will work for both frames, dialogs etc.
Also, you don't need to always create custom Actions. You can use Actions from the DefaultEditorKit. For example:
JMenuItem copy = new JMenuItem( new DefaultEditorKit.CopyAction() );
The Action can be shared by all menu items.
Or you can look up the default Action from the ActionMap of the text field. See Key Bindings for the action name to use for the lookup. It will also show you the default key binding used for the Action.

How to close JInternalFrame by pressing escape key?

I created one JFrame with JDesktopPane, in which I am calling JInternalFrame. Now I want to close that internal frame by pressing escape key.
I tried 2-3 ways, but no output.
I did that by using code given below:
public static void closeWindow(JInternalFrame ji){
ActionListener close=New ActionListener(){
public void actionPerformed(ActionEvent e){
ji.dispose();
}
};
When I called above method from my intern frame class constructor by supplying its object , I was able to close it. But when there I write some other lines of code to the constructor. The above method call doesn't work. Please help me. I unable to find the problem in the code.
Also I tried to add KeyListener to internal frame, so I able to work with key strokes,but it also doesn't work.
Again I tried to setMnemonic to button as escape as below:
jButton1.setMnemonic(KeyEvent.VK_ESCAPE);
But also gives no output.
You need to implement the KeyListener interface, or add one that is Anonymous. In this example, I just implemented it.
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
public class JInternalFrame extends JFrame implements KeyListener {
public JInternalFrame()
{
super();
// other stuff to add to frame
this.setSize(400, 400);
this.setVisible(true);
this.addKeyListener( this );
}
#Override
public void keyTyped(KeyEvent e) {
// Don't need to implement this
}
#Override
public void keyPressed(KeyEvent e) {
if( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
System.exit(0); //Change this to dispose or whatever you want to do with the frame
}
}
#Override
public void keyReleased(KeyEvent e) {
//Dont need to implement anything here
}
public static void main(String[] args)
{
JInternalFrame frame = new JInternalFrame();
}
}
Now if this is an internal jframe as mentioned, it is probably better to implement the keylistener in the JDesktopPane and call the dispose method on the JInternalFrame after pressing escape instead of implementing keylistener in this frame. It all depends on which GUI component has focus of input.
This issue is old now but I recently got stuck on a similar problem. Adding the key listener to the content pane of the internal frame instead of the internal frame itself did the job for me.
this.getContentPane().addKeyListener(this);

Disable a Key Entirely Temporarily with Corresponding Button Java

I have a program that, put in short, advances upon the pressing of a button. During certain execution phases the button is temporarily deactivated to prevent it from firing in code at the wrong point in time. I have now created some Key Bindings to act as shortcuts for the pressing of the buttons, but need to disable them during the same aforementioned times, or else they will cause my array to be trashed and wiped before I even use it.
Any tips, methods, or Java methods I can use to [very] easily but a hold via disablement?
Have the bound key press the JButton with doClick(). Then when the button needs to be deactivated, call setEnabled(false) on the button.
As an aside, I suppose your button and key binding could share the same action, but I don't know if calling setEnabled(false) on the Action will prevent the key from running the Action's actionPerformed method. Time to test.... Be right back...
Edit: yep you can just have the JButton and the bound key share the same Action that is enabled/disabled:
import java.awt.event.*;
import javax.swing.*;
public class TestBoundAbstractActions {
public static void main(String[] args) {
final MyAction myAction = new MyAction();
final JButton actionButton = new JButton(myAction);
JRadioButton enableRadioButton = new JRadioButton("Enabled", true);
enableRadioButton.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
myAction.setEnabled(e.getStateChange() == ItemEvent.SELECTED);
}
});
JPanel panel = new JPanel();
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
String mKey = "m key";
panel.getInputMap(condition).put(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0), mKey);
panel.getActionMap().put(mKey, myAction);
panel.add(new JLabel("Press \"m\" to activate key-bound action"));
panel.add(actionButton);
panel.add(enableRadioButton);
JOptionPane.showMessageDialog(null, panel);
}
}
class MyAction extends AbstractAction {
public MyAction() {
super("My Action");
putValue(MNEMONIC_KEY, KeyEvent.VK_M);
}
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("boo!");
}
}

Scrolling on a JComboBox popup hide it

My client is complaining that JComboBox popups often close when the scroll is being used over a JComboBox popup with no vertical scrollbar. (He seems to accidently use scrolling over it because he is using an Apple Magic Mouse.)
Any way to prevent this to happen ?
I know it has to do with the ComboBoxUI, but I would like a few pointer where to start. BasicComboPopup.handler is private (not reusable) and I don't see any code relative to any a MouseWhellListener in BasicComboPopup.
As seen in the source, BasicPopupMenuUI contains a nested class, MouseGrabber, that implements the AWTEventListener interface. The receipt of MouseEvent.MOUSE_WHEEL in eventDispatched() cancels the popup as a function of isInPopup(). I know of no simple way to defeat the behavior.
Empirically, this example invokes show() from the actionPerformed() handler of a JButton; mouse wheel events are ignored. This might be a reasonable alternative for your user, perhaps combined with a suitable ActionEvent modifier mask.
In contrast, this example invokes show() in response to isPopupTrigger() in a MouseAdapter; as expected, mouse wheel events cancel the popup.
Thanks to your suggestion, I've got an idea an found a solution by hacking AWTEventListeners.
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener()
{
public void eventDispatched(AWTEvent event)
{
if (event instanceof MouseWheelEvent)
{
Object source = event.getSource();
if ((source instanceof JScrollPane) &&
(((JScrollPane) source).getParent().getClass().
getName().equals("com.apple.laf.AquaComboBoxPopup")))
{
JViewport viewport = ((JScrollPane) source).getViewport();
if (viewport.getViewSize().height <= viewport.getHeight())
// prevent consuming if there is a vertical scrollbar
((MouseWheelEvent) event).consume();
}
}
}
}, AWTEvent.MOUSE_WHEEL_EVENT_MASK);
Thanks guys !
I have tested default behaviour of a combobox. And when I am scrolling over the popup it is fine it will not close it. But when I scroll outside it or even over the combobox itself then it disappears.
I do not know if you are after something like this but I have added the mouse wheel listener to the combobox this way if I detect the movement over the combobox there I am reshowing the popup. -- This bit only partially solves the issue that the mouse wheeling will not show the combo box when scrolling over the combobox.
import java.awt.HeadlessException;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ComboBoxMouseWheel
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createGUI();
}
});
}
private static void createGUI() throws HeadlessException
{
String[] items = new String[]
{
"oasoas", "saas", "saasas"
};
final JComboBox jcb = new JComboBox(items);
jcb.addMouseWheelListener(new MouseWheelListener()
{
#Override
public void mouseWheelMoved(MouseWheelEvent e)
{
System.out.println("ohjasajs");
e.consume();
jcb.showPopup();
}
});
JPanel p = new JPanel();
p.add(jcb);
JPanel contentPane = new JPanel();
contentPane.add(p);
JFrame f = new JFrame();
f.setContentPane(contentPane);
f.setSize(300, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
I hope this is helpful even a bit. If you manage to solve other way please do share it with us.
The solution provided by #trashgod seems doable but it looks so elaborated :), thus I propose mine approach an alternative.
Good luck, Boro.
Here is a solution that will work in most cases
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
if (event instanceof MouseWheelEvent) {
Object source = event.getSource();
if (source instanceof JScrollPane) {
JScrollPane scroll = (JScrollPane) source;
if (scroll.getName().equals("ComboBox.scrollPane")) {
MouseWheelEvent sourceEvent = ((MouseWheelEvent) event);
for (MouseWheelListener listener : scroll.getListeners(MouseWheelListener.class)) {
listener.mouseWheelMoved(sourceEvent);
}
sourceEvent.consume();
}
}
}
}
}, AWTEvent.MOUSE_WHEEL_EVENT_MASK);

Categories