I would like to create simply text editor with dynamic amount of tabs. Every tab consist of text field, where someone can load text file and edit or write own text.
I would like to detect changes in tabs, I mean when someone change file and wants close then I'd like to show a dialog about whether you want save changes or no. this is reason why I'd like to follow changes which user committed.
So I have
JTabbedPane tabbedPane with JtextPane textPane
private LinkedList<Boolean> changedList = new LinkedList<Boolean>(); // here I thought of collecting information about changes but it was silly idea.
This is function which I create new Tab
public void newTab()
{
tabbedPane.addTab("tab-" + counter++, new JTextPane());
int totalTabs = tabbedPane.getTabCount();
selected = tabbedPane.getComponentAt(totalTabs-1);
changedList.add( totalTabs-1, false);
textPane = (JTextPane)selected;
textPane.getDocument().addDocumentListener(this);
}
these are function from interface
#Override
public void insertUpdate(DocumentEvent e)
{
changedList.add(tabbedPane.getSelectedIndex(), true);
}
#Override
public void removeUpdate(DocumentEvent e)
{
changedList.add(tabbedPane.getSelectedIndex(), true);
}
#Override
public void changedUpdate(DocumentEvent e)
{
changedList.add(tabbedPane.getSelectedIndex(), true);
}
This is how I've tried to save
public void saveAfterChange()
{
if (changedList.get(tabbedPane.getSelectedIndex()))
{
int reply = JOptionPane.showConfirmDialog(null, "Save?", null, JOptionPane.YES_NO_OPTION);
if (reply == JOptionPane.YES_NO_OPTION)
{
save();
}
}
}
Related
So compulsive window here means like, I currently have interface(1) with a button, I click the button and it gives me a popup. I want this popup to be the only window that the user can interactive, so the user in this case cannot interact with interface(1). Think it like you are saving a Word doc, when you choose the file location to save this Word doc, you cannot modify the content of this Word doc. And that file saving window is what I want.
Currently I have a custom popup:
public class InputPopup {
private int closeflag;
private JFrame popup;
...
public int getCloseflag() {return closeflag;}
public void close(){
System.exit(0);
}
public void run() {
EventQueue.invokeLater(() -> {
try {
popup.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
And this is what I did in the button listener
InputPopup popup = new InputPopup(wordLabel.getText(), description.getText());
popup.run();
int flag = popup.getCloseflag();
while (flag != 1) {
flag = popup.getCloseflag();
}
popup.close();
Of course that does not work. Anyone has any idea on how to achieve that effect?
I have a JTextArea called taMessage which displays a message string. This string can be edited by the user at run time.
I have a JLabel lblLength to show the number of characters. I am using lblLength.setText(taMessage.getText().length()+"/ 160"); to display the character count.
What event listener should I use for taMessage so that as I keep typing text in my text area, lblLength keeps on updating itself?
Something like we see in sites like way2sms or 160by2, where it shows the number of characters left.
Swing text fields and text areas are backed by a class called Document that can have a Document Listener attached to it.
The official docs have a decent tutorial on Document Listeners.
You would want to attach the document listener, and since you're interested in character counts then you'd simply want to use the same code you used above to initialize the label in all three of the Document Listener's callback methods.
In an MVC like way you can listen to the document's change.
JTextArea ta = ...;
JLabel lblLength = ...;
Document taDoc = ta.getDocument();
taDoc.addDocumentListener(new CharacterCounterDocumentListener(lblLength))
public class CharacterCounterDocumentListener implements DocumentListener {
private JLabel counterLabel;
public CharacterCounterDocumentListener(JLabel counterLabel){
this.counterLabel = counterLabel;
}
public void changedUpdate(DocumentEvent e) {
Document d = e.getDocument();
int length = d.getLength();
counterLabel.setText(Integer.toString(length));
}
public void insertUpdate(DocumentEvent e) {
}
public void removeUpdate(DocumentEvent e) {
}
}
A DocumentListener is probably your best bet. You don't even need to create a new class, you can just define it inline.
// Listen for changes in the text
taMessage.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
update();
}
public void removeUpdate(DocumentEvent e) {
update();
}
public void insertUpdate(DocumentEvent e) {
update();
}
public void update() {
lblLength.setText(taMessage.getText().length()+"/ 160");
}
});
I am following the Oracle tutorial on how to create a custom dialog box: http://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html
I have two buttons: Save Object and Delete Object which when clicked should execute a certain piece of code. Unfortunately I can't seem to add any ActionListener to the JOptionPane buttons so when they're clicked nothing happens.
Can anyone help tell me how I can go about doing this? Here is the class I have for the dialog box so far:
class InputDialogBox extends JDialog implements ActionListener, PropertyChangeListener {
private String typedText = null;
private JTextField textField;
private JOptionPane optionPane;
private String btnString1 = "Save Object";
private String btnString2 = "Delete Object";
/**
* Returns null if the typed string was invalid;
* otherwise, returns the string as the user entered it.
*/
public String getValidatedText() {
return typedText;
}
/** Creates the reusable dialog. */
public InputDialogBox(Frame aFrame, int x, int y) {
super(aFrame, true);
setTitle("New Object");
textField = new JTextField(10);
//Create an array of the text and components to be displayed.
String msgString1 = "Object label:";
Object[] array = {msgString1, textField};
//Create an array specifying the number of dialog buttons
//and their text.
Object[] options = {btnString1, btnString2};
//Create the JOptionPane.
optionPane = new JOptionPane(array,
JOptionPane.PLAIN_MESSAGE,
JOptionPane.YES_NO_OPTION,
null,
options,
options[0]);
setSize(new Dimension(300,250));
setLocation(x, y);
//Make this dialog display it.
setContentPane(optionPane);
setVisible(true);
//Handle window closing correctly.
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
/*
* Instead of directly closing the window,
* we're going to change the JOptionPane's
* value property.
*/
optionPane.setValue(new Integer(
JOptionPane.CLOSED_OPTION));
}
});
//Ensure the text field always gets the first focus.
addComponentListener(new ComponentAdapter() {
public void componentShown(ComponentEvent ce) {
textField.requestFocusInWindow();
}
});
//Register an event handler that puts the text into the option pane.
textField.addActionListener(this);
//Register an event handler that reacts to option pane state changes.
optionPane.addPropertyChangeListener(this);
}
/** This method handles events for the text field. */
public void actionPerformed(ActionEvent e) {
optionPane.setValue(btnString1);
System.out.println(e.getActionCommand());
}
/** This method reacts to state changes in the option pane. */
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (isVisible()
&& (e.getSource() == optionPane)
&& (JOptionPane.VALUE_PROPERTY.equals(prop) ||
JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) {
Object value = optionPane.getValue();
if (value == JOptionPane.UNINITIALIZED_VALUE) {
//ignore reset
return;
}
//Reset the JOptionPane's value.
//If you don't do this, then if the user
//presses the same button next time, no
//property change event will be fired.
optionPane.setValue(JOptionPane.UNINITIALIZED_VALUE);
if (btnString1.equals(value)) {
typedText = textField.getText();
String ucText = typedText.toUpperCase();
if (ucText != null ) {
//we're done; clear and dismiss the dialog
clearAndHide();
} else {
//text was invalid
textField.selectAll();
JOptionPane.showMessageDialog(
InputDialogBox.this,
"Please enter a label",
"Try again",
JOptionPane.ERROR_MESSAGE);
typedText = null;
textField.requestFocusInWindow();
}
} else { //user closed dialog or clicked delete
// Delete the object ...
typedText = null;
clearAndHide();
}
}
}
/** This method clears the dialog and hides it. */
public void clearAndHide() {
textField.setText(null);
setVisible(false);
}
I think you're missing the point of the JOptionPane. It comes with the ability to show it's own dialog...
public class TestOptionPane02 {
public static void main(String[] args) {
new TestOptionPane02();
}
public TestOptionPane02() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JTextField textField = new JTextField(10);
String btnString1 = "Save Object";
String btnString2 = "Delete Object";
//Create an array of the text and components to be displayed.
String msgString1 = "Object label:";
Object[] array = {msgString1, textField};
//Create an array specifying the number of dialog buttons
//and their text.
Object[] options = {btnString1, btnString2};
int result = JOptionPane.showOptionDialog(null, array, "", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, "New Object", options, options[0]);
switch (result) {
case 0:
System.out.println("Save me");
break;
case 1:
System.out.println("Delete me");
break;
}
}
});
}
}
To do it manually, you're going to have to do a little more work.
Firstly, you're going to have to listen to the panel's property change events, looking for changes to the JOptionPane.VALUE_PROPERTY and ignoring any value of JOptionPane.UNINITIALIZED_VALUE...
Once you detect the change, you will need to dispose of your dialog.
The you will need extract the value that was selected via the JOptionPane#getValue method, which returns an Object. You will have to interrupt the meaning to that value yourself...
Needless to say, JOptionPane.showXxxDialog methods do all this for you...
Now if you worried about having to go through all the setup of the dialog, I'd write a utility method that either did it completely or took the required parameters...but that's just me
UPDATED
Don't know why I didn't think it sooner...
Instead of passing an array of String as the options parameter, pass an array of JButton. This way you can attach your own listeners.
options - an array of objects indicating the possible choices the user
can make; if the objects are components, they are rendered properly;
non-String objects are rendered using their toString methods; if this
parameter is null, the options are determined by the Look and Feel
For the flexibility you seem to want you should have your class extend JFrame instead of JDialog. Then declare your buttons as JButtons:
JButton saveButton = new JButton("Save"); and add an actionListnener to this button:
saveButton.addActionListener();
either you can put a class name inside the parenthesis of the saveButton, or you can simply pass it the keyword 'this' and declare a method called actionPerformed to encapsulate the code that should execute when the the button is pressed.
See this link for a JButton tutorial with more details:
http://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
I'm making a level editor for my game. I have a property panel where I can modify the selected object its properties. I also have a Save button to write the level xml.
A field-edit is submitted(*) when the editor component lost the focus or Enter is pressed. This is working great, but the only problem is that when I have this sequence of actions:
Edit a field
Press the save button
Because, what happens is this:
I edit the field
I press the save button
The level is saved
The field lost the focus
The edit is submitted
As you can see, this is the wrong order. Of course I want the field to lose its focus, which causes the submit and then save the level.
Is there a trick, hack or workaround to make the field first lose the focus and then perform the action listener of the save button?
Thanks in advance.
(* submit = the edit to the field is also made in the object property)
EDIT: For the field I'm using a FocusAdapter with focusLost:
FocusAdapter focusAdapter = new FocusAdapter()
{
#Override
public void focusLost(FocusEvent e)
{
compProperties.setProperty(i, getColor());
record(); // For undo-redo mechanism
}
};
And for the button a simple ActionListener with actionPerformed`.
btnSave.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
// Save the level
}
});
Hmm ... can't reproduce: in the snippet below the lost is always notified before the actionPerfomed, independent on whether I click the button or use the mnemonic:
final JTextField field = new JTextField("some text to change");
FocusAdapter focus = new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
LOG.info("lost: " + field.getText());
}
};
field.addFocusListener(focus);
Action save = new AbstractAction("save") {
#Override
public void actionPerformed(ActionEvent e) {
LOG.info("save: " + field.getText());
}
};
save.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
JButton button = new JButton(save);
JComponent box = Box.createHorizontalBox();
box.add(field);
box.add(button);
On the other hand, focus is a tricky property to rely on, the ordering might be system-dependent (mine is win vista). Check how the snippet behave on yours.
If you see the same sequence as I do, the problem is somewhere else
if you get the save before the lost, try to wrap the the save action into invokeLater (which puts it at the end of the EventQueue, so it's executed after all pending events)
Action save = new AbstractAction("save") {
#Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
LOG.info("save: " + field.getText());
}
});
}
};
Normally, wrapping your save code into an SwingUtilities.invokeLater() should do the trick. As you already mentioned, this doesn't work? Try this:
private boolean editFocus = false;
FocusAdapter focusAdapter = new FocusAdapter()
{
#Override
public void focusGained(FocusEvent e){
editFocus = true;
}
#Override
public void focusLost(FocusEvent e){
compProperties.setProperty(i, getColor());
record(); // For undo-redo mechanism
editFocus = false;
if (saveRequested){
save();
}
}
};
and for your button:
private boolean saveRequested = false;
btnSave.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
if (editFocus){
saveRequested = true;
return;
} else {
save();
}
}
});
and then your save method:
private void save(){
// do your saving work
saveRequested = false;
}
This only works when your focusLost gets called after your button's action. If suddenly the order is correct, this code will get save() called twice.
But again, wrapping your save() code in your original approach should work, because the save code will execute after processing all events. That is after processing your button click and your focusLost events. Because your focusLost code executes immediately (it's not wrapped in an invokeLater()), the focusLost code should be executed always before your save code. This does not mean that the event order will be correct! But the code associated to the events will executed in the right order.
I've created a jtextarea where a user can modify its content. I want to know,if there is any way, whether the user has modified its content or not before closing the application. Please help.
-Thanks in advance
You need to add a DocumentListener to the Document that backs the text area.
Then in the callback methods (insertUpdate(), removeUpdate(), changedUpdate()) of the listener, simply set a flag that something has changed and test that flag before closing the application
public class MyPanel
implements DocumentListener
{
private boolean changed;
public MyPanel()
{
JTextArea textArea = new JTextArea();
textArea.getDocument().addDocumentListener(this);
.....
}
.....
public void insertUpdate(DocumentEvent e)
{
changed = true;
}
public void removeUpdate(DocumentEvent e)
{
changed = true;
}
public void changedUpdate(DocumentEvent e)
{
changed = true;
}
}
Save the value of jtextarea and compare this value to the value of jtextarea in the moment of application closing.
Pseudocode here, doesn't remember the excact syntax of text area:
String oldText = textarea.getText();
....
// not the exact method, just to point the moment of application exit
public onClose() {
String newText = textArea.getText();
// assuming oldText is not null
if (oldText.equals(newText)) {
// no changes have been done
} else {
// the value changed
}
}