---
category: swing
folder: MiniMap
title: JEditorPaneのミニマップを表示する
tags: [JEditorPane, JScrollPane, JScrollBar, ImgeIcon, LayoutManager]
author: aterai
pubdate: 2019-08-12T20:46:57+09:00
description: JEditorPaneのミニマップをImageIconで作成し、これをLayoutManagerを使用してJScrollPane上に重ねて表示します。
image: https://drive.google.com/uc?id=1_Ix_ksss07Sfpz-vM0rEWJ_yorOS9dX8
---
* 概要 [#summary]
`JEditorPane`のミニマップを`ImageIcon`で作成し、これを`LayoutManager`を使用して`JScrollPane`上に重ねて表示します。

#download(https://drive.google.com/uc?id=1_Ix_ksss07Sfpz-vM0rEWJ_yorOS9dX8)

* サンプルコード [#sourcecode]
#code(link){{
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);
}}

* 解説 [#explanation]
- `JEditorPane`の縮小画像から`ImageIcon`を生成して`JLabel`に設定、さらにスクロールバーを非表示にした`JScrollPane`に配置してミニマップを作成
-- [[JTabbedPaneのサムネイルをJToolTipで表示>Swing/TabThumbnail]]
- `JEditorPane`を配置した`JScrollPane`とミニマップを配置した`JScrollPane`を重ねて配置する`LayoutManager`を使用
-- [[Component上に重ねて配置したダイアログの表示状態をアニメーション付きで切り替える>Swing/OverlayBorderLayout]]
- ミニマップとして使用する`JLabel`に`MouseListener`を追加してマウスで表示位置を変更可能に設定
-- [[JLabelとIconで作成した検索位置表示バーをマウスで操作する>Swing/BoundedRangeModel]]

* 参考リンク [#reference]
- [[JLabelとIconで作成した検索位置表示バーをマウスで操作する>Swing/BoundedRangeModel]]
- [[Component上に重ねて配置したダイアログの表示状態をアニメーション付きで切り替える>Swing/OverlayBorderLayout]]
- [[JTabbedPaneのサムネイルをJToolTipで表示>Swing/TabThumbnail]]

* コメント [#comment]
#comment
#comment