Java Swing - Detecting a change in the document - java

For school, I'm attempting to recreate Microsoft's Notepad program using Java's Swing. I'm working on the saving and opening of .txt files, and I'm trying to figure out a way for the program to detect when a change has been made to the document. If a change has been detected and the user chooses to open or create a new file, I want the program to prompt the user if they would like to save their changes before continuing.
My thought for this was to create a flag, called documentChanged, that would initially be false, and which would be set to true whenever a change was made to the JTextArea. To detect this change, I thought of using a TextListener as follows:
public class JNotepad implements ActionListener
{
boolean documentChanged;
JTextArea notepad;
JNotepad()
{
documentChanged = false;
notepad = new JTextArea();
notepad.addTextListener(new TextListener() {
public void textValueChanged(TextEvent te) {
documentChanged = true;
}
});
}
}
However, I learned that Java classes are unable to implement multiple interfaces at once, and I'm already using ActionListeners to implement the items of my notepad's menu bar.
My question is, is there any way to use both TextListener and ActionListener (or any other listener) simultaneously in the same class? If not, what would be my best plan of action for detecting a change in the document?

It was answer in another post. See Text Changed event in JTextArea? How to?
And also see How to Write a Document Listener (DocumentListener) in Oracle, you will see an applet example.

How does your this even compile
notepad = new JTextArea();
notepad.addTextListener(new TextListener() {
// ....
}
since TextListeners are not defined to work with JTextAreas but rather with TextAreas, a completely different beast.
You should add a DocumentListener to your JTextArea's Document.
notepad.getDocument().addDocumentListener(new DocumentListener() {
void insertUpdate(DocumentEvent e) {
documentChanged = true;
}
void removeUpdate(DocumentEvent e) {
documentChanged = true;
}
void changedUpdate(DocumentEvent e) {
documentChanged = true;
}
});
Regarding
My question is, is there any way to use both TextListeners and ActionListeners (or any other listener) simultaneously in the same class?
Use of a DocumentListener has nothing to do with ActionListeners used elsewhere in your program since their domains are orthogonal to each other, i.e., the one has absolutely nothing to do with the other.

Related

Closing a frame in Java and opening another one, with the swt library from eclipse

I'm currently making a reading app in Java, and this is my main menu.
What I want is that when I press the bottom button after selecting a book another window with the book opens. What I did now is a function that will open the other window while closing the one I'm currently in to free a little bit of memory.
I first close the current window after retrieving the necessary information from it (the index of the book) like this:
btnOuvrirLeLivre.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
int index = list.getSelectionIndex();
LiseuseController controller = new LiseuseController(null, null);
parent.dispose();
controller.viewBookController(index);
}
});
(I'm using the MVC method for my project), parent is just the composite used to open the frame.
public void viewBookController(int index) {
Display displayBook = new Display();
Shell shellBook = new Shell(displayBook);
shellBook.setLayout(new GridLayout(1, false));
index += 250;
LiseuseView lecture = new LiseuseView(shellBook, SWT.NONE, index);
shellBook.pack();
shellBook.open();
while(!shellBook.isDisposed()) {
if(!displayBook.readAndDispatch())
displayBook.sleep();
}
displayBook.dispose();
}
The index is just the book's number in my database, everything should be fine but I get this error when I do this after pressing the button to open the book:
Exception in thread "main" org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:4875)
at org.eclipse.swt.SWT.error(SWT.java:4790)
at org.eclipse.swt.SWT.error(SWT.java:4761)
at org.eclipse.swt.widgets.Display.checkDisplay(Display.java:824)
at org.eclipse.swt.widgets.Display.create(Display.java:887)
at org.eclipse.swt.graphics.Device.<init>(Device.java:126)
at org.eclipse.swt.widgets.Display.<init>(Display.java:563)
at org.eclipse.swt.widgets.Display.<init>(Display.java:554)
at Controller.LiseuseController.viewBookController(LiseuseController.java:133)
at View.LiseuseHome$4.widgetSelected(LiseuseHome.java:81)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:252)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:89)
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4209)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1037)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4026)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3626)
at Controller.LiseuseController.viewController(LiseuseController.java:126)
at Main.Main.main(Main.java:59)
(LiseuseController.java:133) contains "Display displayBook = new Display();"
And (LiseuseController.java:126) contains
if(!display.readAndDispatch())
display.sleep();
This is from the other function used to open the first window.
I don't really understand what is causing this error and I can't just put one in "visible" and the other one "invisible" like if I'm using Jframe because the 2 windows are on 2 different .java files.
Do you have any idea on how to fix this?
From the API documentation of org.eclipse.swt.widgets.Display
Applications which are built with SWT will almost always require only
a single display. In particular, some platforms which SWT supports
will not allow more than one active display. In other words, some
platforms do not support creating a new display if one already exists
that has not been sent the dispose() message.
You should therefore adapt your application to use only one instance of Display. Since the class LiceuseController already manages opening the initial window and the supplementary window(s), it seems fitting for it to manage an instance of Display for both uses.
This instance should be created at the application's start, maintained by the LiceuseController class and finally disposed when the application shuts down.
Another problem is that none of the methods in LiceuseController actually returns. As you can see from the stack trace, Display.readAndDispatch from LiseuseController.viewController is still active when you are creating the new window. I guess you also want to reopen the original window when the supplementary window is closed. Opening and closing windows in this manner will, however, endlessly increase your call stack until you end up with a StackOverflowException.
Instead, the LiceuseController should be able to create windows without outside interference. Therefore, instead of actively calling a method in the controller class to open another window, the listener should only tell the controller what window it should open next when the current window was closed.
An example could look like
public enum WindowType {
MAIN, BOOK, NONE
}
public class LiceuseController {
Display display;
WindowType nextToOpen = WindowType.MAIN;
public LiceuseController() {
display = new Display();
}
public void setNextToOpen(WindowType value) {
nextToOpen = value;
}
public void run() {
for (boolean run = true; run;) {
switch(nextToOpen) {
case MAIN:
viewController();
break;
case BOOK:
viewBookController();
break;
case NONE:
run = false;
break;
default:
throw new RuntimeException("unexpected enum constant");
}
}
/*
* Depending on how you want to manage the instance of this class,
* you could also extract this into a separate method.
*/
display.dispose();
}
private void viewController() {
// open main window using 'display'
}
private void viewBookController() {
// open book window using 'display'
}
}
so that a listener only needs to call LiceuseController.setNextToOpen and then close the current window. This will cause either viewController or viewBookController to return after which the loop will reenter and open the requested window. To shut down the application, call setNextToOpen with WindowType.NONE.

setDefaultButton not working as expected

public JoinChatClient(String serverAddress, String chatName)
{
chatWindow.getContentPane().add(sendButton, "South");
chatWindow.getContentPane().add(splitPane, "Center");
chatWindow.setSize(800,500);
sendButton.addActionListener(this);
chatWindow.setTitle("Chat Room");
chatWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
splitPane.setDividerLocation(350);
sendButton.setBackground(Color.gray);
sendButton.setForeground(Color.red);
outChatTextArea.setEditable(false);
inChatTextArea.setFont (new Font("default",Font.ITALIC,20));
outChatTextArea.setFont(new Font("default",Font.BOLD,20));
inChatTextArea.setLineWrap(true);
outChatTextArea.setLineWrap(true);
inChatTextArea.setWrapStyleWord(true);
outChatTextArea.setWrapStyleWord(true);
inChatTextArea.setText("Enter text to be sent here.");
outChatTextArea.setText("You can move the separator bar!");
inChatTextArea.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
if(inChatTextArea.getText().equals("Enter text to be sent here."))
{
inChatTextArea.setText("");
inChatTextArea.setFont(new Font("default",Font.BOLD,20));
}
}
public void focusLost(FocusEvent e) {
if(inChatTextArea.getText().isEmpty())
{
inChatTextArea.setFont (new Font("default",Font.ITALIC,20));
inChatTextArea.setText("Enter text to be sent here.");
}
}
});
chatWindow.getRootPane().setDefaultButton(sendButton);
chatWindow.setVisible(true);
}
I've looked over all the threads I could find concerning this, and I cannot figure out why hitting ENTER doesn't activate the actionPerformed method attached to sendButton. Is it because the text field has a FocusListener?
Things I've tried:
changing the statement to target the specific text field (inChatTextArea)
moved the setVisible statement to the end
targeted different parts of the GUI when hitting enter
Bear in mind I've only included the code that builds the GUI in an attempt to waste less of your time.
What I want: Ideally, I want to keep my FocusListener (or something like it) so that I can display the "text field hint." I would like to be able to hit ENTER to send the user's text while the inChatTextArea field is focused.
If a component on the JFrame has focus, and can accept an enter key press, such as one of the JTextAreas, then the enter presses will go to that component and not to the default button. For the default button to work, then the JFrame or the button or some other component that does not accept enter key presses, needs to have focus. I'm guessing that one of your JTextAreas has stolen the focus, and that this is messing you up.
This question is old, but I found it when having the same issue. So I hope others might find it useful.
I figured out that getRootPane() will return null if the component is trying to access the root pane too early, e.g. under construction of the component.
Hence, I propose to use SwingUtilities.invoke(Runnable) to postpone setting the default button on the root pane, and also to request the focus to the button.
So this method could be a helper method on a class to extend from:
protected void setDefaultButton(JButton button) {
// Uses invoke later, as getRootPane() might return null if the method is called under construction
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JRootPane rootPane = getRootPane();
if (rootPane != null) {
rootPane.setDefaultButton(button);
}
button.requestFocus(); // set the focus on the button
}
});
}

How to add a hook to close event for a particular editor in Netbeans?

I need to do something when an editor for a particular document is closed. I have a following code:
FileObject fobj = FileUtil.toFileObject(file);
final DataObject dobj = DataObject.find(fobj);
if (dobj != null) {
EditorCookie ec = dobj.getLookup().lookup(EditorCookie.class);
StyledDocument doc = ec.openDocument();
// Here I would like to add a listener for close event, for the editor window that was opened
}
Is there a way of doing this? Or can I at least hook a listener for global editor closing, so that I get notified of each editor window being closed? I guess in that case I would be somehow able to decide whether the given editor window is the one I am interested in.
OK, I found a working solution, however, it still feels like an ugly workaround. I do not understand why all solutions to my problems with NetBeans IDE look so ugly. I can see that the platform aims to prepare a nice space for creating new editors and other pluggable components, however, when one wants just to listen to existing components (Editors, etc.), it becomes a nightmare.
In my current solution I add a property change listener to TopComponent Registry and listen for a "tcClosed" property to change, and then I test whether it has an EditorCookie (so whether it is an editor) and whether the cookie is the same one that I want to listen to:
TopComponent.getRegistry().addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("tcClosed") && evt.getOldValue() == null) {
EditorCookie cookie = ((TopComponent) evt.getNewValue()).getLookup().lookup(EditorCookie.class);
// I have to remember editor cookie to compare it to the one that is closed
// so that I can find out whether it is the editor I want to listen to
if (cookie != null && cookie.equals(ParentClass.this.getRememberedEditorCookie())) {
// Do my stuff
}
}
}
});
However, if anyone knows a better solution, I will be happy to hear about it.
By accident I stumbled about something which may help you:
EditorCookie.Observable cookie = dataObject.getLookup().lookup(EditorCookie.Observable.class);
cookie.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
// Do your stuff
}
});

How to ignore the mouse events except the last one in Eclipse RCP

See below for code sample, the method handleMouseDoubleClick method will take seconds to run and open another layout screen containing buttons and links. End users may click many times on one listed item in the table control and create flood of mouse events, how can I handle the last mouse event only?
Table tableControl = (Table) control;
tableControl.addMouseListener(new MouseAdapter()
{
public void mouseDown(MouseEvent e)
{
handleMouseDown(e);
}
public void mouseUp(MouseEvent e)
{
handleMouseUp(e);
}
public void mouseDoubleClick(MouseEvent e)
{
handleMouseDoubleClick(e);
}
}
Create a flag field. Set it to true when handler was called. Initialize it with false.
You just need to check whether your screen is already initialized or not before creating another one.
Set the cursor to hourglass and/or disable the table, resetting these after the new "layout screen" is closed...

JTextField key listener is one behind after pasting

I am trying to check that the text in a JTextField matches a perticular pattern, and if it does / doesn't display a message the user. This is what I have so far:
public class input extends KeyListener{
// Some code here
final JTextField inputField = new JTextField(35);
// Some more code...
public void generate(){
// Some GUI code here...
inputField.addKeyListener(this);
}
public void keyPressed(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {
if(e.getSource() instanceof JTextField && e.getSource().equals(inputField)){
if(Pattern.matches("../../....", (JTextComponent) e.getSource()).getText())))
System.out.println("Yh, it works");
else System.out.println("EPIC FAIL (LOL)");
}
}
}
And it does actually work almost perfectly. However, if I paste something using CTRL + V, I have to type two more characters (as opposed to one) before the KeyListener registers that the string is different! So does any one have any idea's why?
Sorry if I have missed out any details - I have tried to make the post as short and concise as possible; so please don't hesitate to ask anything...
For starters, don't use a KeyListener for this type of problem as it is doomed to fail, and even if you get it to work, it's a kludge at best. Instead I'd use either an ActionListener if I wanted to do my checking after the user is completely done entering information, or a DocumentListener if I want to check input as a user is entering, but am not going to block that entering or change the displayed text, or a Document Filter if I'm going to check the input as the user is entering and block it or change it if it is not appropriate.

Categories

Resources