0

If the text area has the focus, of course the PgUp and PgDn keys work fine, but I was hoping to scroll it up or down using key bindings, leaving the focus where it is instead of moving it to text area and back.

So I've mapped VK_PAGEDOWN with CTRL_DOWN_MASK to a menu items in hopes that when the user presses Ctrl+PgDn the program would scroll the text area by 20 lines.

But while txaOutput.getLineCount() returns number of lines in text area, I can't find a way to set the caret line to that number minus 20. txaOutput.setCaretPosition(int i) sets caret at BYTE number i.

What I've done is bogus, but it does scroll (Page Up is identical except - scrollBytes):

    PageDown = new KeyBoundMenuItem("PAGEDOWN", VK_PAGE_DOWN, CTRL_DOWN_MASK) 
    {
      @Override
      public void action(ActionEvent e) {
          scrollBytes = 20 * totalBytesInTextArea / txaOutput.getLineCount();
          try{
              txaOutput.setCaretPosition(txaOutput.getCaretPosition() + scrollBytes);
          }catch(Exception ex){}
      }
    };

Is there a way to set the caret to a particular line number in a JTextArea in a JScrollPane?

EDIT

The text area contains single words of 3 to 11 letters per line.

EDIT 2

Here's why it's action and not actionPerformed:

import java.awt.event.ActionEvent;
import javax.swing.*;
import static javax.swing.KeyStroke.getKeyStroke;

public abstract class KeyBoundMenuItem extends JMenuItem{

  public abstract void action(ActionEvent e);


      public KeyBoundMenuItem(String actionMapKey, int key, int mask)
      {
        Action myAction = new AbstractAction()
        {
          @Override public void actionPerformed(ActionEvent e)
          {
            action(e);
          }
        };  

        setAction(myAction);

        getInputMap(WHEN_IN_FOCUSED_WINDOW)
                      .put(getKeyStroke(key, mask),actionMapKey);
        getActionMap().put(                        actionMapKey, myAction);

      }
    }

EDIT 3

Could have used this instead of KeyBoundMenuItem:

  public static void shortcut(JMenuItem item, int mnem, int mods, int key)
  {  
    item.setMnemonic(mnem);
    item.setAccelerator(getKeyStroke(key,mods));
  }

But had code in place for the analogous class KeyBoundButton. Easy change to make late in day.

EDIT 4

Here's the sort of text needing scrolling up or down:

enter image description here

DSlomer64
  • 4,234
  • 4
  • 53
  • 88
  • Consider creating and posting a [mcve]. – Hovercraft Full Of Eels Aug 15 '15 at 23:54
  • I wonder if some of the methods in the [javax.swing.Utilities](http://docs.oracle.com/javase/8/docs/api/javax/swing/text/Utilities.html) class might help you. – Hovercraft Full Of Eels Aug 16 '15 at 00:03
  • I think the best solution here is to get the text from the JTextArea and manually scan for newlines. Keep a List of all the newline characters and their position in the text until you've reached the current caret position or the desired line number, whichever is further down. Then you can set the caret position on the desired newline. – Knox Aug 16 '15 at 00:41
  • You should be implementing `actionPerformed(...)`, not "action(...)". – camickr Aug 16 '15 at 01:07
  • @camickr--see EDIT 2. – DSlomer64 Aug 16 '15 at 01:21
  • 1
    @Knox: that won't work, since if set properly, text in a JTextArea will wrap when the line of text is almost at the width of the visible component, and when that happens, a new line is created *without* any newline characters. – Hovercraft Full Of Eels Aug 16 '15 at 01:30
  • Do note that @camickr provided exactly what I need, unless someone finds fault with it. My lines are all short so no problem with wrapping. – DSlomer64 Aug 16 '15 at 01:32
  • @DSlomer64, not sure why you are going to all that trouble. You can create a JMenuItem with an Action and set the accelerator keys on the Action. Swing will then do the Key Bindings for you and the best part is the accelerator key is now part of the menu item so you have self documentation of the key bindings. Read the section from the Swing tutorial on [How to Use Actions](http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html) for more information. – camickr Aug 16 '15 at 01:33
  • Already had analogous method `KeyBoundBUTTON` that was working but messed up UI. Easy change to make: change button to menuitem. Lazy, but no-brainer. Could have used my `shortcut` method in EDIT 3. – DSlomer64 Aug 16 '15 at 14:43

2 Answers2

2

Is there a way to set the caret to a particular line number

Check out the Text Utilities class. Methods like:

  1. getLineAtCaret() and
  2. gotoStartOfLine(...)

should allow you to do what you want. That is to scroll down you can do:

int currentLine = RXTextUtilities.getLineAtCaret(textArea);
RXTextUtilities.gotoStartOfLine(textArea, currentLine + 10);
camickr
  • 321,443
  • 19
  • 166
  • 288
  • Rob, you are the MAN! I have little doubt this will work for me, since you were right on the money with [Table Column Adjuster](https://tips4java.wordpress.com/2008/11/10/table-column-adjuster/). As usual, I'm at the end of my day/rope. Will look at Text Utilities tomorrow. – DSlomer64 Aug 16 '15 at 01:04
  • @camickr--Changed my mind. Thought about how easy it was to implement TCA, so took shot at TextUtilities. WORKS GREAT. I'd put you on the payroll if I had a payroll! Just added these two lines: `int currentLine = rxtextutilities.RXTextUtilities.getLineAtCaret(txaOutput); rxtextutilities.RXTextUtilities.gotoStartOfLine(txaOutput, currentLine + 28);` and similar to page up. – DSlomer64 Aug 16 '15 at 01:16
  • @DSlomer64, not sure why you are using `rxtextutilities.RXTextUtilities...`. The all the methods are static methods so you don't need to create an instance of the class, you just use `RXTextUtilities.???(...)`. I had a typo (forgot the "RX") in my original code, which I have since fixed. – camickr Aug 16 '15 at 01:23
  • Did it without thinking. Want to go home! Thanks for the heads-up. I didn't create an instance of the class; just used the methods as static. Qualifiers are the package name plus class name. Frustrated with Netbeans--it just needed clean and build. Here's what I'm using: `int currentLine = RXTextUtilities.getLineAtCaret(txaOutput); RXTextUtilities.gotoStartOfLine(txaOutput, currentLine + 28);` – DSlomer64 Aug 16 '15 at 01:30
0

Thanks to @camickr, here's what worked (code for PAGEUP is identical except for - instead of +:

PageDown = new KeyBoundMenuItem("PAGEDOWN", VK_PAGE_DOWN, CTRL_DOWN_MASK) 
{
  @Override
  public void action(ActionEvent e) 
  {
      int currentLine = RXTextUtilities.getLineAtCaret(txaOutput);
      RXTextUtilities.gotoStartOfLine(
        txaOutput, 
        currentLine + RXTextUtilities.maxLinesVisibleInWindow(txaOutput));
  }
};

I should probably have revised this code to reflect his valid criticism that I didn't have to make an abstract class to accomplish the key binding, but it ain't broke. (I may get around to it one day...)

I even figured out (by studying his Text Utilities) how to scroll the window without using a constant (28, see comments):

  public static int maxLinesVisibleInWindow(JTextComponent component)
  {
      Container container = getAncestorOfClass(JViewport.class, component);

    if (container == null) return 0;

    JViewport   viewport        = (JViewport)container;
    int         extentHeight    = viewport.getExtentSize().height;
    FontMetrics fm              = component.getFontMetrics(component.getFont());
    int         characterHeight = fm.getHeight();

    return (int)extentHeight/characterHeight - 1;
  }
DSlomer64
  • 4,234
  • 4
  • 53
  • 88