Results 1 to 6 of 6
  1. #1
    Join Date
    Apr 2010
    Posts
    13
    Rep Power
    0

    Default Write-only JTextField?

    Hello.

    My question today is, hopefully, a simple one: is there an easy way to have a JTextField (or JTextArea or any simillar component) that only allows to input text but not modify in any way what's already been entered?

    The behaviour I'd like to have is that once the user has entered a character, he can neither delete it, nor cut it, nor reposition the caret (therefore making it impossible to enter new text before the already typed text; all new text should be appended to the end of the previous text).

    So far, the only solution I see involves writing a class that implements KeyListener (or extends KeyAdapter) and another that implements CaretListener, and register them as the KeyListener and CaretListener of the JTextField. The KeyListener would ignore KeyEvents that match the backspace, delete, and arrow keys, and the CaretListener would reposition the caret if it's moved anywhere except the end of the text. Would that accomplish what I want? Is there a better or easier way to do it?

    Thanks in advance.

  2. #2
    Eranga's Avatar
    Eranga is offline Moderator
    Join Date
    Jul 2007
    Location
    Colombo, Sri Lanka
    Posts
    11,371
    Blog Entries
    1
    Rep Power
    20

    Default

    In simple way, why don't you validate them with the key codes. Restrict the events for delete, backspace, spacebar and text selection.

  3. #3
    Join Date
    Apr 2010
    Posts
    13
    Rep Power
    0

    Default

    Quote Originally Posted by Eranga View Post
    In simple way, why don't you validate them with the key codes. Restrict the events for delete, backspace, spacebar and text selection.
    By "restrict" I assume you mean using something like the consume() method? Either way, that would only solve part of the problem, since the user could still, for example, reposition the caret using the mouse and therefore insert characters in places other than at the end of the existing text. So it seems I'd need to restrict caret events and probably some mouse events (like dragging events to prevent selection of text, just in case the user decides to try and outsmart me by cutting text instead of deleting it) too. I guess implementing listeners is the way to go after all.

  4. #4
    Eranga's Avatar
    Eranga is offline Moderator
    Join Date
    Jul 2007
    Location
    Colombo, Sri Lanka
    Posts
    11,371
    Blog Entries
    1
    Rep Power
    20

    Default

    Basically restrict means that you've to rollback what users did. In that sense you've to think about various combination of such user interactions. What I've mentioned is few things which are comes into my mind at that min. :)

  5. #5
    camickr is offline Senior Member
    Join Date
    Jul 2009
    Posts
    1,236
    Rep Power
    7

    Default

    The code below doesn't do what you want but you should be able to customize it for your requirements. Basically you should be able to customize the navigation filter to prevent it from going backwards. Then you need to replace the default backspace action to do nothing.

    Java Code:
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.text.*;
    
    public class NavigationFilterPrefixWithBackspace extends NavigationFilter
    {
    	private int prefixLength;
    	private Action deletePrevious;
    
    	public NavigationFilterPrefixWithBackspace(int prefixLength, JTextComponent component)
    	{
    		this.prefixLength = prefixLength;
    		deletePrevious = component.getActionMap().get("delete-previous");
    		component.getActionMap().put("delete-previous", new BackspaceAction());
    		component.setCaretPosition(prefixLength);
    	}
    
    	public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
    	{
    		fb.setDot(Math.max(dot, prefixLength), bias);
    	}
    
    	public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
    	{
    		fb.moveDot(Math.max(dot, prefixLength), bias);
    	}
    
    	class BackspaceAction extends AbstractAction
    	{
    		public void actionPerformed(ActionEvent e)
    		{
    			JTextComponent component = (JTextComponent)e.getSource();
    
    			if (component.getCaretPosition() > prefixLength)
    			{
    				deletePrevious.actionPerformed( null );
    			}
    		}
    	}
    
    	public static void main(String args[]) throws Exception {
    
    		JTextField textField = new JTextField("Prefix_", 20);
    		textField.setNavigationFilter( new NavigationFilterPrefixWithBackspace(7, textField) );
    
    		JFrame frame = new JFrame("Navigation Filter Example");
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		frame.getContentPane().add(textField);
    		frame.pack();
    		frame.setLocationRelativeTo( null );
    		frame.setVisible(true);
    	}
    }

  6. #6
    Join Date
    Apr 2010
    Posts
    13
    Rep Power
    0

    Default

    Quote Originally Posted by camickr View Post
    The code below doesn't do what you want but you should be able to customize it for your requirements. Basically you should be able to customize the navigation filter to prevent it from going backwards. Then you need to replace the default backspace action to do nothing.

    [code was here]
    Thanks, that worked like a charm (with a bit of customization, of course). Here's an example of how I got the behaviour I wanted:

    Java Code:
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.Collections;
    import java.util.LinkedList;
    import java.util.List;
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JDialog;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.text.JTextComponent;
    import javax.swing.text.NavigationFilter;
    import javax.swing.text.Position;
    
    public class WriteOnlyTextField extends JPanel{
        private static final long serialVersionUID = 0L;
        JButton startButton;
        JLabel sampleLabel;
        JTextField copyField;
        Action insert;
        Action deletePrevious;
        Action deleteNext;
        private double errors = 0;
        private double typedChars = 0;
        private double insertions = 0;
        private double deletions = 0;
        private double substitutions = 0;
        private String sampleText = "";
        private boolean editMode = false;
        private boolean deleted = false;
        private List<String> wordList;
        
        
        public class CustomAction extends AbstractAction {
            private static final long serialVersionUID = 1L;
            
            public void actionPerformed(ActionEvent arg0) {
                JTextComponent component = (JTextComponent) arg0.getSource();
                component.setCaretPosition(copyField.getText().length());            
            }
        }
       
        public class CustomInsertContentAction extends AbstractAction {
            private static final long serialVersionUID = 2L;
    
            public void actionPerformed(ActionEvent arg0) {
                JTextComponent component = (JTextComponent) arg0.getSource();
                typedChars++;
                System.out.println("DERP");
                if (editMode) {
                    if (!sampleText.equalsIgnoreCase(copyField.getText())) {
                        if (!deleted) {
                            if (component.getCaretPosition()<copyField.getText().length()) {
                                insertions++;
                                System.out.println("Insertions: "+insertions);
                                insert.actionPerformed(null);
                            } 
                        } else {
                            deletions--;
                            deleted = false;
                            substitutions++;
                            System.out.println("Substitutions: "+substitutions);
                            insert.actionPerformed(null);
                        }
                        System.out.println("Sample Text and length: "+sampleLabel.getText()+", "+sampleLabel.getText().length());
                        System.out.println("User Text and length: "+copyField.getText()+", "+copyField.getText().length());
                    } else {
                        errors = substitutions+deletions+insertions;
                        System.out.println("Errors in normal mode: "+errors);
                    }
                } else {
                    if (sampleText.length()<copyField.getText().length()) {
                        component.setCaretPosition(copyField.getText().length());            
                        System.out.println("Sample Text and length: "+sampleLabel.getText()+", "+sampleLabel.getText().length());
                        System.out.println("User Text and length: "+copyField.getText()+", "+copyField.getText().length());
                        insert.actionPerformed(null);
                        System.out.println("Sample Text and length: "+sampleLabel.getText()+", "+sampleLabel.getText().length());
                        System.out.println("User Text and length: "+copyField.getText()+", "+copyField.getText().length());
                    } else {
                        double errors = 0;
                        for (int i = 0; i <= copyField.getText().length(); i++) {
                            if (copyField.getText().charAt(i) != sampleLabel.getText().charAt(i)) {
                                errors++;
                            }
                        }
                        System.out.println("Errors in write-only mode: "+errors);
                    }
                }  
            }
        }
        
        public class CustomDeleteNextAction extends AbstractAction {
            private static final long serialVersionUID = 3L;
            
            public void actionPerformed(ActionEvent arg0) {
                    deletions++;
                    deleted = true;
                    System.out.println("Deletions: "+deletions);                
                    deleteNext.actionPerformed(null);
            }
        }
    
        public class CustomDeletePreviousAction extends AbstractAction {
            private static final long serialVersionUID = 4L;
            
            public void actionPerformed(ActionEvent arg0) {
                deletions++;
                deleted = true;
                deletePrevious.actionPerformed(null);
            }
        }
    
        
        public class EditionNavigationFilter extends NavigationFilter {        
                   
            public EditionNavigationFilter() {
                if (!editMode) {
                    //if the user can't edit, disable moving the caret
                    copyField.getActionMap().put("caret-backward", new CustomAction());
                    copyField.getActionMap().put("caret-begin", new CustomAction());
                    copyField.getActionMap().put("caret-begin-line", new CustomAction());
                    copyField.getActionMap().put("caret-begin-word", new CustomAction());
                    copyField.getActionMap().put("caret-down", new CustomAction());
                    copyField.getActionMap().put("caret-forward", new CustomAction());
                    copyField.getActionMap().put("caret-previous-word", new CustomAction());
                    copyField.getActionMap().put("caret-up", new CustomAction());
                    deleteNext = copyField.getActionMap().get("delete-next");
                    copyField.getActionMap().put("delete-next", new CustomAction());
                    deletePrevious = copyField.getActionMap().get("delete-previous");
                    copyField.getActionMap().put("delete-previous", new CustomAction());
                } else {
                    deleteNext = copyField.getActionMap().get("delete-next");
                    copyField.getActionMap().put("delete-next", new CustomDeleteNextAction());
                    deletePrevious = copyField.getActionMap().get("delete-previous");
                    copyField.getActionMap().put("delete-previous", new CustomDeletePreviousAction());
                }
                //else, disable selection and override insertion and deletion
                copyField.getActionMap().put("cut-to-clipboard", new CustomAction());
                copyField.getActionMap().put("copy-to-clipboard", new CustomAction());
                copyField.getActionMap().put("paste-from-clipboard", new CustomAction());
                copyField.getActionMap().put("select-all", new CustomAction());
                copyField.getActionMap().put("selection-backward", new CustomAction());
                copyField.getActionMap().put("selection-begin", new CustomAction());
                copyField.getActionMap().put("selection-begin-line", new CustomAction());
                copyField.getActionMap().put("selection-begin-word", new CustomAction());
                copyField.getActionMap().put("selection-down", new CustomAction());
                copyField.getActionMap().put("selection-end", new CustomAction());
                copyField.getActionMap().put("selection-end-line", new CustomAction());
                copyField.getActionMap().put("selection-end-word", new CustomAction());
                copyField.getActionMap().put("selection-forward", new CustomAction());
                copyField.getActionMap().put("selection-next-word", new CustomAction());
                copyField.getActionMap().put("selection-previous-word", new CustomAction());
                copyField.getActionMap().put("selection-up", new CustomAction());
                copyField.getActionMap().put("select-line", new CustomAction());
                copyField.getActionMap().put("select-word", new CustomAction());
                insert = copyField.getActionMap().get("insert-content");
                copyField.getActionMap().put("insert-content", new CustomInsertContentAction());
                copyField.getActionMap().put("delete-next-word", new CustomAction());
                copyField.getActionMap().put("delete-previous-word", new CustomAction());
            }
            
            @Override
       	public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
                    if (!editMode) {
                        fb.setDot(copyField.getText().length(), bias);
                    } else {
                        fb.setDot(dot, bias);
                    }        
    	}
    
            @Override
           	public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
                if (!editMode) {
                    fb.moveDot(copyField.getText().length(), bias);
                } else {
                    fb.moveDot(dot, bias);
                }        
    	}        
        }
            
        public class StartButtonActionListener implements ActionListener {
    
            public void actionPerformed(ActionEvent arg0) {
                startButton.setEnabled(false);
                copyField.setEditable(true);
                copyField.setNavigationFilter(new EditionNavigationFilter());
            }
        }
    
        public static void main(String[] args) {
            try {           
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (UnsupportedLookAndFeelException ex) {
                ex.printStackTrace();
            } catch (IllegalAccessException ex) {
                ex.printStackTrace();
            } catch (InstantiationException ex) {
                ex.printStackTrace();
            } catch (ClassNotFoundException ex) {
                ex.printStackTrace();
            }
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    createAndShowGUI();
                }
            });
        }
        
        
        private static void createAndShowGUI() {
            JDialog frame = new JDialog();
            frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            frame.setTitle("Write-only Text Field");
            frame.setModal(true);
            JComponent newContentPane = new WriteOnlyTextField();
            newContentPane.setOpaque(true); 
            frame.setContentPane(newContentPane);
            frame.setPreferredSize(new Dimension(640, 480));
            frame.setLocationRelativeTo(null);
            frame.pack();        
            frame.setVisible(true);
        }
    
        public WriteOnlyTextField() {
            super(new BorderLayout());
            wordList = new LinkedList<String>();
            wordList.add("word1");
            wordList.add("word2");
            wordList.add("word3");
            Collections.shuffle(wordList);
            StringBuilder labelString = new StringBuilder();
            if (wordList.isEmpty()) {
                sampleText="";
            } else {
                for (String s : wordList) {
                    labelString.append(s+" ");
                }
                labelString.delete(labelString.length()-1,labelString.length());
            } 
            sampleText =  labelString.toString();
            JPanel textPanel = new JPanel();
            textPanel.setOpaque(true);
            sampleLabel = new JLabel("");
            sampleLabel.setBackground(Color.BLACK);
            sampleLabel.setForeground(Color.WHITE);
            sampleLabel.setOpaque(true);
            sampleLabel.setPreferredSize(new Dimension(635,200));
            sampleLabel.setText(sampleText);
            copyField = new JTextField("");
            copyField.setBackground(Color.BLACK);
            copyField.setForeground(Color.WHITE);
            copyField.setOpaque(true);
            copyField.setPreferredSize(new Dimension (635,200));
            copyField.setEditable(false);
            textPanel.add(sampleLabel,BorderLayout.CENTER);
            textPanel.add(copyField,BorderLayout.PAGE_END);
            JPanel buttonPanel = new JPanel();
            startButton = new JButton("Start");
            startButton.addActionListener(new StartButtonActionListener());
            buttonPanel.add(startButton);
            add(textPanel,BorderLayout.CENTER);
            add(buttonPanel,BorderLayout.PAGE_END);
            System.out.println("Sample Text and length: "+sampleLabel.getText()+", "+sampleLabel.getText().length());
            System.out.println("User Text and length: "+copyField.getText()+", "+copyField.getText().length());
        }          
    }
    Now, I just need to find why it's not running the code that prints the errors in the "else" branches on the CustomInsertContentAction method. It seems that whenever I call insert.actionPerformed(null), it returns and doesn't run any code placed after that call.

    What I wanted to do was to display words from a list in random order in a JLabel, and have the user copy the words in the text field, and check how many mistakes were made.
    If the text in the text field can be deleted, the program should check if it matches the text of the label, and then display the number of times the user deleted a character plus the number of times the user changed a character (change = delete and type in the same position) plus the number of times the user inserts text before another character (so the position of the caret at the time of the insertion is not the end of the text). If the text can't be deleted (in which case inserting text should only be allowed at the end of existing text), the program should check the lengths of the label's text and the field's text and if they match, count how many letters from the field are not equal to the letter in the same position in the JLabel and display that number.

    However, for some reason, it seems that the CustomInsertContentAction is not even running. Or at least, the calls to System.out.println() that I have placed on it to easily check what's happening and the values of some variables are not. The (not so) funny thing is that the other custom actions are running fine, and I replaced the default InsertContentAction by my CustomInsertContentAction in the same way I replaced the other default actions with my custom actions, so this one should be working too, and I don't see why the print calls wouldn't be executed if the action is working. So, does anyone know what am I doing wrong?

Similar Threads

  1. JTextField
    By gancio in forum AWT / Swing
    Replies: 20
    Last Post: 08-26-2009, 04:11 PM
  2. how to additem in jtextfield
    By santhosh_el in forum AWT / Swing
    Replies: 3
    Last Post: 05-07-2009, 03:16 PM
  3. Replies: 1
    Last Post: 01-30-2009, 07:44 PM
  4. JtextField
    By kashifu in forum Advanced Java
    Replies: 2
    Last Post: 06-27-2008, 05:25 PM
  5. help with JTextfield
    By gary in forum New To Java
    Replies: 4
    Last Post: 07-11-2007, 02:58 PM

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •