• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JTabbedPane間でタブのドラッグ&ドロップ移動
#navi(../)
RIGHT:Posted by [[terai]] at 2009-03-23
*JTabbedPane間でタブのドラッグ&ドロップ移動 [#yae9971e]
JTabbedPane間でタブのDrag&Dropによる移動を行います。

-&jnlp;
-&jar;
-&zip;

#screenshot

**サンプルコード [#e06345a4]
#code{{
class TabTransferHandler extends TransferHandler {
  private final DataFlavor localObjectFlavor;
  public TabTransferHandler() {
    System.out.println("TabTransferHandler");
    localObjectFlavor = new ActivationDataFlavor(DnDTabbedPane.class,
      DataFlavor.javaJVMLocalObjectMimeType, "DnDTabbedPane");
  }
  @Override
  protected Transferable createTransferable(JComponent c) {
    System.out.println("createTransferable");
    return new DataHandler(c, localObjectFlavor.getMimeType());
  }
  @Override
  public boolean canImport(TransferHandler.TransferSupport support) {
    //System.out.println("canImport");
    if (!support.isDrop() || !support.isDataFlavorSupported(localObjectFlavor)) {
      System.out.println("canImport:"+support.isDrop()+" "
                         +support.isDataFlavorSupported(localObjectFlavor));
      return false;
    }
    support.setDropAction(TransferHandler.MOVE);
    TransferHandler.DropLocation tdl = support.getDropLocation();
    Point pt = tdl.getDropPoint();
    DnDTabbedPane target = (DnDTabbedPane)support.getComponent();
    target.autoScrollTest(pt);
    DnDTabbedPane.DropLocation dl =
      (DnDTabbedPane.DropLocation)target.dropLocationForPoint(pt);
    int idx = dl.getIndex();
    boolean isDropable = false;
    try {
      DnDTabbedPane source = (DnDTabbedPane)support.getTransferable()
                              .getTransferData(localObjectFlavor);
      if (target==source) {
        //System.out.println("target==source");
        isDropable = target.getTabAreaBounds().contains(pt)
                     && idx>=0 && idx!=target.dragTabIndex
                     && idx!=target.dragTabIndex+1;
      } else {
        //System.out.println("target!=source");
        //System.out.println("  target: "+target.getName());
        //System.out.println("  source: "+source.getName());
        int srcIdx = source.dragTabIndex;
        isDropable = target.getTabAreaBounds().contains(pt) && idx>=0;
      }
    } catch (UnsupportedFlavorException ufe) {
      ufe.printStackTrace();
    } catch (java.io.IOException ioe) {
      ioe.printStackTrace();
    }
    if (isDropable) {
      support.setShowDropLocation(true);
      dl.setDropable(true);
      target.setDropLocation(dl, null, true);
      return true;
    } else {
      support.setShowDropLocation(false);
      dl.setDropable(false);
      target.setDropLocation(dl, null, false);
      return false;
    }
  }
  @Override
  public int getSourceActions(JComponent c) {
    System.out.println("getSourceActions");
    DnDTabbedPane src = (DnDTabbedPane)c;
    if (glassPane==null) glassPane = new GhostGlassPane(src);

    Rectangle rect = src.getBoundsAt(src.dragTabIndex);
    BufferedImage image = new BufferedImage(
      c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics g = image.getGraphics();
    c.paint(g);
    if (rect.x<0) {
      rect.translate(-rect.x,0);
    }
    if (rect.y<0) {
      rect.translate(0,-rect.y);
    }
    if (rect.x+rect.width>image.getWidth()) {
      rect.width = image.getWidth() - rect.x;
    }
    if (rect.y+rect.height>image.getHeight()) {
      rect.height = image.getHeight() - rect.y;
    }
    BufferedImage img = image.getSubimage(rect.x,rect.y,rect.width,rect.height);
    glassPane.setImage(img);
    //setDragImage(img); //java 1.7.0-ea-b84
    c.getRootPane().setGlassPane(glassPane);
    glassPane.setVisible(true);
    return TransferHandler.MOVE;
  }
  @Override
  public boolean importData(TransferHandler.TransferSupport support) {
    System.out.println("importData");
    if (!canImport(support)) return false;
    DnDTabbedPane target = (DnDTabbedPane)support.getComponent();
    DnDTabbedPane.DropLocation dl = target.getDropLocation();
    try {
      DnDTabbedPane source = (DnDTabbedPane)support.getTransferable()
                              .getTransferData(localObjectFlavor);
      int index = dl.getIndex(); //boolean insert = dl.isInsert();
      if (target==source) {
        source.convertTab(source.dragTabIndex, index);
      } else {
        source.exportTab(source.dragTabIndex, target, index);
      }
      return true;
    } catch (UnsupportedFlavorException ufe) {
      ufe.printStackTrace();
    } catch (java.io.IOException ioe) {
      ioe.printStackTrace();
    }
    return false;
  }
  @Override
  protected void exportDone(JComponent c, Transferable data, int action) {
    System.out.println("exportDone");
    DnDTabbedPane src = (DnDTabbedPane)c;
    c.getRootPane().getGlassPane().setVisible(false);
    src.setDropLocation(null, null, false);
  }
  private GhostGlassPane glassPane;
}
}}

**解説 [#o96b9e3f]
上記のサンプルでは、JDK 6 で導入された、TransferHandler.DropLocationを継承するDnDTabbedPane.DropLocationなどを作成して、JTabbedPane間でタブの移動ができるように設定しています。

-注意点
--自身の子であるJTabbedPaneにタブを移動することはできない
--子コンポーネントがnull(例えばaddTab("Tab",null))のタブは移動不可
--タブが選択不可(例えばsetEnabledAt(idx, false))の場合は移動不可
-バグ?
--%%子コンポーネントがJTableの場合、マウスカーソルが点滅する%%
---%%Windows環境のみ?%%
--子コンポーネントがJTextAreaなどの場合、ドラッグ中のタブゴーストが表示できない
---%%JTable、JTextAreaどちらも、JScrollPaneが影響している?%%
---java 1.7.0-ea-b84 で、TransferHandler#setDragImage(Image) を使用するとちゃんと表示される
--SCROLL_TAB_LAYOUTの場合、タブゴーストにスクロールボタンが表示される場合がある
>
#screenshot(,screenshot1.png)

**参考リンク [#y58f4e18]
-[[JTabbedPaneのタブをドラッグ&ドロップ>Swing/DnDTabbedPane]]

**コメント [#gcad5394]
- タブのドラッグ中、JTable上などでCursorが点滅するのを修正。 -- [[terai]] &new{2009-05-19 (火) 17:32:38};
- 点滅の原因は? -- [[Dad]] &new{2010-01-16 (土) 01:48:13};
-- おそらく、[[Cursor flickering during D&D when using CellRendererPane with validation>http://bugs.sun.com/view_bug.do?bug_id=6700748]]が原因。現在は、canImport メソッド内で、一々GlassPane#setCursor(isDropable?DragSource.DefaultMoveDrop:DragSource.DefaultMoveNoDrop);として回避中。 -- [[terai]] &new{2010-01-16 (土) 12:25:38};
- 6u20ぐらいからWebStartで、java.security.AccessControlException: access denied (java.awt.AWTPermission accessClipboard)? -- [[terai]] &new{2010-06-17 (木) 01:57:34};

#comment