I am making a text editor using Java swing. I am using JTextArea for the same. I want to know how I can use Undo and Redo functionality in JTextArea as I am not able to use it.
As I understand it, JTextArea has no inherent Undo/Redo functionality built in, but a Google search did find this article which might be helpful.
There apparently exists an Undo Manager in javax.swing which you can hook up to the JTextArea's change events.
You can do like this
UndoManager manager = new UndoManager();
textArea.getDocument().addUndoableEditListener(manager);
Once the manager is attached to the document of the JTextArea, it will monitor all changes
to the contents of the text area.
After attaching the manager to the text component, you must provide some means to tell
the manager to undo/redo an operation.
Call the public void undo() and public void redo() method of the UndoManager where necessary(Eg. actionPerformed() method of an actionlistener)
You can attach Action objects to a button in the following way instead of calling undo() and redo() methods which simplifies the task:
JButton undoButton = new JButton(UndoManagerHelper.getUndoAction(manager));
JButton redoButton = new JButton(UndoManagerHelper.getRedoAction(manager));
Its been a while since I did this and I dont recall the details, but here is a link with some info: http://java.sun.com/docs/books/tutorial/uiswing/components/generaltext.html
Scroll down to the section titled "Listening for Changes on a Document" to get started.
I created a simple class that can assign undo functionality to a JTextcomponent (JTextField, JTextArea, etc.) with a single method call:
UndoTool.addUndoFunctionality(area);
or alternatively construct a new JTextArea with the undo functionality pre-assigned:
UndoTool.createJTextFieldWithUndo();
Here is the implementation of the utility class:
public class UndoTool {
private static final String REDO_KEY = "redo";
private static final String UNDO_KEY = "undo";
private JTextComponent component;
private KeyStroke undo = KeyStroke.getKeyStroke("control Z");
private KeyStroke redo = KeyStroke.getKeyStroke("control Y");
public UndoTool(JTextComponent component) {
this.component = component;
}
public void setUndo(KeyStroke undo) {
this.undo = undo;
}
public void setRedo(KeyStroke redo) {
this.redo = redo;
}
public static void addUndoFunctionality(JTextComponent component) {
UndoTool tool = new UndoTool(component);
UndoManager undo = tool.createAndBindUndoManager();
tool.bindUndo(undo);
tool.bindRedo(undo);
}
public static JTextArea createJTextAreaWithUndo() {
JTextArea area = new JTextArea();
addUndoFunctionality(area);
return area;
}
public static JTextField createJTextFieldWithUndo() {
JTextField field = new JTextField();
addUndoFunctionality(field);
return field;
}
public UndoManager createAndBindUndoManager() {
Check.notNull(component);
UndoManager manager = new UndoManager();
Document document = component.getDocument();
document.addUndoableEditListener(event -> manager.addEdit(event.getEdit()));
return manager;
}
public void bindRedo(UndoManager manager) {
component.getActionMap().put(REDO_KEY, new AbstractAction(REDO_KEY) {
#Override
public void actionPerformed(ActionEvent evt) {
try {
if (manager.canRedo()) {
manager.redo();
}
} catch (CannotRedoException ignore) {
}
}
});
component.getInputMap().put(redo, REDO_KEY);
}
public void bindUndo(UndoManager manager) {
component.getActionMap().put(UNDO_KEY, new AbstractAction(UNDO_KEY) {
#Override
public void actionPerformed(ActionEvent evt) {
try {
if (manager.canUndo()) {
manager.undo();
}
} catch (CannotUndoException ignore) {
}
}
});
component.getInputMap().put(undo, UNDO_KEY);
}
}
I had to go through multiple links just to get enough help. I'm adding here what I implemented successfully just to help future visitors. I implemented this using JTextPane but am assuming the same would apply for the JTextArea
JTextArea textArea = new JTextArea();
JButton undo = new JButton("Undo");
JButton redo = new JButton("Redo");
KeyStroke undoKeyStroke = KeyStroke.getKeyStroke(
KeyEvent.VK_Z, Event.CTRL_MASK);
KeyStroke redoKeyStroke = KeyStroke.getKeyStroke(
KeyEvent.VK_Y, Event.CTRL_MASK);
UndoManager undoManager = new UndoManager();
Document document = textArea.getDocument();
document.addUndoableEditListener(new UndoableEditListener() {
#Override
public void undoableEditHappened(UndoableEditEvent e) {
undoManager.addEdit(e.getEdit());
}
});
// Add ActionListeners
undo.addActionListener((ActionEvent e) -> {
try {
undoManager.undo();
} catch (CannotUndoException cue) {}
});
redo.addActionListener((ActionEvent e) -> {
try {
undoManager.redo();
} catch (CannotRedoException cre) {}
});
// Map undo action
textArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(undoKeyStroke, "undoKeyStroke");
textArea.getActionMap().put("undoKeyStroke", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
try {
undoManager.undo();
} catch (CannotUndoException cue) {}
}
});
// Map redo action
textArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(redoKeyStroke, "redoKeyStroke");
textArea.getActionMap().put("redoKeyStroke", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
try {
undoManager.redo();
} catch (CannotRedoException cre) {}
}
});
Related
I need the user to input a name and I want to disable the ok button until some input is given. How can I disable it... ?
JOptionPane allows you to supply a component as the message pane and the controls/options that can be displayed on it.
If you add the correct listeners to the message component, then you should be able to influence the controls that are used as options.
Take a look at JOptionPane.showOptionDialog(Component parentComponent, Object message, String title, int optionType, int messageType, Icon icon, Object[] options, Object initialValue)
Updated
For example...
public class TestOptionPane05 {
public static void main(String[] args) {
new TestOptionPane05();
}
protected JOptionPane getOptionPane(JComponent parent) {
JOptionPane pane = null;
if (!(parent instanceof JOptionPane)) {
pane = getOptionPane((JComponent)parent.getParent());
} else {
pane = (JOptionPane) parent;
}
return pane;
}
public TestOptionPane05() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final JButton okay = new JButton("Ok");
okay.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane pane = getOptionPane((JComponent)e.getSource());
pane.setValue(okay);
}
});
okay.setEnabled(false);
final JButton cancel = new JButton("Cancel");
cancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane pane = getOptionPane((JComponent)e.getSource());
pane.setValue(cancel);
}
});
final JTextField field = new JTextField();
field.getDocument().addDocumentListener(new DocumentListener() {
protected void update() {
okay.setEnabled(field.getText().length() > 0);
}
#Override
public void insertUpdate(DocumentEvent e) {
update();
}
#Override
public void removeUpdate(DocumentEvent e) {
update();
}
#Override
public void changedUpdate(DocumentEvent e) {
update();
}
});
JOptionPane.showOptionDialog(
null,
field,
"Get",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
new Object[]{okay, cancel},
okay);
}
});
}
}
As far as I know this is impossible without overriding JOptionPane.
Try searching for swinglabs or jGoodies libraries for Java. They have built in type for the thing you need.
I need the user to input a name and I want to disable the ok button until some input is given.
wrong way to do it.
i.e. define 'what is a name' = can be anything.
so, what you're, in effect, trying to do is not accept an empty string,
and you do that as an error-check 'after' the OK button has been pressed.
if empty - pop-up error message/repeat input request/confirm cancel/whatever you want to do
I want to initialize a Graphical User Interface (GUI) for the user to input a form. After this is accomplished i want to open a new GUI, but as soon as the first GUI pops-up the next one is initialized to.
Is there any way to solve this without using waits and notifies?
here is an example of my code:
public static void main(String[] args) {
new GUIForm();
// wait until the user inputs the complete form
new GUIWelcome();
}
It is really simple I woild like to keep it that way.
Create an Interface OnActionListener
public interface OnActionListener {
public void onAction();
}
Add these code in GUIForm class
private OnActionListener listener;
private JButton action;
public GUIForm(OnActionListener listener) {
this.listener = listener;
action = new JButton("Action");
action.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
GUIForm.this.listener.onAction();
}
});
}
Now you can achieve that
new GUIForm(new OnActionListener() {
#Override
public void onAction() {
new GUIWelcome();
}
});
You need to use some sort pub/sub mechanism. This in a nutshell is what you need:
public class PubSub {
public static void main(String[] args) {
JFrame frame1 = new JFrame("GUIForm");
frame1.setSize(640, 480);
JButton button = new JButton("User Input");
JFrame frame2 = new JFrame("Welcome");
frame2.setSize(320, 240);
button.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
button.setCursor(new Cursor(Cursor.HAND_CURSOR));
}
#Override
public void mouseExited(MouseEvent e) {
button.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
#Override
public void mouseClicked(MouseEvent e) {
frame2.setVisible(true);
}
});
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.add(button);
frame1.setVisible(true);
}
}
This version uses JFrame's listeners, but you could implement your on callback mechanism to accomplish the same
I have an application I'm making for a game to automatically update a game client.
Once you press Launch, it will open up my DownloadFrame (extends JDialog), and will look like this:
If you click the icon for the application in the taskbar, (maybe Windows 8 is the problem?) it will minimize the application like usual. However when you go to maximise the application again, the JDialog will be hidden, I'm assuming, behind the parent. It looks like this:
Here's my code for my extension of JDialog. Apologies in advance for it being messy.
public class DownloadFrame extends JDialog implements Runnable {
private static final long serialVersionUID = -8764984599528942303L;
private Background frame;
private ImageIcon[] gifs;
private JLabel spinner;
public DownloadFrame() {
super(Loader.application, false);
setLayout(null);
setUndecorated(true);
setAutoRequestFocus(true);
new Thread(this).start();
generateBackground();
generateButton();
generateGif();
}
private void generateBackground() {
frame = new Background("sub_background.png");
setSize(frame.getWidth(), frame.getHeight());
setBackground(new Color(1.0f, 1.0f, 1.0f, 0.0f));
setLocationRelativeTo(null);
setLocation(this.getX(), this.getY() + 5);
setLayout(null);
setContentPane(frame);
}
private void generateGif() {
gifs = Utils.generateGifImages();
spinner = new JLabel(gifs[0]);
spinner.setBounds(70, 30, gifs[0].getIconWidth(), gifs[0].getIconHeight());
add(spinner);
}
private HoverableButton cancel;
public HoverableButton getCancelButton() {
return cancel;
}
private void generateButton() {
cancel = new HoverableButton(Settings.CANCEL_BUTTON, 75, 145);
cancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
/*
* TODO -
* stop the download in progress
*/
for (HoverableButton button : Loader.application.getPrimaryButtons()) {
button.setActive(true);
button.setVisible(true);
}
dispose();
}
});
add(cancel);
}
private int cycleCount;
private void cycleGif() {
if (spinner == null) {
return;
}
cycleCount++;
if (cycleCount > 7) {
cycleCount = 0;
}
spinner.setIcon(gifs[cycleCount]);
}
#Override
public void run() {
while (true) {
cycleGif();
try {
Thread.sleep(100L);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
In case it's needed, here's my usage of it. Most of the stuff can be ignored I'm sure, it's simply there to hide the four buttons while the download is in progress.
((HoverableButton) components[2]).addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
HoverableButton source = (HoverableButton) components[2];
if (source.isActive()) {
try {
Thread.sleep(500L);
} catch (Exception ex) {
ex.printStackTrace();
}
if (panel == null) {
panel = new DownloadFrame();
panel.setVisible(true);
} else {
panel.setVisible(true);
panel.getCancelButton().removeHighlight();
}
for (HoverableButton button : getPrimaryButtons()) {
button.setActive(false);
button.setVisible(false);
button.removeHighlight();
}
/*
* TODO -
* handle checking for updates / downloading updates
*/
}
}
});
However when you go to maximise the application again, the JDialog will be hidden, I'm assuming, behind the parent
Yes. When you create the JDialog, you need to specify the "owner" JFrame of the dialog in the constructor.
So you must create and make the JFrame and make the frame visible before you create the dialog.
Everytime I press cancel or save on the UI it always executes both of the buttons. I've tried countless ways to make it listen to the if statements in the actionperformed block, but it seems to ignore it. I need it so that if I click save it only executes onSave() and cancel for onCancel(). Thanks for your time
public class EditTagPanel extends AbstractTagPanel implements ActionListener {
TagPanelEventListener tagPanelEventListener;
JButton save;
JButton cancel;
public EditTagPanel(ID3v1 id3v1Tag) {
super(id3v1Tag);
}
#Override
protected void configureActionFields() {
JPanel editOptionsPanel = new JPanel(new FlowLayout());
save = new JButton("Save");
save.addActionListener(this);
editOptionsPanel.add(save);
cancel = new JButton("Cancel");
cancel.addActionListener(this);
editOptionsPanel.add(cancel);
this.add(editOptionsPanel, BorderLayout.PAGE_END);
}
public void addTagPanelEventListener(TagPanelEventListener tagPanelEvent) {
this.tagPanelEventListener = tagPanelEvent;
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(save));
{
tagPanelEventListener.onSave(getId3v1Tag());
}
if(e.getSource().equals(cancel));
{
tagPanelEventListener.onCancel();
}
}
Just remove:
;
after each if-statment in your actionPerformed() method, like next:
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(save)) {
tagPanelEventListener.onSave(getId3v1Tag());
}
if (e.getSource().equals(cancel)) {
tagPanelEventListener.onCancel();
}
}
I'm trying to call this event below; I create the frame with TabBuilder (since is part of my application) then it calls the Search screen which is popping up; but the event of the search with key bind or simple click on the button is not working and of course I'm doing something wrong but I don't know what since I'm a little bit new in Java. Please could anyone help me?
SearchScreen:
public class SearchScreen extends EventSearch{
public static void main (String[] args){
SearchScreen s= new SearchScreen();
}
public void SearchScreen(){
TabBuilder tb = new TabBuilder();
tb.searchTab();
}
}
EventSearch:
public class EventSearch extends TabBuilder{
String userQuery;
String key = "ENTER";
KeyStroke keyStroke = KeyStroke.getKeyStroke(key);
public EventSearch(){
btSearch.addActionListener(this);
txtSearch.getInputMap().put(keyStroke, key);
txtSearch.getActionMap().put(key, enterAction);
}
Action enterAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
try{
System.out.println("worked");
} catch (IOException e1) {
e1.printStackTrace(); //print failure
JOptionPane.showMessageDialog(null, "HTTP request failure.");
}
}
};
}
TabBuilder:
public class TabBuilder implements ActionListener {
protected JButton btSearch;
JMenuItem close, search;
protected JTextField txtSearch;
protected JFrame searchFrame = new JFrame();
public void TabBuilder(){
}
public void searchTab(){
JLabel lbSearch;
JPanel searchPane;
btSearch= new JButton("Search");
lbSearch= new JLabel("Type Keywords in english to be searched below:");
lbSearch.setHorizontalAlignment(SwingConstants.CENTER);
txtSearch= new JTextField();
searchPane=new JPanel();
searchPane.setBackground(Color.gray);
searchPane.add(lbSearch);
searchPane.add(txtSearch);
searchPane.add(btSearch);
searchPane.setLayout(new GridLayout(3,3));
btSearch.setEnabled(true);
searchFrame.add(searchPane);
searchFrame.setTitle("SHST");
searchFrame.setSize(400, 400);
searchFrame.setVisible(true);
searchFrame.setDefaultCloseOperation(1);
}
public void actionPerformed(ActionEvent e){
if(e.getSource()==close){
System.exit(0);
}
if(e.getSource()==search){
SearchScreen s = new SearchSreen();
}
}
}
You write this actionListener
public void actionPerformed(ActionEvent e){
if(e.getSource()==close){
System.exit(0);
}
if(e.getSource()==search){
TabBuilder tb = new TabBuilder();
tb.searchTab();
}
}
and you added to btnSearch.addActionListener(this) , your actionListener never would do anything.
And for your KeyBinding happens something similar , you add the action to the txtSearch and then you are asking if the source is the e.getSource()==btSearch
And for KeyBindings you can use Constants to specify when they have to be binded.
JComponent.WHEN_FOCUSED, JComponent.WHEN_IN_FOCUSED_WINDOW , JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
For example :
txtSearch.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, key);
How to use KeyBindings