• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JTabbedPaneのタブタイトルを変更
#navi(../)
*JTabbedPaneのタブタイトルを変更 [#l3a0e3fa]
>編集者:[[Terai Atsuhiro>terai]]~
作成日:2006-03-27~
更新日:&lastmod;
---
category: swing
folder: EditTabTitle
title: JTabbedPaneのタブタイトルを変更
tags: [JTabbedPane, GlassPane, JTextField]
author: aterai
pubdate: 2006-03-27T01:01:49+09:00
description: JTabbedPaneのタブタイトルを直接編集します。
image: https://lh5.googleusercontent.com/_9Z4BYR88imo/TQTMGR-jIQI/AAAAAAAAAYo/g3tGLp5zrdY/s800/EditTabTitle.png
hreflang:
    href: https://java-swing-tips.blogspot.com/2008/09/double-click-on-each-tab-and-change-its.html
    lang: en
---
* 概要 [#summary]
`JTabbedPane`のタブタイトルを直接編集します。

#contents
#download(https://lh5.googleusercontent.com/_9Z4BYR88imo/TQTMGR-jIQI/AAAAAAAAAYo/g3tGLp5zrdY/s800/EditTabTitle.png)

**概要 [#ga7c35e5]
JTabbedPaneのタブタイトルを直接編集します。
* サンプルコード [#sourcecode]
#code(link){{
class EditableTabbedPane extends JTabbedPane {
  private final JComponent glassPane = new EditorGlassPane();
  private final JTextField editor = new JTextField();
  private final Action startEditing = new AbstractAction() {
    @Override public void actionPerformed(ActionEvent e) {
      getRootPane().setGlassPane(glassPane);
      Rectangle rect = getBoundsAt(getSelectedIndex());
      Point p = SwingUtilities.convertPoint(
          EditableTabbedPane.this, rect.getLocation(), glassPane);
      // rect.setBounds(p.x + 2, p.y + 2, rect.width - 4, rect.height - 4);
      rect.setLocation(p);
      rect.grow(-2, -2);
      editor.setBounds(rect);
      editor.setText(getTitleAt(getSelectedIndex()));
      editor.selectAll();
      glassPane.add(editor);
      glassPane.setVisible(true);
      editor.requestFocusInWindow();
    }
  };
  private final Action cancelEditing = new AbstractAction() {
    @Override public void actionPerformed(ActionEvent e) {
      glassPane.setVisible(false);
    }
  };
  private final Action renameTab = new AbstractAction() {
    @Override public void actionPerformed(ActionEvent e) {
      if (!editor.getText().trim().isEmpty()) {
        setTitleAt(getSelectedIndex(), editor.getText());
        //java 1.6.0 ---->
        Component c = getTabComponentAt(getSelectedIndex());
        if (c instanceof JComponent) {
          ((JComponent) c).revalidate();
        }
        //<----
      }
      glassPane.setVisible(false);
    }
  };

#screenshot
  protected EditableTabbedPane() {
    super();
    editor.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
    editor.getInputMap(JComponent.WHEN_FOCUSED).put(
        KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "rename-tab");
    editor.getActionMap().put("rename-tab", renameTab);
    editor.getInputMap(JComponent.WHEN_FOCUSED).put(
        KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel-editing");
    editor.getActionMap().put("cancel-editing", cancelEditing);

**サンプルコード [#o93646f5]
#code{{
 class EditableTabbedPane extends JTabbedPane {
   private final MyGlassPane panel  = new MyGlassPane();
   private final JTextField  editor = new JTextField();
   private final JFrame      frame;
   private final FocusTraversalPolicy policy;
   private FocusTraversalPolicy ftp;
   private Rectangle rect;
   
   public EditableTabbedPane(JFrame frame) {
     this.frame = frame;
 ......
     panel.add(editor);
     frame.setGlassPane(panel);
     panel.setVisible(false);
   }
   class MyGlassPane extends JPanel{
     public MyGlassPane() {
       super((LayoutManager)null);
       setOpaque(false);
       addMouseListener(new MouseAdapter() {
         public void mouseClicked(MouseEvent me) {
           if(rect==null || rect.contains(me.getPoint())) return;
           renameTab();
         }
       });
     }
   }
   private void initEditor() {
     rect = getUI().getTabBounds(this, getSelectedIndex());
     rect.setRect(rect.x+2, rect.y+2, rect.width-2, rect.height-2);
     editor.setBounds(rect);
     editor.setText(getTitleAt(getSelectedIndex()));
   }
   private void startEditing() {
     initEditor();
     ftp = frame.getFocusTraversalPolicy();
     panel.setVisible(true);
     editor.requestFocusInWindow();
     frame.setFocusTraversalPolicy(policy);
   }
   private void cancelEditing() {
     frame.setFocusTraversalPolicy(ftp);
     panel.setVisible(false);
   }
   private void renameTab() {
     frame.setFocusTraversalPolicy(ftp);
     if(editor.getText().trim().length()>0) {
       setTitleAt(getSelectedIndex(), editor.getText());
     }
     panel.setVisible(false);
   }
 }
    addMouseListener(new MouseAdapter() {
      @Override public void mouseClicked(MouseEvent e) {
        if (e.getClickCount() == 2) {
          startEditing.actionPerformed(
            new ActionEvent(e.getComponent(), ActionEvent.ACTION_PERFORMED, ""));
        }
      }
    });
    getInputMap(JComponent.WHEN_FOCUSED).put(
        KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "start-editing");
    getActionMap().put("start-editing", startEditing);
  }

  private class EditorGlassPane extends JComponent {
    protected EditorGlassPane() {
      super();
      setOpaque(false);
      setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {
        @Override public boolean accept(Component c) {
          return Objects.equals(c, editor);
        }
      });
      addMouseListener(new MouseAdapter() {
        @Override public void mouseClicked(MouseEvent e) {
          // if (Objects.nonNull(rect) && !rect.contains(e.getPoint())) {
          if (!editor.getBounds().contains(e.getPoint())) {
            renameTab.actionPerformed(
              new ActionEvent(e.getComponent(), ActionEvent.ACTION_PERFORMED, ""));
          }
        }
      });
    }

    @Override public void setVisible(boolean flag) {
      super.setVisible(flag);
      setFocusTraversalPolicyProvider(flag);
      setFocusCycleRoot(flag);
    }
  }
}
}}
-&jnlp;
-&jar;
-&zip;

**解説 [#t11e99b7]
Excel風にJTabbedPaneのタブタイトルを直接編集しています。
* 解説 [#explanation]
上記のサンプルでは、`JTabbedPane`のタブタイトルを`Excel`などのように直接編集できるよう設定しています。

編集が開始されると、対象となるタブ上にJTextFieldをレイアウトしたGlassPaneを表示しています。編集中はフォーカスの移動が起こらないようにするためのFocusTraversalPolicyを設定しています。
編集が開始されると対象となるタブ上に`JTextField`をレイアウトした`GlassPane`を表示しています。この`GlassPane`には編集中にフォーカス移動イベントが発生しないようにするための`FocusTraversalPolicy`などを追加しています。

-操作方法
--マウスでタブをダブルクリック、またはタブを選択してリターンキーで編集開始
--編集中に入力欄以外をクリック、またはリターンキーでタイトル文字列が確定
--編集中タブキーを押しても無視
--Escで編集をキャンセル
--0文字で確定した場合も、キャンセル扱い
- 操作方法
-- マウスでタブをダブルクリック、またはタブを選択してKBD{Enter}キーで編集開始
-- 編集中に入力欄以外をクリック、またはKBD{Enter}キーでタイトル文字列が確定
-- 編集中はKBD{Tab}キーを押しても無視
-- KBD{Esc}キーで編集をキャンセル
-- `0`文字で確定した場合もキャンセル扱い

**参考リンク [#k3c4771a]
-[[Cursorを砂時計に変更>Swing/WaitCursor]]
* 参考リンク [#reference]
- [[Cursorを砂時計に変更>Swing/WaitCursor]]
- [[JTabbedPaneのタブにJTextFieldを配置してタイトルを編集>Swing/TabTitleEditor]]
-- `JDK 6`で追加された`JTabbedPane#setTabComponentAt`メソッドで、タブに`JTextField`を配置し、同様のことを行うサンプル
- [https://community.oracle.com/thread/1359811 Swing - Floating text field]
-- `JPopupMenu`に`JTextField`を配置することで、同様のことを行うコードをKelVarnsonさんが投稿している

**コメント [#s3b3f708]
- 1.4系だと編集開始時にうまく選択状態にできない場合があるようです。 -- [[terai]] &new{2006-03-27 (月) 15:03:01};
-- 上記の問題と、選択状態がマウスの移動で外れてしまうバグを修正しました。 -- [[terai]] &new{2006-03-29 (水) 03:26:56};
- 余白などを追加するとエディタがずれるバグを修正しました。 -- [[terai]] &new{2006-09-01 (金) 15:04:00};
* コメント [#comment]
#comment
- `1.4`系だと編集開始時にうまく選択状態にできない場合があるようです。 -- &user(aterai); &new{2006-03-27 (月) 15:03:01};
-- 上記の問題と、選択状態がマウスの移動で外れてしまうバグを修正しました。 -- &user(aterai); &new{2006-03-29 (水) 03:26:56};
- 余白などを追加するとエディタがずれるバグを修正しました。 -- &user(aterai); &new{2006-09-01 (金) 15:04:00};
- `setTabComponentAt(...)`メソッドで閉じるボタンなどと併用していた場合、編集後に文字列の長さがおかしくなるので、`revalidate()`するように修正。 -- &user(aterai); &new{2010-08-10 (火) 16:49:52};

#comment