クリップボード


システムクリップボード

アプリケーション間で、何らかのデータを転送する手段として一般的なのはクリップボードです
Java は基本的にネイティブなレベルの依存を拒否しますが
java.awt.datatransfer パッケージを使うことによって
システムがサポートしているクリップボードへのアクセスを可能とします

クリップボードには、OS が持つシステムレベルのクリップボードと
Java のアプリケーション内で論理的に実装するローカルクリップボードがあります
前者はアプリケーションに、後者はアプレットで実現するのに適していると考えられます

クリップボードへのアクセスは java.awt.datatransfer.Clipboard クラスを使用します

public Clipboard( String name )

ただし、システムクリップボードの場合は直接このクラスを生成しません
これは、後記するローカルクリップボードで使うコンストラクタです
システムクリップボードは Toolkit クラスの getSystemClipboard() を使います

public abstract Clipboard getSystemClipboard()

このメソッドは、ネイティブプラットフォームがサポートするクリップボードを返します
クリップボードからデータを取得するには Clipboard クラスの getContents() を用います

public synchronized Transferable getContents( Object requestor )

requestor には、データを要求するオブジェクトを渡します
インスタンスを持つクラス内(つまり非static)なら this を渡せば良いです
クリップボードに何も入っていない場合は null が、そうでなければ
java.awt.datatransfer.Transferable インターフェイスが返ります

このインターフェイス型のオブジェクトは、クリップボードの内容と
データフレーバー(データ形式)の情報を持っています
これは、クリップボードが論理的にはどのようなデータ型も転送できる仕組みだからです

次に、渡されたデータがテキスト形式かどうかを調べる必要があります
これを調べるには Transferable クラスの isDataFlavorSupported() メソッドを使います

public abstract boolean isDataFlavorSupported( DataFlavor flavor )

flavor には、データに対して要求するフレーバを指定します
戻り値は、指定された形式と同じならば true、そうでなければ false です
これで、Transferable オブジェクトが持つデータ形式を調べられます

flavor に渡すパラメータは java.awt.datatransfer.DataFlavor クラスです
このクラスは、Java のクラスなどのデータ形式を抽象化し、転送などに利用されます
ただし、Java.lang.String 型のフレーバはあらかじめ用意されています

public static DataFlavor stringFlavor

Transferable の isDataFlavorSupported() にこれを渡せば
データがテキスト形式かどうかを確認することができます
そして、Transferable の getTransferData() メソッドからデータを得られます
public abstract Object getTransferData( DataFlavor flavor )
	throws UnsupportedFlavorException, IOException
flavor は要求するフレーバです
戻り値は、フレーバで表現されたクラスにキャストすることができます
IOException は要求されたフレーバで、データが使用できない場合
UnsupportedFlavorException は、要求されたデータ フレーバがサポートされていない場合に発生します

getTransferData() メソッドに DataFlavor.stringFlavor を渡せし
クリップボードに文字列データがあれば、文字列を転送することができます
import java.awt.*;
import java.awt.datatransfer.*;

public class test extends Frame {
	String strClip;

	public static void main(String argv[]) {
		App65 frm = new App65();
		frm.setSize(400 , 400);
		frm.show();
	}
	public App65() {
		Clipboard clp = getToolkit().getSystemClipboard();
		Transferable data = clp.getContents(this);

		if (data == null || !data.isDataFlavorSupported(DataFlavor.stringFlavor))
			strClip = "転送に失敗しました";
		else try {
			strClip = (String)data.getTransferData(
				DataFlavor.stringFlavor
			);
		}
		catch(Exception e) {}
	}
	public void paint(Graphics g) {
		g.drawString(strClip , 10 , 50);
	}
}
このプログラムを実行すると、クライアント領域にクリップボードの内容が表示されます
転送が行われなかった場合は、それ専用のメッセージが表示されます
(ウィンドウリスナの実装はしていないので、強制終了してください)

次に、クリップボードに文字列を転送してみましょう
クリップボードにデータを転送するには Transferable インターフェイスを実装する必要があります
文字列の場合は java.awt.datatransfer.StringSelection クラスが
文字列のクリップボード転送をサポートしています。コンストラクタは次のとおりです

public StringSelection( String data )

data には、転送するStringオブジェクトを指定します
このクラスは、インターフェイスの実装しか持たないのでとくに重要なメソッドはありません
後は、このオブジェクトを Clipboard クラスの setContents() に渡すだけです

public synchronized void setContents( Transferable contents, ClipboardOwner owner )

contents には、クリップボードに転送するデータを指定します
StringSelection は Transferable をインプリメントしているのでそのまま渡せます
owner には java.awt.datatransfer.ClipboardOwner インターフェイスを
実装しているオブジェクト渡します

このインターフェイスは、クリップボードの所有権を通知するもので
lostOwnership() メソッドを宣言しています

public abstract void lostOwnership( Clipboard clipboard, Transferable contents )

このメソッドは、オブジェクトがクリップボードの所有権を失った時に呼び出されます
clipboard には、所有権を失った Clipboard オブジェクトが
contents には、所有権をもっていたオーナーがクリップボードに置いていたデータが格納されます

なぜこのようなインターフェイスが必要なのでしょうか?
それは、setContents() を使った時、即座にクリップボードにデータが転送されるわけではなく
場合によっては、要求があるまでデータをクリップボードに転送しないのです

これを 遅延データモード と呼び、Win32 のディレイドレンダリングにあたります
文字列は即座に転送しても問題ありませんが、巨大なイメージなどではこの方法が有効なので
クリップボード所有者は lostOwnership() が呼び出されるまで
クリップボードのデータをアクセス可能な状態にしておく必要があります
import java.awt.*;
import java.awt.datatransfer.*;

public class test extends Component implements ClipboardOwner {
	public static void main(String argv[]) {
		App65 obj = new App65();
	}
	public App65() {
		Clipboard clp = getToolkit().getSystemClipboard();
		StringSelection strClip = new StringSelection("Kitty on your lap");
		clp.setContents(strClip , this);
	}
	public void lostOwnership(Clipboard clipboard , Transferable contents) {
		System.out.println("クリップボード所有権を失いました");
		System.exit(0);
	}
}
このプログラムを起動すると、クリップボードに "Kitty on your lap" という文字が転送されます
他のタスクやオブジェクトが介入するまで、クリップボードの所有者となります

他のアプリケーションなどから「コピー」や「切り取り」を使ってクリップボードに転送すると
このアプリケーションは所有権を失い lostOwnership() メソッドが実行され
文字列をコンソールに表示してプログラムを終了します

ちなみに、StringSelection クラスは ClipboardOwner をインプリメントしているので
とくに処理の必要がなければ clp.setContents(strClip , strClip); でも構いません

データの転送先は、プラットフォームがサポートするクリップボードです
これを利用すれば、Javaアプリケーションと他のアプリケーションとでデータを交換できます


ローカルクリップボード

システムクリップボードへのアクセスは、アプリケーションにとって重要ですが
アプレットなど、ネットワークで動作するサンドボックス内では逆にこれが
セキュリティによって例外となり、ソフトの動作に問題をおこしてしまいます

そこで、サンドボックス内のプログラムでは
システムではなく、Javaが提供するローカルクリップボードを使います
ローカルクリップボードは、Clipboard() コンストラクタからクリップボードを生成します
第一パラメータに文字列を指定しますが、これはクリップボードの名前です

ローカルクリップボードは、同時に複数のクリップボードを所有できます
それらを識別する名前を用い、それらは Clipboard クラスの getName() で取得できます

public String getName()

このメソッドは、クリップボードの名前を返します
Clipboard インスタンスを直接生成するということ以外、操作はシステムとまったく同じです
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;

/*	<applet code="App65.class" width="300"height="300">
 	</applet>
*/

public class App65 extends Applet implements ActionListener {
	Clipboard clp;
	TextArea txtCopy , txtPaste;
	Button butCopy , butPaste;

	public void init() {
		clp = new Clipboard("Kitty on your lap");
		setLayout(new GridLayout(2 , 2));

		txtCopy = (TextArea)add(new TextArea());
		butCopy = (Button)add(new Button("コピー"));
		butCopy.addActionListener(this);

		txtPaste = (TextArea)add(new TextArea());
		butPaste = (Button)add(new Button("ペースト"));
		butPaste.addActionListener(this);
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getActionCommand() == "コピー") {
			StringSelection strClip = new StringSelection(txtCopy.getText());
			clp.setContents(strClip , strClip);
		}
		else try {
			Transferable data = clp.getContents(this);
			txtPaste.setText(
				(String)data.getTransferData(DataFlavor.stringFlavor)
			);
		}
		catch(Exception err) {}
	}
}
アプレット

コピーボタンを押すと、上のテキストエリアの文字列をクリップボードに転送し
ペーストボタンを押すと、したのテキストエリアにクリップボードの文字列を表示します

このように、ローカルクリップボードであればアプレットでも実現できます
ただし、システムは参照していないので他のアプリケーションとデータの交換はできません



前のページへ戻る次のページへ