• 追加された行はこの色です。
  • 削除された行はこの色です。
---
category: swing
folder: Scrollspy
title: JEditorPaneのスクロールに連動してJTreeのノードを選択する
tags: [JEditorPane, JTree, JScrollPane, HTMLDocument, Html]
author: aterai
pubdate: 2020-01-13T03:43:21+09:00
description: JEditorPaneのスクロールに連動して表示状態になったリンクと同名のJTreeノードを検索・選択します。
image: https://drive.google.com/uc?id=1WPiIXVEn_n8seXVfgwCByMIWUPocOwmF
hreflang:
    href: https://java-swing-tips.blogspot.com/2020/01/automatically-update-jtree-node.html
    lang: en
---
* 概要 [#summary]
`JEditorPane`のスクロールに連動して表示状態になったリンクと同名の`JTree`ノードを検索・選択します。

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

* サンプルコード [#sourcecode]
#code(link){{
JScrollPane scroll = new JScrollPane(editor);
scroll.getVerticalScrollBar().getModel().addChangeListener(e -> {
  HTMLDocument.Iterator itr = doc.getIterator(HTML.Tag.A);
  for (; itr.isValid(); itr.next()) {
    try {
      Rectangle r = editor.modelToView(itr.getStartOffset());
      if (r != null && editor.getVisibleRect().contains(r.getLocation())) {
        searchTreeNode(tree, itr.getAttributes().getAttribute(HTML.Attribute.NAME));
        break;
      }
    } catch (BadLocationException ex) {
      UIManager.getLookAndFeel().provideErrorFeedback(editor);
    }
  }
});
}}

* 解説 [#explanation]
上記のサンプルでは、`JEditorPane`を配置した`JScrollPane`の縦`ScrollBar`に`ChangeListener`を設定し、表示状態になったリンクと同名の`JTree`ノードを検索して選択します。

- リンクが表示されているかは`HTMLDocument#getIterator(HTML.Tag.A)`で取得した`<a>`タグの先頭位置が`JEditorPane#getVisibleRect()`で取得した表示範囲に含まれるかで判断
- リンクが表示されているかは`HTMLDocument#getIterator(HTML.Tag.A)`で取得した`<a>`タグの先頭位置が`JEditorPane#getVisibleRect()`で取得した表示範囲に含まれるかで判断する
- `JTree`側にはノードをマウスクリックなどで選択したら`JEditorPane#scrollToReference(ref)`でそのリンクまでスクロールする`TreeSelectionListener`を追加しているので、`JEditorPane`側からのノード選択を実行する場合はその`TreeSelectionListener`が反応しないよう設定する必要がある
#code{{
tree.addTreeSelectionListener(e -> {
  if (!tree.isEnabled()) { // JEditorPane側からのノード選択は無視する
    return;
  }
  Object o = e.getNewLeadSelectionPath().getLastPathComponent();
  if (o instanceof DefaultMutableTreeNode) {
    DefaultMutableTreeNode node = (DefaultMutableTreeNode) o;
    String ref = Objects.toString(node.getUserObject());
    editor.scrollToReference(ref);
  }
});
// ...
private static void searchTreeNode(JTree tree, Object name) {
  TreeModel model = tree.getModel();
  DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
  Collections.list((Enumeration<?>) root.preorderEnumeration()).stream()
      .filter(DefaultMutableTreeNode.class::isInstance)
      .map(DefaultMutableTreeNode.class::cast)
      .filter(node -> Objects.equals(name, Objects.toString(node.getUserObject())))
      .findFirst()
      .ifPresent(node -> {
        tree.setEnabled(false); // JTreeに設定したTreeSelectionListenerを無効にする
        TreePath path = new TreePath(node.getPath());
        tree.setSelectionPath(path);
        tree.scrollPathToVisible(path);
        tree.setEnabled(true); // JTreeに設定したTreeSelectionListenerを有効に戻す
      });
}
}}

* 参考リンク [#reference]
- [[JEditorPane内のリンク参照位置までスクロールする>Swing/ScrollToReference]]

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