AIRプログラムからクリップボードにアクセスする

今回説明するのは、AIRプログラムからクリップボードにアクセスする方法だ。

ネイティブOSが持つクリップボードにアクセスすると、他のアプリケーションと連携してコピー&ペーストを行うことができるようになる。

AIRで、クリップボードにアクセスするために用いるクラスは以下の2つだ。

  • flash.desktop.ClipboardManager - クリップボードにアクセスするためのマネージャクラス
  • flash.desktop.TransferableData - クリップボード内のデータを表すオブジェクト

後者のTransferableDataクラスは、ドラッグ&ドロップ処理でも利用した。TransferableDataは、アプリケーション外部の環境とやり取りするデータを表す抽象的なクラスだ。

詳しい説明は、前回の記事を参照していただきたい。

クリップボードにアクセスするプログラムを書くのは、あっけないほど簡単だ。

以下のように、ClipboardManager.accessClipboard()メソッドに関数オブジェクトを渡し、内部でClipboardManager.dataプロパティにアクセスするだけだ。同プロパティはTransferableData型であり、プロパティに値をセットすればクリップボードにデータが入り、同プロパティを参照すればクリップボードからデータを読み出すことができる。

// クリップボードへのアクセスはaccessClipboard()の
// 引数に渡した関数内で行う。
ClipboardManager.accessClipboard(function():void {
    // クリップボードからのデータ読み出し
    var clipboardData:TransferableData = ClipboardManager.data;
});

var data:TransferableData = ...
ClipboardManager.accessClipboard(function():void {
    // クリップボードへのデータセット
    ClipboardManager.data = data;
});

ClipboardManager.accessClipboard()メソッドに引き渡した関数のブロック内ではなく、いきなりClipboardManager.dataにアクセスすると、以下のようなエラーが発生する。

ClipboardManager.dataプロパティに不正アクセスした際のエラー

ちなみに、AIRでは、クリップボードに格納できるデータは一つだけだ。クリップボードに後からデータを追加すると、前のデータは破棄されてしまう。

では、以上を踏まえた上で、今回のサンプルプログラムを解説していこう。

クリップボード処理のサンプルプログラム

今回のサンプルは、ドラッグ&ドロップ編で利用したサンプルに多少似せてある。

ネイティブOS上でファイルをクリップボードに入れ(右クリック→コピーなどで)、今回のアプリケーション上で「C」というキーを押せば、そのファイルのアイコンがアプリケーション内に張り付く。

「C」キーで、アプリケーション内にファイルをコピーできる

アプリケーション上に貼り付けたファイルをコピーすることもできる。アプリケーション上でファイルのアイコンを選択すると、選択したアイコンが半透明になる。その状態で「V」キーを押すとクリップボードにファイルのデータが格納され、Windowsのエクスプローラなどでペーストすることができる。

ファイルのアイコンを選択すると、アイコンが半透明になり・・・

エクスプローラでファイルをペーストすることもできる。

以下が、サンプルアプリケーションのソースコードだ。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init()">
    <mx:Script>
        <![CDATA[
            import flash.desktop.Icon;
            import mx.core.UIComponent;
            import flash.filesystem.File;
            import flash.desktop.*;

            // (1) 選択されたファイルを保持しておく変数
            private var selectedFileImage:UIComponent;

            // ルート要素「WindowedApplication」のapplicationComplete属性に指定された初期化メソッド
            private function init():void {
                // (2) 表示領域内でキーボードが押された時の処理を追加
                this.stage.addEventListener(KeyboardEvent.KEY_DOWN, function(event:KeyboardEvent):void {
                    if (event.keyCode == Keyboard.V) {
                        onPaste();
                    } else if (event.keyCode == Keyboard.C) {
                        onCopy();
                    }
                });
            }
            // ファイルのペースト処理
            private function onPaste():void {
                // (3) クリップボードへのアクセス
                ClipboardManager.accessClipboard(function():void {
                    // クリップボード内のデータを取得し、ファイル形式のデータじゃなかった場合は何もしない
                    var data:TransferableData = ClipboardManager.data;
                    if (!data.hasFormat(TransferableFormats.FILE_LIST_FORMAT)) {
                        return;
                    }
                    // 選択されたファイルを取得
                    var files:Array = data.dataForFormat(TransferableFormats.FILE_LIST_FORMAT) as Array;
                    for each (var file:File in files) {
                        // アイコンとラベルの貼り付け
                        var icon:Icon = file.icon;
                        for each (var bitmapData:BitmapData in icon.bitmaps) {
                            // 32x32のアイコンのみ対象とする
                            if (bitmapData.height == 32) {
                                // アイコン画像をコンポーネントとしてキャンバスに追加
                                var fileImage:UIComponent = new UIComponent();
                                fileImage.addChild(new Bitmap(bitmapData));
                                // ファイルのパスをコンポーネントの名前にしておく
                                fileImage.name = file.nativePath;
                                fileImage.toolTip = file.name;
                                fileImage.width = 40;
                                fileImage.height = 40;
                                // アイコンをクリックされた際の処理
                                fileImage.addEventListener(MouseEvent.CLICK, onFileImageClick);
                                // キャンバスに追加
                                view.addChild(fileImage);
                            }
                        }
                    }
                });
            }
            // ファイルのアイコンがクリックされた際の処理
            private function onFileImageClick(event:MouseEvent):void {
                // 以前選択されていたファイルイメージの透明度を1に戻す
                var fileImage:UIComponent = event.target as UIComponent;
                if (selectedFileImage)
                    selectedFileImage.alpha = 1.0;
                fileImage.alpha = .5;
                selectedFileImage = fileImage;
            }
            // ファイルのコピー処理
            private function onCopy():void {
                if (!selectedFileImage) {
                    return;
                }
                // (4) コンポーネント名をファイルのパスとし、Fileオブジェクトを作成して
                //     クリップボードに格納
                var file:File = new File(selectedFileImage.name);
                // クリップボードに入れるデータを作成
                var transfer:TransferableData = new TransferableData();
                transfer.addData([file], TransferableFormats.FILE_LIST_FORMAT, true);
                // クリップボードにデータを追加
                ClipboardManager.accessClipboard(function():void {
                    ClipboardManager.data = transfer;
                });
            }
        ]]>
    </mx:Script>
    <mx:Label x="10" y="10" text="下のキャンバスにはファイルをコピー&amp;ペーストできます。(C=コピー V=ペースト)"/>
    <mx:Tile id="view" borderStyle="none" x="0" y="36" width="473" height="320" backgroundColor="white"/>
</mx:WindowedApplication>

ポイントを解説していこう。

(1) アプリケーション内で選択されたファイルの情報を格納するための変数selectedFileImageを宣言している。

private var selectedFileImage:UIComponent;

(2) アプリケーションが起動したら、アプリケーションの表示領域内でキーボードが押された際のイベントリスナを追加している。「V」でペースト、「C」でコピーだ。本当は、Ctrlキーとの組み合わせでコピーとペーストを行いたかったのだが、AIRのベータ版ではCtrlキーと通常キーの組み合わせを正しく扱うことができない

// (2) 表示領域内でキーボードが押された時の処理を追加
this.stage.addEventListener(KeyboardEvent.KEY_DOWN, function(event:KeyboardEvent):void {
    if (event.keyCode == Keyboard.V) {
        onPaste();
    } else if (event.keyCode == Keyboard.C) {
        onCopy();
    }
});

(3) 「V」キーを押され、ファイルのペーストを行われた際の処理を行っているのが以下の部分だ。

// (3) クリップボードへのアクセス
ClipboardManager.accessClipboard(function():void {
    // クリップボード内のデータを取得し、ファイル形式のデータじゃなかった場合は何もしない
    var data:TransferableData = ClipboardManager.data;
    if (!data.hasFormat(TransferableFormats.FILE_LIST_FORMAT)) {
        return;
    }
    // 選択されたファイルを取得
    var files:Array = data.dataForFormat(TransferableFormats.FILE_LIST_FORMAT) as Array;

前述の通り、ClipboardManager.accessClipboard()メソッドに関数を引き渡し、内部でClipboardManager.dataプロパティにアクセスしている。TransferableDataクラスのhasFormat(*フォーマット形式*)メソッドを利用して、クリップボード内のデータがファイルの形式かどうかを判定している。クリップボード内のデータ形式がファイル(複数も可)だった場合は、TransferableDataクラスのdataForFormat(*フォーマット形式*)メソッドを用いてデータを取得している。

(4) 最後のポイントは、アプリケーション上で「C」キーを押された際、クリップボードにファイルの情報を格納する処理だ。

// (4) コンポーネント名をファイルのパスとし、Fileオブジェクトを作成して
//     クリップボードに格納
var file:File = new File(selectedFileImage.name);

// クリップボードに入れるデータを作成
var transfer:TransferableData = new TransferableData();
transfer.addData([file], TransferableFormats.FILE_LIST_FORMAT, true);

// クリップボードにデータを追加
ClipboardManager.accessClipboard(function():void {
    ClipboardManager.data = transfer;
});

FileクラスのオブジェクトをTransferableData.addData()メソッドで格納し、ClipboardManager.dataプロパティにデータをセットしている。たったこれだけで、クリップボードにファイル情報を格納できる。

今回は、AIRプログラムからネイティブOSが持つクリップボードにアクセスする方法を解説した。ClipboardManagerクラスとTransferableDataクラスという、たった二つのクラスについての知識さえあればよいので、非常に簡潔にまとまったAPIだと言えよう。