2008年1月3日木曜日

Java6 JTree クリップボードにコピー、切り取り、貼り付けテスト + ファイルドロップ、コピペ


package JTreeClipBoardTest2;
import java.awt.BorderLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.InputEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

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 + File Drop Copy Paste

* Java6 JTree クリップボードにコピー、切り取り、貼り付けテスト + ファイルドロップ、コピペ

*

* サンプルなので1つのファイルに必要なクラスを全部詰め込んであります。

*

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

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

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

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

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

*

* エクスプローラからのファイルのコピペ、DnDに対応します。

* 文字列のコピペ、DnDも対応してみました。

*

* 自分自身にドロップ禁止、子ノードにドロップ禁止してみた。

* canImportの中で判定するとうまくいかなかったので、ドロップ後に判定するようにしてみた。(canImport2)

* exportAsDragで、JTreeをクラス変数に設定して、exportDoneでクラス変数をnullに設定。

* importDataのJComponentと、クラス変数が同じ場合、ツリーパスの判定を行う。

*/
public class JTreeClipBoardTest2 {

/**
* @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(Object obj) {
System.out.println("setSelectionPathsDnd");
System.out.println("class = " + obj.getClass());
if (obj == null) {
return false;
}
if (obj instanceof TreePath[]) {
TreePath[] paths = (TreePath[])obj;
return setSelectionPathsDnd(paths);
}
if (obj instanceof List) {
List files = (List)obj;
return setSelectionPathsDnd(files);
}
if (obj instanceof String) {
String files = obj.toString();
return setSelectionPathsDnd(files);
}
return false;
}
/**
* ファイルリスト用インポートメソッド
*/
public boolean setSelectionPathsDnd(List files) {
if (files == null ) {
return false;
}
List filess = (List)files;
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 (File file : filess) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(file.getName());
putNode.add(node);
((DefaultTreeModel)getModel()).reload(putNode);
}
return true;
}
/**
* ツリーパス用インポートメソッド
*/
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(putNode);
}
return true;
}
/**
* 文字列用インポートメソッド
*/
public boolean setSelectionPathsDnd(String str) {
if (str == 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();
String[] strAry = str.split("\n");
for (String path : strAry) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(path);
putNode.add(node);
((DefaultTreeModel)getModel()).reload(putNode);
}
return true;
}
}
/**
* ツリーパストランスファハンドラ
*/
class TreePathTransferHandler extends TransferHandler {
JComponent comp;
/**
* コンストラクタ プロパティ設定なし
*/
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 null;
}
@Override
public void exportAsDrag(JComponent comp, InputEvent e, int action) {
this.comp = comp;
super.exportAsDrag(comp, e, action);
}
/**
* アクションを返します。
*/
@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) {
// 受け入れ可能かどうかを返します。
// ツリーパスフレーバとファイルフレーバ以外のフレーバを受け入れないようにします。
Transferable f = createTransferable(comp);
boolean flg = false;
for (DataFlavor df : transferFlavors) {
if (f.isDataFlavorSupported(df)) {
flg = true;
break;
}
}
return flg;
}

/**
* 出力側のドロップ完了処理。
* actionがMOVEの場合、ツリーを削除します。
* ※importDataがfalseを返した場合、actionはNONEになります。
*/
@Override
protected void exportDone(JComponent source, Transferable data,
int action) {
// 出力完了時にcompとnullに設定します。
comp = null;
if (!(source instanceof JTree)) {
return;
}
MyTree3 tree = (MyTree3)source;
if (action == MOVE) {
try {
for (DataFlavor df : data.getTransferDataFlavors()) {
Object obj = data.getTransferData(df);
// エクスポートの場合、処理するのはTreePathの場合だけでよい。
if (obj instanceof TreePath[]) {
TreePath[] patha = (TreePath[])obj;
TreePath path = patha[0];
Object oo = path.getLastPathComponent();
DefaultMutableTreeNode node = (DefaultMutableTreeNode)oo;
node.removeFromParent();
((DefaultTreeModel)tree.getModel()).reload(node);
}
if (obj instanceof List) {
super.exportDone(source, data, action);
}
}
} 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;
}
if (!canImport2(comp, t)) {
return false;
}
MyTree3 tree = (MyTree3)comp;
DataFlavor[] dfa = t.getTransferDataFlavors();
for (DataFlavor df : dfa) {
try {
if (tree.setSelectionPathsDnd(t.getTransferData(df))) {
return true;
}
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}

}
return false;
}
private boolean canImport2(JComponent comp, Transferable t) {
boolean flg = true;
if (comp == this.comp) {
try {
// 同じJTreeにドロップする場合。
MyTree3 tree = (MyTree3)comp;
if (tree.getDropLocation() != null) {
TreePath dropPath = tree.getDropLocation().getPath();
TreePath[] paths = (TreePath[])t.getTransferData(TreePathDataFravor.treePathFlavor);
for (TreePath dragPath : paths) {
if (dragPath.getPathCount() > dropPath.getPathCount()) {
continue;
}
if (dropPath.equals(dragPath)) {
flg = false;
break;
}
Object[] dragPathObject = dragPath.getPath();
Object[] dropPathObject = dropPath.getPath();
boolean sameFlg = true;
for (int i = 0; i < dragPathObject.length; i++) {
if (!dragPathObject[i].toString().equals(dropPathObject[i].toString())) {
sameFlg = false;
break;
}
}
if (sameFlg == true) {
flg = false;
break;
}
}
} else {
flg = false;
}
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
System.out.println(flg);
return flg;
}
}
/**
* ツリーパスデータフレーバクラス。
* 作ったはいいけど、ツリーパスフレーバーの定数を保管しているだけである。
*/
class TreePathDataFravor extends DataFlavor {
public static final DataFlavor treePathFlavor = new DataFlavor(TreePath[].class, javaJVMLocalObjectMimeType);

}
/**
* ツリーパス用トランスファエイブル
* ツリーパスの配列と、データフレーバーの配列を持つ。
*/
class TreePathTransferable implements Transferable
{
TreePath[] paths;
// ツリーパスフレーバとファイルリストフレーバ
static final DataFlavor[] dflv = {
TreePathDataFravor.treePathFlavor, // ツリーパスフレーバ
TreePathDataFravor.javaFileListFlavor, // ファイルリストフレーバ
TreePathDataFravor.stringFlavor // 文字列フレーバ
};

public TreePathTransferable(TreePath[] paths){
this.paths = paths;
}
public TreePathTransferable(MyTree3 tree) {
this(tree.getSelectionPathsDnd());
}
/**
* オブジェクト取得メソッド。

* フレーバに対応したオブジェクトを返します。

*/
@Override
public Object getTransferData(DataFlavor flavor){
if (TreePathDataFravor.treePathFlavor.equals(flavor)) {
// ツリーパスフレーバの場合
return paths;
} else if (TreePathDataFravor.javaFileListFlavor.equals(flavor)) {
// ファイルリストフレーバの場合
// ※偶然ファイルが存在しない限り、何も転送できない筈です。
List files = new ArrayList();
for (TreePath path : paths) {
files.add(new File(path.getLastPathComponent().toString()));
}
return files;
} else if (TreePathDataFravor.stringFlavor.equals(flavor)) {
// 文字列フレーバの場合
StringBuffer sb = new StringBuffer();
for (TreePath path : paths) {
sb.append(path.getLastPathComponent().toString() + "\n");
}
return sb.toString();
}
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 件のコメント: