Google Tag Manager

2019/11/29

Change the word selection behavior by double clicking the mouse in JTextArea

Code

class SelectWordCaret extends DefaultCaret {
  private SelectingMode selectingMode = SelectingMode.CHAR;
  private int p0; // = Math.min(getDot(), getMark());
  private int p1; // = Math.max(getDot(), getMark());

  @Override public void mousePressed(MouseEvent e) {
    super.mousePressed(e);
    int clickCount = e.getClickCount();
    if (SwingUtilities.isLeftMouseButton(e) && !e.isConsumed()) {
      if (clickCount == 2) {
        selectingMode = SelectingMode.WORD;
        p0 = Math.min(getDot(), getMark());
        p1 = Math.max(getDot(), getMark());
      } else if (clickCount >= 3) {
        selectingMode = SelectingMode.ROW;
        JTextComponent target = getComponent();
        int offs = target.getCaretPosition();
        try {
          p0 = Utilities.getRowStart(target, offs);
          p1 = Utilities.getRowEnd(target, offs);
          setDot(p0);
          moveDot(p1);
        } catch (BadLocationException ex) {
          UIManager.getLookAndFeel().provideErrorFeedback(target);
        }
      }
    } else {
      selectingMode = SelectingMode.CHAR;
    }
  }

  @Override public void mouseDragged(MouseEvent e) {
    if (!e.isConsumed() && SwingUtilities.isLeftMouseButton(e)) {
      if (selectingMode == SelectingMode.WORD) {
        continuouslySelectWords(e);
      } else if (selectingMode == SelectingMode.ROW) {
        continuouslySelectRows(e);
      }
    } else {
      super.mouseDragged(e);
    }
  }

  // If you continue to drag the mouse after selecting a word with double press:
  private void continuouslySelectWords(MouseEvent e) {
    Position.Bias[] biasRet = new Position.Bias[1];
    JTextComponent c = getComponent();
    int pos = getCaretPositionByLocation(c, e.getPoint(), biasRet);
    try {
      if (p0 < pos && pos < p1) {
        setDot(p0);
        moveDot(p1, biasRet[0]);
      } else if (p1 < pos) {
        setDot(p0);
        moveDot(Utilities.getWordEnd(c, pos), biasRet[0]);
      } else if (p0 > pos) {
        setDot(p1);
        moveDot(Utilities.getWordStart(c, pos), biasRet[0]);
      }
    } catch (BadLocationException ex) {
      UIManager.getLookAndFeel().provideErrorFeedback(c);
    }
  }

  // If you drag the mouse continuously after selecting the entire line in triple press:
  private void continuouslySelectRows(MouseEvent e) {
    Position.Bias[] biasRet = new Position.Bias[1];
    JTextComponent c = getComponent();
    int pos = getCaretPositionByLocation(c, e.getPoint(), biasRet);
    try {
      if (p0 < pos && pos < p1) {
        setDot(p0);
        moveDot(p1, biasRet[0]);
      } else if (p1 < pos) {
        setDot(p0);
        moveDot(Utilities.getRowEnd(c, pos), biasRet[0]);
      } else if (p0 > pos) {
        setDot(p1);
        moveDot(Utilities.getRowStart(c, pos), biasRet[0]);
      }
    } catch (BadLocationException ex) {
      UIManager.getLookAndFeel().provideErrorFeedback(c);
    }
  }
  // ...
}

References

2019/10/30

Wraps JPanel child components horizontally and dynamically expands their horizontal spacing

Code

class ScrollableWrapLayout extends FlowLayout {
  private final int fixedHorizontalGap;

  protected ScrollableWrapLayout(int align, int hgap, int vgap) {
    super(align, hgap, vgap);
    fixedHorizontalGap = hgap;
  }

  private int getPreferredHorizontalGap(Container target) {
    Insets insets = target.getInsets();
    int columns = 0;
    int width = target.getWidth();
    if (target.getParent() instanceof JViewport) {
      width = target.getParent().getBounds().width;
    }
    width -= insets.left + insets.right + fixedHorizontalGap * 2;
    for (int i = 0; i < target.getComponentCount(); i++) {
      Component m = target.getComponent(i);
      if (m.isVisible()) {
        Dimension d = m.getPreferredSize();
        if (width - d.width - fixedHorizontalGap < 0) {
          columns = i;
          break;
        }
        width -= d.width + fixedHorizontalGap;
      }
    }
    return fixedHorizontalGap + width / columns;
  }

  @Override public void layoutContainer(Container target) {
    setHgap(getPreferredHorizontalGap(target));
    super.layoutContainer(target);
  }

  @Override public Dimension preferredLayoutSize(Container target) {
    Dimension dim = super.preferredLayoutSize(target);
    synchronized (target.getTreeLock()) {
      if (target.getParent() instanceof JViewport) {
        dim.width = target.getParent().getBounds().width;
        for (int i = 0; i < target.getComponentCount(); i++) {
          Component m = target.getComponent(i);
          if (m.isVisible()) {
            Dimension d = m.getPreferredSize();
            dim.height = Math.max(dim.height, d.height + m.getY());
          }
        }
      }
      return dim;
    }
  }
}

References

2019/09/30

Change drawing area of selected highlight in JEditorPane

Code

class ParagraphMarkHighlightPainter extends DefaultHighlightPainter {
  protected ParagraphMarkHighlightPainter(Color color) {
    super(color);
  }

  @Override public Shape paintLayer(
      Graphics g, int offs0, int offs1,
      Shape bounds, JTextComponent c, View view) {
    Shape s = super.paintLayer(g, offs0, offs1, bounds, c, view);
    Rectangle r = s.getBounds();
    if (r.width - 1 <= 0) {
      g.fillRect(r.x + r.width, r.y, r.width + r.height / 2, r.height);
    }
    return s;
  }
}

class WholeLineHighlightPainter extends DefaultHighlightPainter {
  protected WholeLineHighlightPainter(Color color) {
    super(color);
  }

  @Override public Shape paintLayer(
      Graphics g, int offs0, int offs1,
      Shape bounds, JTextComponent c, View view) {
    Rectangle rect = bounds.getBounds();
    rect.width = c.getSize().width;
    return super.paintLayer(g, offs0, offs1, rect, c, view);
  }
}

References

2019/08/31

Create a minimap of JEditorPane with ImageIcon and overlay it on JScrollPane using LayoutManager

Code

private final JEditorPane editor = new JEditorPane();
private final JScrollPane scroll = new JScrollPane(editor);
private final JLabel label = new JLabel() {
  private transient MouseInputListener handler;
  @Override public void updateUI() {
    removeMouseListener(handler);
    removeMouseMotionListener(handler);
    super.updateUI();
    handler = new MiniMapHandler();
    addMouseListener(handler);
    addMouseMotionListener(handler);
  }

  @Override public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Container c = SwingUtilities.getAncestorOfClass(JViewport.class, editor);
    if (!(c instanceof JViewport) || editor == null) {
      return;
    }
    JViewport vport = (JViewport) c;
    Rectangle vrect = vport.getBounds(); // scroll.getViewportBorderBounds();
    Rectangle erect = editor.getBounds();
    Rectangle crect = SwingUtilities.calculateInnerArea(this, new Rectangle());

    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
    double sy = crect.getHeight() / erect.getHeight();
    AffineTransform at = AffineTransform.getScaleInstance(1d, sy);

    // paint Thumb
    Rectangle thumbRect = new Rectangle(vrect);
    thumbRect.y = vport.getViewPosition().y;
    Rectangle r = at.createTransformedShape(thumbRect).getBounds();
    int y = crect.y + r.y;
    g2.setColor(THUMB_COLOR);
    g2.fillRect(0, y, crect.width, r.height);
    g2.setColor(THUMB_COLOR.darker());
    g2.drawRect(0, y, crect.width - 1, r.height - 1);
    g2.dispose();
  }
};

private class MiniMapHandler extends MouseInputAdapter {
  @Override public void mousePressed(MouseEvent e) {
    processMiniMapMouseEvent(e);
  }

  @Override public void mouseDragged(MouseEvent e) {
    processMiniMapMouseEvent(e);
  }

  protected final void processMiniMapMouseEvent(MouseEvent e) {
    Point pt = e.getPoint();
    Component c = (Component) e.getComponent();
    BoundedRangeModel m = scroll.getVerticalScrollBar().getModel();
    int brm = m.getMaximum() - m.getMinimum();
    int iv = (int) (.5 - m.getExtent() * .5 + pt.y * brm / (double) c.getHeight());
    m.setValue(iv);
  }
}
// ...
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll.getVerticalScrollBar().getModel().addChangeListener(e -> label.repaint());

JPanel pp = new JPanel(new BorderLayout(0, 0));
pp.add(label, BorderLayout.NORTH);
JScrollPane minimap = new JScrollPane(pp);
minimap.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
minimap.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

Box box = Box.createHorizontalBox();
box.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
box.add(Box.createHorizontalGlue());
box.add(button);

JPanel p = new JPanel() {
  @Override public boolean isOptimizedDrawingEnabled() {
    return false;
  }
};
p.setLayout(new BorderLayout(0, 0) {
  @Override public void layoutContainer(Container parent) {
    synchronized (parent.getTreeLock()) {
      Insets insets = parent.getInsets();
      int width = parent.getWidth();
      int height = parent.getHeight();
      int top = insets.top;
      int bottom = height - insets.bottom;
      int left = insets.left;
      int right = width - insets.right;
      Component ec = getLayoutComponent(parent, BorderLayout.EAST);
      if (Objects.nonNull(ec)) {
        Dimension d = ec.getPreferredSize();
        JScrollBar vsb = scroll.getVerticalScrollBar();
        int vsw = vsb.isVisible() ? vsb.getSize().width : 0;
        ec.setBounds(right - d.width - vsw, top, d.width, bottom - top);
      }
      Component cc = getLayoutComponent(parent, BorderLayout.CENTER);
      if (Objects.nonNull(cc)) {
        cc.setBounds(left, top, right - left, bottom - top);
      }
    }
  }
});
p.add(minimap, BorderLayout.EAST);
p.add(scroll);

References

2019/07/30

Enable focus move by tab key in cell editor of JTable

Code

JTable table = makeTable();
ActionMap am = table.getActionMap();
Action sncc = am.get("selectNextColumnCell");
Action action = new AbstractAction() {
  @Override public void actionPerformed(ActionEvent e) {
    if (!table.isEditing() || !isEditorFocusCycle(table.getEditorComponent())) {
      // System.out.println("Exit editor");
      sncc.actionPerformed(e);
    }
  }
};
am.put("selectNextColumnCell2", action);

InputMap im = table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "selectNextColumnCell2");
// ...
protected boolean isEditorFocusCycle(Component editor) {
  Component child = CheckBoxesEditor.getEditorFocusCycleAfter(editor);
  if (child != null) {
    child.requestFocus();
    return true;
  }
  return false;
}
// ...
public static Component getEditorFocusCycleAfter(Component editor) {
  Component fo = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
  if (fo == null || !(editor instanceof Container)) {
    return null;
  }
  Container root = (Container) editor;
  if (!root.isFocusCycleRoot()) {
    root = root.getFocusCycleRootAncestor();
  }
  if (root == null) {
    return null;
  }
  // System.out.println("FocusCycleRoot: " + root.getClass().getName());
  FocusTraversalPolicy ftp = root.getFocusTraversalPolicy();
  Component child = ftp.getComponentAfter(root, fo);
  if (child != null && SwingUtilities.isDescendingFrom(child, editor)) {
    // System.out.println("requestFocus: " + child.getClass().getName());
    // child.requestFocus();
    return child;
  }
  return null;
}

References