class NextVisualPositionAction extends TextAction {

    private boolean select;
    private int direction;
    private String command;

    NextVisualPositionAction(String nm, boolean select, int dir) {
//TODO forward selection space+image - space not selected alone
//TODO up/down might step left or right
      super(nm);

      this.select = select;
      this.direction = dir;
      this.command = nm;
    }

    private static int getNSVisualPosition(EditorPane txt, int pos, int direction) {
      Element root = txt.getDocument().getDefaultRootElement();
      int numLines = root.getElementIndex(txt.getDocument().getLength() - 1) + 1;
      int line = root.getElementIndex(pos) + 1;
      int tarLine = direction == SwingConstants.NORTH ? line - 1 : line + 1;
      try {
        if (tarLine <= 0) {
          return 0;
        }
        if (tarLine > numLines) {
          return txt.getDocument().getLength();
        }

        Rectangle curRect = txt.modelToView(pos);
        Rectangle tarEndRect;
        if (tarLine < numLines) {
          tarEndRect = txt.modelToView(txt.getLineStartOffset(tarLine) - 1);
        } else {
          tarEndRect = txt.modelToView(txt.getDocument().getLength() - 1);
        }
        Debug.log(9, "curRect: " + curRect + ", tarEnd: " + tarEndRect);

        if (curRect.x > tarEndRect.x) {
          pos = txt.viewToModel(new Point(tarEndRect.x, tarEndRect.y));
        } else {
          pos = txt.viewToModel(new Point(curRect.x, tarEndRect.y));
        }
      } catch (BadLocationException e) {
        Debug.error(me + "Problem getting next visual position\n%s", e.getMessage());
      }

      return pos;

    }

    @Override
    public void actionPerformed(ActionEvent e) {<FILL_FUNCTION_BODY>}
  }

      JTextComponent textArea = (JTextComponent) e.getSource();

      Caret caret = textArea.getCaret();
      int dot = caret.getDot();

      /*
       * Move to the beginning/end of selection on a "non-shifted"
       * left- or right-keypress.  We shouldn't have to worry about
       * navigation filters as, if one is being used, it let us get
       * to that position before.
       */
      if (!select) {
        switch (direction) {
          case SwingConstants.EAST:
            int mark = caret.getMark();
            if (dot != mark) {
              caret.setDot(Math.max(dot, mark));
              return;
            }
            break;
          case SwingConstants.WEST:
            mark = caret.getMark();
            if (dot != mark) {
              caret.setDot(Math.min(dot, mark));
              return;
            }
            break;
          default:
        }
      }

      Position.Bias[] bias = new Position.Bias[1];
      Point magicPosition = caret.getMagicCaretPosition();

      try {

        if (magicPosition == null
                && (direction == SwingConstants.NORTH
                || direction == SwingConstants.SOUTH)) {
          Rectangle r = textArea.modelToView(dot);
          magicPosition = new Point(r.x, r.y);
        }

        NavigationFilter filter = textArea.getNavigationFilter();

        if (filter != null) {
          dot = filter.getNextVisualPositionFrom(textArea, dot,
                  Position.Bias.Forward, direction, bias);
        } else {
          if (direction == SwingConstants.NORTH
                  || direction == SwingConstants.SOUTH) {
            dot = getNSVisualPosition((EditorPane) textArea, dot, direction);
          } else {
            dot = textArea.getUI().getNextVisualPositionFrom(
                    textArea, dot,
                    Position.Bias.Forward, direction, bias);
          }
        }
        if (select) {
          caret.moveDot(dot);
        } else {
          caret.setDot(dot);
        }

        if (magicPosition != null
                && (direction == SwingConstants.NORTH
                || direction == SwingConstants.SOUTH)) {
          caret.setMagicCaretPosition(magicPosition);
        }

      } catch (BadLocationException ble) {
        Debug.error(me + "Problem while trying to move caret\n%s", ble.getMessage());
      }

    
<methods>public void <init>() ,public java.lang.Object clone() ,public javax.swing.text.Document createDefaultDocument() ,public void deinstall(javax.swing.JEditorPane) ,public javax.swing.Action[] getActions() ,public javax.swing.text.Element getCharacterAttributeRun() ,public javax.swing.text.MutableAttributeSet getInputAttributes() ,public javax.swing.text.ViewFactory getViewFactory() ,public void install(javax.swing.JEditorPane) <variables>javax.swing.text.Element currentParagraph,javax.swing.text.Element currentRun,private static final javax.swing.Action[] defaultActions,private static final javax.swing.text.ViewFactory defaultFactory,private javax.swing.text.StyledEditorKit.AttributeTracker inputAttributeUpdater,javax.swing.text.MutableAttributeSet inputAttributes