javaswingswingxswingutilities

Unable to select "text with number and underscore" on DoubleClick in JTextPane


I have a text like, "test_1_another_2_test3" in my JTextPane. When I double click on the text, it does not select the whole text (sometimes, it selects only one character or number or the text between underscores). I can implement a mouseactionlistener, but have no idea how to implement the action.


Solution

  • I can implement a mouseactionlistener

    You should not implement a MouseListener.

    Swing works by using Actions. The DefaultEditorKit provides the default Action that selects the text on a double click.

    See Key Bindings. This class will show which Actions are mapped to a KeyStroke by default. It will also display all the default Actions for a component.

    So basically you need to replace the default Action. The default Action is called DefaultWordActon. This Action in turn calls StartWordAction and EndWordAction. It appears these two Actions stop searching when they find an underscore with a numeric value. So basically you need to modify this behaviour to keep searching.

    The following code adds a loop to keep searching when an underscore is found:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.text.*;
    
    public class SelectWordAction extends TextAction
    {
        public SelectWordAction()
        {
            super("Select Word");
        }
    
        @Override
        public void actionPerformed(ActionEvent e)
        {
            JTextComponent target = getTextComponent(e);
    
            if (target != null)
            {
                int offset = target.getCaretPosition();
                beginWord(target, offset);
                endWord(target, offset);
            }
        }
    
        private void beginWord(JTextComponent target, int offset)
        {
            try
            {
                boolean keepSearching = true;
                int beginOffset = Utilities.getWordStart(target, offset);
    
                while (beginOffset > 1 && keepSearching)
                {
                    String previousCharacter = target.getText(beginOffset - 1, 1);
    
                    if ("_".equals(previousCharacter))
                        beginOffset = Utilities.getWordStart(target, beginOffset - 2);
                    else
                        keepSearching = false;
                }
    
                target.setCaretPosition(beginOffset);
            }
            catch (BadLocationException bl)
            {
                UIManager.getLookAndFeel().provideErrorFeedback(target);
            }
        }
    
        private void endWord(JTextComponent target, int offset)
        {
            try
            {
                int length = target.getDocument().getLength() - 2;
                boolean keepSearching = true;
                int endOffset = Utilities.getWordEnd(target, offset);
    
                while (endOffset < length && keepSearching)
                {
                    String nextCharacter = target.getText(endOffset, 1);
    
                    if ("_".equals(nextCharacter))
                        endOffset = Utilities.getWordEnd(target, endOffset + 1);
                    else
                        keepSearching = false;
                }
    
                target.moveCaretPosition(endOffset);
            }
            catch (BadLocationException bl)
            {
                UIManager.getLookAndFeel().provideErrorFeedback(target);
            }
        }
    
    
        private static void createAndShowGUI()
        {
            JTextPane textPane = new JTextPane();
    
            Action action = new SelectWordAction();
            textPane.getActionMap().put("select-word", action);
    
            JFrame frame = new JFrame("SSCCE");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add( new JScrollPane( textPane ) );
            frame.setLocationByPlatform( true );
            frame.setSize(300, 300);
            frame.setVisible( true );
        }
    
        public static void main(String[] args)
        {
            EventQueue.invokeLater( () -> createAndShowGUI() );
    /*
            EventQueue.invokeLater(new Runnable()
            {
                public void run()
                {
                    createAndShowGUI();
                }
            });
    */
        }
    }