2008年1月3日木曜日

Java6 JTree クリップボードにコピー、切り取り、貼り付けテスト


package JTreeClipBoardTest;
import java.awt.BorderLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

/**
* Java6 JTree ClipBoard Copy Cut Paste Test

* Java6 JTree クリップボードにコピー、切り取り、貼り付けテスト

*

* 起動すると、JTreeが2枚表示されます。

* JTree間でコピペ、カトペ、ドラッグアンドドロップできます。

* 他のアプリケーションからドロップしようとしても禁止マークがでます。

* DnDでムーブ、Ctrl+DnDでコピーも有効です。

* ※JTreeDnDTest.javaをベースに作成したのですが、コピペ対応のために弄ってる間に、対応できちゃったようです。

*/
public class JTreeClipBoardTest {

/**
* @param args
*/
public static void main(String[] args) {
JFrame f = new JFrame();
JTree t = new MyTree3();
JTree t2 = new MyTree3();
f.add(t, BorderLayout.CENTER);
f.add(t2, BorderLayout.EAST);
f.setSize(200, 200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}

/**
* JTreeを継承して、初期化メソッドと、コピペ、DnD用のセッタゲッタを追加したクラス。
*/
class MyTree3 extends JTree {
public MyTree3() {
super();
init();
}

private void init() {
// ドラッグを許可します。
setDragEnabled(true);
// トランスファハンドラを設定します。
setTransferHandler(new TreePathTransferHandler("selectionPathsDnd"));
// ※selectionPathsDndは、内部でsetほにゃらら、getほにゃららのメソッド検索に使われます。
}
/**
* TransferHandlerのプロパティに対応するため、DnD、コピペ用のセッタゲッタを用意します。
* @return
*/
public TreePath[] getSelectionPathsDnd() {
return getSelectionPaths();
}
/**
* 名前はDndついてますが、コピペでも同じメソッドが使われます。
* @param paths
* @return
*/
public boolean setSelectionPathsDnd(TreePath[] paths) {
if (paths == null ) {
return false;
}
javax.swing.JTree.DropLocation loc = getDropLocation();
TreePath putPath = null;
if (loc != null) {
// ドロップしたパスを取得します。
putPath = loc.getPath();
} else {
// ドロップロケーションがnullだったら、貼り付けと判断して選択済みのパスを取得します。
putPath = getSelectionPath();
}
DefaultMutableTreeNode putNode = (DefaultMutableTreeNode)putPath.getLastPathComponent();
for (TreePath path : paths) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
putNode.add(node);
((DefaultTreeModel)getModel()).reload();
}
return true;
}
}
/**
* ツリーパストランスファハンドラ
*/
class TreePathTransferHandler extends TransferHandler {
/**
* コンストラクタ プロパティ設定なし
*/
public TreePathTransferHandler() {
super();
}
/**
* コンストラクタ プロパティ設定あり
*/
public TreePathTransferHandler(String property) {
super(property);
}
/**
* 転送データ作成処理。
*/
@Override
protected Transferable createTransferable(JComponent c){
if (c instanceof MyTree3) {
MyTree3 tree = (MyTree3)c;
Transferable tf = new TreePathTransferable(tree);
return tf;
}
return super.createTransferable(c);
}
/**
* アクションを返します。
*/
@Override
public int getSourceActions(JComponent c) {
// スーパークラスの戻り値がCOPYの場合、COPY_OR_MOVEに変換します。
// ※スーパークラスがCOPYかNONEしか返さないため。
int action = super.getSourceActions(c);
if (action == COPY) {
action = COPY_OR_MOVE;
}
return action;
}
@Override
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
// 受け入れ可能かどうかを返します。
// ツリーパスフレーバ以外のフレーバを受け入れないようにします。
for (DataFlavor df : transferFlavors) {
if (!df.equals(TreePathDataFravor.treePathFravor)) {
return false;
}
}
return true;
}

/**
* 出力側のドロップ完了処理。
* actionがMOVEの場合、ツリーを削除します。
* ※importDataがfalseを返した場合、actionはNONEになります。
*/
@Override
protected void exportDone(JComponent source, Transferable data,
int action) {
if (!(source instanceof JTree)) {
return;
}
MyTree3 tree = (MyTree3)source;
if (action == MOVE) {
try {
for (DataFlavor df : data.getTransferDataFlavors()) {
TreePath[] patha = (TreePath[]) data.getTransferData(df);
TreePath path = patha[0];
Object oo = path.getLastPathComponent();
DefaultMutableTreeNode node = (DefaultMutableTreeNode)oo;
node.removeFromParent();
((DefaultTreeModel)tree.getModel()).reload();
}
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} else {
super.exportDone(source, data, action);
}
}
/**
* 入力側のドロップ処理。
* ドロップされたノードに、ドラッグされたノードを追加します。
*/
@Override
public boolean importData(JComponent comp, Transferable t) {
if (!(comp instanceof MyTree3)) {
return false;
}
if (!canImport(comp, t.getTransferDataFlavors())) {
return false;
}
MyTree3 tree = (MyTree3)comp;
DataFlavor[] dfa = t.getTransferDataFlavors();
for (DataFlavor df : dfa) {
try {
TreePath[] orgPathA = (TreePath[])t.getTransferData(df);
return tree.setSelectionPathsDnd(orgPathA);
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}

}
return false;
}
}
/**
* ツリーパスデータフレーバクラス。
* 作ったはいいけど、ツリーパスフレーバーの定数を保管しているだけである。
*/
class TreePathDataFravor extends DataFlavor {
public static final DataFlavor treePathFravor = new DataFlavor(TreePath[].class, javaJVMLocalObjectMimeType);
}
/**
* ツリーパス用トランスファエイブル
* ツリーパスの配列と、データフレーバーの配列を持つ。
*/
class TreePathTransferable implements Transferable
{
TreePath[] paths;
// データフレーバ(フレーバ=食品への風味付け)
static final DataFlavor[] dflv = {TreePathDataFravor.treePathFravor};

public TreePathTransferable(TreePath[] paths){
this.paths = paths;
}
public TreePathTransferable(MyTree3 tree) {
this(tree.getSelectionPathsDnd());
}
@Override
public Object getTransferData(DataFlavor flavor){
for (DataFlavor df : dflv) {
if (df.equals(flavor)){
return paths;
}
}
return null;
}
@Override
public DataFlavor[] getTransferDataFlavors(){
return dflv;
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor){
DataFlavor[] flv = getTransferDataFlavors();
for(int i = 0 ; i < flv.length ; i++){
if(flv[i].equals(flavor)){
return true;
}
}
return false;
}
}

0 件のコメント: