Transfer cursor to JTextField when press JButton - java

When I click on the "Preview Page" button, the cursor should move to the Start Position (txtStartPosition). Below is my code that I wrote but somehow it is not working.
btnPreviewPage = new JButton("Preview Single");
btnPreviewPage.setFocusable(false);
btnPreviewPage.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
txtStartPosition.setCaretPosition(txtStartPosition.getCaretPosition() - 1);
new UIPreviewPage().execute();
}
});
panel.add(btnPreviewPage, "cell 1 18,alignx right");

You need to do JTextField.requestFocus();or, the more preferred JTextField.requestFocusInWindow();
https://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#requestFocus()
public void requestFocus()
Requests that this Component gets the input focus. Refer to Component.requestFocus() for a complete description of this method.
Note that the use of this method is discouraged because its behavior is platform dependent. Instead we recommend the use of requestFocusInWindow(). If you would like more information on focus, see How to Use the Focus Subsystem, a section in The Java Tutorial.
Overrides:
requestFocus in class Component
Since:
1.4
See Also:
Component.requestFocusInWindow(), Component.requestFocusInWindow(boolean)
Using your code it should be
tnPreviewPage = new JButton("Preview Single");
btnPreviewPage.setFocusable(false);
btnPreviewPage.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//Add YourJTextField.requestFocusInWindow() here, but not sure if the other information is needed
txtStartPosition.setCaretPosition(txtStartPosition.getCaretPosition() - 1);
new UIPreviewPage().execute();
}
});
panel.add(btnPreviewPage, "cell 1 18,alignx right");

Related

JButton Prints Multiple Times Instead of Once. Why?

I have a JButton and the code is below. When pressed it prints 3 times to the console instead of once. Why is it doing that and how to fix that? Thanks in advance! I also posted on code ranch.
change61 = new JButton("N");
change61.setLocation(0,0);
change61.setSize(25,14);
change61.setFocusPainted(false);
change61.setBorder(new LineBorder(Color.BLACK));
change61.setMargin(new Insets(0,0,0,0));
change61.setFont(new Font("Arial", Font.BOLD, 7));
change61.setRolloverEnabled(false); // TEST
change61.addActionListener(this);
change61.setActionCommand("Normal");
buttons16.add(change61);
change61.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
ButtonModel model = change61.getModel();
if (model.isArmed()) {
cl1.setIcon(CL2);
lvrvr1.setIcon(LVRL);
dsw1.setIcon(LSIG);
dsy1.setIcon(CL1);
b1b.setIcon(LHC);
System.out.println("Button Pressed"); // THIS GETS PRINTED 3 TIMES TO CONSOLE INSTEAD OF ONCE
} else {
cl1.setIcon(CL1);
}
}
});
Within stateChagned method, use isPressed instead of isArmed. It should work.
Or as #camickr suggested
change61.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cl1.setIcon(CL2);
lvrvr1.setIcon(LVRL);
dsw1.setIcon(LSIG);
dsy1.setIcon(CL1);
b1b.setIcon(LHC);
System.out.println("Button Pressed"); // THIS GETS PRINTED 3 TIMES TO CONSOLE INSTEAD OF ONCE
}
});
Since the question got me hooked, I wanted to know why it is fired three times.
As the stacktrace reveals, the changes are fired by the mouse event.
First, the mouse is pressed. It calls DefaultButtonModel.isArmed(true) and DefaultButtonModel.isPressed(true). Each method triggers a change event. Here we have the first and second iteration.
Second, the mouse is released. It calls DefaultButtonModel.isPressed(false), again triggering a change event. The third iteration.
FYI, the DefaultButtonModel is the implementation of the ButtonModel- Interface.

Event handling in Java (JTree + JButton)

private void createEvents()
{
menuFileExit.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
System.exit(0);
}
});
////// Events on tree selection
jtStoryViewer.addTreeSelectionListener(new TreeSelectionListener()
{
public void valueChanged(TreeSelectionEvent arg0)
{
DefaultMutableTreeNode selection = (DefaultMutableTreeNode) jtStoryViewer.getLastSelectedPathComponent();
Object nodeObject = selection.getUserObject();
////// Checks if selected node is a String (only story title is a string)
if(selection.getUserObject().getClass().getName() == "java.lang.String" )
{
tfTitle.setText(nodeObject.toString());
////// Action listener for Change Button
btnChange.addActionListener(new ActionListener()
{
////// Title text swap
public void actionPerformed(ActionEvent arg0)
{
selection.setUserObject(tfTitle.getText());
((DefaultTreeModel)jtStoryViewer.getModel()).nodeChanged(selection);
}
});
}
///// checks if the object is a chapter object
if(selection.getUserObject().getClass().getName() == "ISW.common.Chapter")
{
Chapter chapter = (Chapter) selection.getUserObject();
tfTitle.setText(chapter.toString());
////// Action listener for Change Button
btnChange.addActionListener(new ActionListener()
{
////// Title text swap
public void actionPerformed(ActionEvent arg0)
{
chapter.setTitle(tfTitle.getText());
((DefaultTreeModel)jtStoryViewer.getModel()).nodeChanged(selection);
}
});
}
}
});
}
I am using JTree to display and modify some objects. I added a TreeSelectionListener to get the object data on selection. For now I want to be able to change the title of an object, it works fine on first selection on the tree , I change the value in the text box and the "Change" button works just fine, but when I move on to next objects, the change button also modifies the value of all previously selected objects.
I guess it is caused due to my improper usage of the ActionListeners but I can't tell for sure and at this point I'm stuck.
Will be grateful for any hints.
Don't keep adding an ActionListener to the btnChange JButton within the TreeSelectionListener#valueChanged method.
This will cause the button to call EVERY ActionListener you have previously
Instead, give the btnChange a single ActionListener, when clicked, can act on the currently selected node (by checking the JTree it self). You could have the TreeSelectionListener#valueChanged method enable or disable the btnChange based on the validity of the selection
Also, if(selection.getUserObject().getClass().getName() == "ISW.common.Chapter") isn't how String comparison is done in Java, instead you should use something more like if("ISW.common.Chapter".equals(selection.getUserObject().getClass().getName()))

Java Swing default focus on frame

I am learning java and Swing right now and trying to develop simple programms for education purposes.
So here is the question.
I have gridlayout and fields on my frame with default text
accNumberField = new JTextField("0", 10);
accNumberField.addFocusListener(new FocusListener() {
int focusCounter = 0;
#Override
public void focusGained(FocusEvent arg0) {
// TODO Auto-generated method stub
if (focusCounter > 0)
accNumberField.setText("");
focusCounter++;
}
What I want is that when user click on field for the first time the default text is disappered. So I add focus listener and used accNumberField.setText(""); in focusGained method.
But the problem is that for default first field in my frame getting focus right in time of frame creation. And default text is disappearing from the begining. I used counter as you can see. But that's not what I wanted.
I want that no field would get focus in time of creation and every field would be able to get focus from the time when user would click on one of them.
Sorry if I spelled something wrong. English is not my native language.
Found a thread having a code example of your desired functionality, Java JTextField with input hint. Precisely, you need to provide your own implementation of JTextField which will be holding the "default-text" in a field, specially created for that.
For your second question, you can set the focus to some button or frame itself.
Is there any reason that you use focusListener()? why not use mouseListener() as follow?
accNumberField.addMouseListener(new MouseAdapter()
{
#Override
public void mouseReleased(MouseEvent e)
{
accNumberField.setText("");
}
});
if you want to clear the text for the first click, you can simply use a boolean:
//outside constructor
private boolean isTextCleared = false;
//in constructor
accNumberField.addMouseListener(new MouseAdapter()
{
#Override
public void mouseReleased(MouseEvent e)
{
if (!isTextCleared)
{
accNumberField.setText("");
isTextCleared = true;
}
}
});

Java - Adding tab to JTabbedPane causing StackOverflowError (Disabling a firing ChangeEvent?)

Hey StackOverflow community, I'm having a StackOverflow problem.
I'm having difficulty adding a new tab to my GUI's JTabbedPane container, when the [+] tab is selected. So far, whenever I click the [+] tab, new tabs are appended until a StackOverflowError occurs.
A new tab is added to the JTabbedPane when the following condition is true.
if(songPanel.getSelectedIndex()==songPanel.getTabCount()-1){
...
}
I've tried to revert back to the previously selected tab to avoid tabs being added repeatedly to the JTabbedPane, to no avail. When the ChangeEvent actuator is fired, does it stay on indefinitely? I haven't come across anything useful in the SE7 API.
Relevant code (non-compilable, excerpt from larger program. May be missing brackets, only because I copy-pasted excerpts of the code, and liable to make mistakes)
#Override
public void init(){
setLayout(new GridLayout(MAIN_LAYOUT_ROWS, MAIN_LAYOUT_COLUMNS));
add(renderPanel = new JScrollPane());
add(controlPanel = new JPanel());
add(colourPanel = new JPanel());
add(songPanel = new JTabbedPane());
//songPanel options
songPanel = new JTabbedPane();
songPanel.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
songPanel.addTab("#1", new JTextArea());
songPanel.addTab("+", null, new JLabel(), "+");
Container cp = getContentPane();
cp.add(BorderLayout.SOUTH, songPanel);
//integrate songPanel changeListener
songPanel.addChangeListener(new ChangeListener(){
#Override //Method called when selected tab changes
public void stateChanged(ChangeEvent e){
try {
if(songPanel.getSelectedIndex()==songPanel.getTabCount()-1){
addTab("songPanel");
}
} catch (StackOverflowError soe){soe.printStackTrace();}
}
});
//*************************************************************************
#Override
public void start(){
}
//*************************************************************************
private void addTab(String panelName){
System.out.println("ADDING TAB");
if(panelName.equals("songPanel")){
String tabName = ("#" + Integer.toString(songPanel.getTabCount()-1));
songPanel.insertTab(tabName, null, new JTextField(), tabName, songPanel.getTabCount()-2);
}
}
}
//**************************************************************************
}
I've tried:
Setting a revert index in the addTab() method, so the newest tab is selected (still results in StackOverflowError)
Note this line:
songPanel.getSelectedIndex()==songPanel.getTabCount()-1)
Both "songPanel.getSelectedIndex()" and "songPanel.getTabCount()-1)" are always equal, so condition is always true (causing the StackOverflowError)
Error message:
java.lang.StackOverflowError
at javax.swing.text.StyleContext$SmallAttributeSet.getAttributeNames(StyleContext.java:947)
at javax.swing.text.StyleContext$SmallAttributeSet.containsAttributes(StyleContext.java:973)
at javax.swing.text.StyleContext$SmallAttributeSet.equals(StyleContext.java:852)
at java.util.WeakHashMap.eq(WeakHashMap.java:282)
at java.util.WeakHashMap.get(WeakHashMap.java:379)
at java.util.Collections$SynchronizedMap.get(Collections.java:2031)
at javax.swing.text.StyleContext.getImmutableUniqueSet(StyleContext.java:520)
at javax.swing.text.StyleContext.addAttributes(StyleContext.java:340)
at javax.swing.text.AbstractDocument$AbstractElement.addAttributes(AbstractDocument.java:1985)
at javax.swing.text.AbstractDocument$AbstractElement.<init>(AbstractDocument.java:1777)
at javax.swing.text.AbstractDocument$LeafElement.<init>(AbstractDocument.java:2502)
at javax.swing.text.AbstractDocument$BidiElement.<init>(AbstractDocument.java:2674)
at javax.swing.text.AbstractDocument.<init>(AbstractDocument.java:149)
at javax.swing.text.AbstractDocument.<init>(AbstractDocument.java:109)
at javax.swing.text.PlainDocument.<init>(PlainDocument.java:90)
at javax.swing.text.PlainDocument.<init>(PlainDocument.java:80)
at javax.swing.text.DefaultEditorKit.createDefaultDocument(DefaultEditorKit.java:130)
at javax.swing.plaf.basic.BasicTextUI.installUI(BasicTextUI.java:799)
at javax.swing.JComponent.setUI(JComponent.java:655)
at javax.swing.text.JTextComponent.setUI(JTextComponent.java:338)
at javax.swing.text.JTextComponent.updateUI(JTextComponent.java:348)
at javax.swing.text.JTextComponent.<init>(JTextComponent.java:322)
at javax.swing.JTextField.<init>(JTextField.java:231)
at javax.swing.JTextField.<init>(JTextField.java:172)
at application.Analyzer.addTab(Analyzer.java:133)
at application.Analyzer.access$100(Analyzer.java:24)
at application.Analyzer$1.stateChanged(Analyzer.java:101)
at javax.swing.JTabbedPane.fireStateChanged(JTabbedPane.java:416)
at javax.swing.JTabbedPane$ModelListener.stateChanged(JTabbedPane.java:270)
at javax.swing.DefaultSingleSelectionModel.fireStateChanged(DefaultSingleSelectionModel.java:132)
at javax.swing.DefaultSingleSelectionModel.setSelectedIndex(DefaultSingleSelectionModel.java:67)
at javax.swing.JTabbedPane.setSelectedIndexImpl(JTabbedPane.java:616)
at javax.swing.JTabbedPane.insertTab(JTabbedPane.java:735)
at application.Analyzer.addTab(Analyzer.java:133)
at application.Analyzer.access$100(Analyzer.java:24)
.
.
.
Do you have any suggestions? I know it's kind of vague, but I'm really not sure what is going wrong.
Any suggestions?
Thanks.
Tyler
StackOverflow identifies an infinite recursion. So first thing is to find that recursion. In your case these are the lines of the stacktrace that identifies that recursion:
at application.Analyzer.addTab(Analyzer.java:133) at
application.Analyzer.access$100(Analyzer.java:24) at
application.Analyzer$1.stateChanged(Analyzer.java:101) at
javax.swing.JTabbedPane.fireStateChanged(JTabbedPane.java:416) at
javax.swing.JTabbedPane$ModelListener.stateChanged(JTabbedPane.java:270)
at
javax.swing.DefaultSingleSelectionModel.fireStateChanged(DefaultSingleSelectionModel.java:132)
at
javax.swing.DefaultSingleSelectionModel.setSelectedIndex(DefaultSingleSelectionModel.java:67)
at javax.swing.JTabbedPane.setSelectedIndexImpl(JTabbedPane.java:616)
at javax.swing.JTabbedPane.insertTab(JTabbedPane.java:735) at
application.Analyzer.addTab(Analyzer.java:133)
So when you insert a tab, it automatically triggers a change of selected tab which in turns calls your ChangeEventListener which will trigger the insertion of a tab etc...
So you have two simple solutions:
Use a flag (a boolean) that is set to true before you add the new tab and that you set back to false when you are done. In your condition to test if you need to add a tab, you also check that this flag is not true.
You remove your change listener from the JTabbedPane before you insert the tab and you put it back afterwards.
In both case, use a try/finally block to make sure to return to a consistent state.
UPDATED SOLUTION
Sorry, the previous solution is not working as expected. Here my updated one:
public class TabbedPaneTest {
private final static JButton ADD_NEW_TAB_BUTTON = new JButton();
private JFrame mainFrame;
private JTabbedPane tabbedPane;
public void run() {
mainFrame = new JFrame("Test JTabbedPane");
mainFrame.setSize(300, 400);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tabbedPane = new JTabbedPane();
tabbedPane.addTab("default new tab", new JLabel("this is a default tab"));
addNewTabButton();
tabbedPane.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if (tabbedPane.getSelectedComponent() == ADD_NEW_TAB_BUTTON) {
tabbedPane.addTab("new tab", new JLabel("new tab label"));
addNewTabButton();
}
}
});
mainFrame.getContentPane().add(tabbedPane);
mainFrame.setVisible(true);
}
private void addNewTabButton() {
tabbedPane.remove(ADD_NEW_TAB_BUTTON);
tabbedPane.addTab("[+]", ADD_NEW_TAB_BUTTON);
}
public static void main(String[] params) {
TabbedPaneTest test = new TabbedPaneTest();
test.run();
}
}
The problem is that the changeListener is called after the adding once again with the + tab as the selected tab, causing the creation of a new tab, and so on.
a pretty simple solution could be just to add a bool flag as Guillaume Polet stated :
songPanel.addChangeListener(new ChangeListener(){
#Override //Method called when selected tab changes
public void stateChanged(ChangeEvent e){
try {
if(songPanel.getSelectedIndex()==songPanel.getTabCount()-1 && !adding){
adding = true;
addTab("songPanel");
adding = false;
}
} catch (StackOverflowError soe){soe.printStackTrace();}
}
});
The adding flag is a class field initialized to false, and indicates whether or not you are in progress of adding a tab.
Minor changes to the addTab to get everything to work:
private void addTab(String panelName){
System.out.println("ADDING TAB");
if(panelName.equals("songPanel")){
String tabName = ("#" + Integer.toString(songPanel.getTabCount()));
int index = songPanel.getTabCount()-1;
songPanel.insertTab(tabName, null, new JTextField(), tabName, index);
songPanel.setSelectedIndex(index);
}
}
I made a little change in code, making the active tab the newly created one, and the indexes where a bit off.
Hope this helps :)

Stopping JPopupMenu stealing the focus

I have a JTextField for which I'm hoping to suggest results to match the user's input. I'm displaying these suggestions in a JList contained within a JPopupMenu.
However, when opening the popup menu programmatically via show(Component invoker, int x, int y), the focus is getting taken from the JTextField.
Strangely enough, if I call setVisible(true) instead, the focus is not stolen; but then the JPopupMenu is not attached to any panel, and when minimizing the application whilst the box is open, it stays painted on the window.
I've also tried to reset the focus to the JTextField using requestFocus(), but then I have to restore the caret position using SwingUtilities.invokeLater(), and the invoke later side of things is giving the user a slight margin to mess around with the existing contents / overwrite it / or do other unpredictable things.
The code I've got is effectively:
JTextField field = new JTextField();
JPopupMenu menu = new JPopupMenu();
field.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
JList list = getAListOfResults();
menu.add(list);
menu.show(field, 0, field.getHeight());
}
});
Can anyone suggest the best avenue to go down to show the JPopupMenu programmatically whilst preserving the focus on the JTextField?
The technical answer is to set the popup's focusable property to false:
popup.setFocusable(false);
The implication is that the textField has to take over all keyboard and mouse-triggered actions that are normally handled by the list itself, sosmething like:
final JList list = new JList(Locale.getAvailableLocales());
final JPopupMenu popup = new JPopupMenu();
popup.add(new JScrollPane(list));
popup.setFocusable(false);
final JTextField field = new JTextField(20);
Action down = new AbstractAction("nextElement") {
#Override
public void actionPerformed(ActionEvent e) {
int next = Math.min(list.getSelectedIndex() + 1,
list.getModel().getSize() - 1);
list.setSelectedIndex(next);
list.ensureIndexIsVisible(next);
}
};
field.getActionMap().put("nextElement", down);
field.getInputMap().put(
KeyStroke.getKeyStroke("DOWN"), "nextElement");
As your context is very similar to a JComboBox, you might consider having a look into the sources of BasicComboBoxUI and BasicComboPopup.
Edit
Just for fun, the following is not answering the focus question :-) Instead, it demonstrates how to use a sortable/filterable JXList to show only the options in the dropdown which correspond to the typed text (here with a starts-with rule)
// instantiate a sortable JXList
final JXList list = new JXList(Locale.getAvailableLocales(), true);
list.setSortOrder(SortOrder.ASCENDING);
final JPopupMenu popup = new JPopupMenu();
popup.add(new JScrollPane(list));
popup.setFocusable(false);
final JTextField field = new JTextField(20);
// instantiate a PatternModel to map text --> pattern
final PatternModel model = new PatternModel();
model.setMatchRule(PatternModel.MATCH_RULE_STARTSWITH);
// listener which to update the list's RowFilter on changes to the model's pattern property
PropertyChangeListener modelListener = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("pattern".equals(evt.getPropertyName())) {
updateFilter((Pattern) evt.getNewValue());
}
}
private void updateFilter(Pattern newValue) {
RowFilter<Object, Integer> filter = null;
if (newValue != null) {
filter = RowFilters.regexFilter(newValue);
}
list.setRowFilter(filter);
}
};
model.addPropertyChangeListener(modelListener);
// DocumentListener to update the model's rawtext property on changes to the field
DocumentListener documentListener = new DocumentListener() {
#Override
public void removeUpdate(DocumentEvent e) {
updateAfterDocumentChange();
}
#Override
public void insertUpdate(DocumentEvent e) {
updateAfterDocumentChange();
}
private void updateAfterDocumentChange() {
if (!popup.isVisible()) {
popup.show(field, 0, field.getHeight());
}
model.setRawText(field.getText());
}
#Override
public void changedUpdate(DocumentEvent e) {
}
};
field.getDocument().addDocumentListener(documentListener);
It looks straight forward to me. Add the following
field.requestFocus();
after
menu.add(list);
menu.show(field, 0, field.getHeight());
Of course, you will have to code for when to hide the popup etc based on what is going on with the JTextField.
i.e;
menu.show(field, field.getX(), field.getY()+field.getHeight());
menu.setVisible(true);
field.requestFocus();
You may take a look to JXSearchField, which is part of xswingx

Categories