ホーム < ゲームつくろー! < IKD備忘録

wxWidgets
ファイルドラッグアンドドロップをサポート

(2010. 7. 12)
(2010. 7. 15追記)


 wxWidgetsでファイルのドラッグアンドドロップをサポートするには次のようにします。



@ wxFileDropTargetクラスを継承

 wxWidgetsではドラッグされる物を取り扱うクラスが独立して存在しています。ドラッグされたファイル名を管理してくれるのはwxFileDropTargetクラスです。ただ、このクラスは抽象クラスのため、プログラマがこのクラスを継承する必要があります。

 仮想メソッドはただ一つOnDropFilesメソッドです。典型的な宣言は以下のようになります:

class MyFileDropTarget : public wxFileDropTarget {
    MyFrame *frame;
public:
    MyFileDropTarget(MyFrame *frame) : frame(frame) {}
    virtual ~MyFileDropTarget() {}
    bool OnDropFiles(wxCoord x, wxCoord y ,const wxArrayString &filenames);
};

 OnDropFilesメソッドは例えば次のようになります:

bool MainFrame::MyFileDropTarget::OnDropFiles(wxCoord x, wxCoord y ,const wxArrayString &filenames) {
    // ドロップされたファイルを渡す
    const wxChar *c = filenames[0].GetData();
    frame->OnDropFile(c);
    return true;
}

 コンストラクタで渡したフレーム(ウィンドウ)にOnDropFileメソッドを設けておきます。MyFileDropTarget::OnDropFilesメソッドはコールバックになっていて、ファイルがドロップされると引数にドロップされた位置とファイル名配列が渡されます。上の例では最初のファイル名(フルパス)を配列から取得してそれをMyFrameに渡しています。



A wxWindow::SetDropTargetメソッドにwxFileDropTargetを渡す

 @で作ったクラスのオブジェクトをwxWindow::SetDropTargetメソッドに渡すと、そのウィンドウへのドラッグアンドドロップが許可されるようになります:

//MyFrameコンストラクタ
MyFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size) :
    wxFrame(NULL, -1, title, pos, size)
{
    // ドロップターゲットにする
    fileDropTarget = new MyFileDropTarget(this);
    this->SetDropTarget(fileDropTarget);
}

こんな感じでコンストラクタで渡しておけば良いでしょう。ドラッグアンドドロップはこれだけで実現できます(^-^)。



(2010. 7. 15追記)
B クラスを用いないドラッグアンドドロップサポート

 先の実装に対して神無月様より「クラスを使わなくてもできますよ」というご報告を頂きました。ありがとうございます!ご教授いただいた実装方法を追記致します。

 ファイルドロップ時に呼ばれるイベントとしてEVT_DROP_FILESというのがあり、これを受けるハンドラメソッドを次のように定義します:

BEGIN_EVENT_TABLE(MainFrame, wxFrame)
    EVT_DROP_FILES(MainFrame::onDropFile)
END_EVENT_TABLE()

これは通常のイベントハンドラ定義と同じ書き方です。次に、wxWindow::DragAcceptFilesメソッド(wxFrameの親です)でファイルのドラッグアンドドロップを許可します:

this->DragAcceptFiles(true);

これでそのウィンドウにファイルをドラッグアンドドロップできるようになります。結果はハンドラに渡されます:

void MainFrame::onDropFile(wxDropFilesEvent &event) {
}

引数のeventにはファイル名やドロップ位置等が入ってきます。

 クラスによる方法とハンドラによる方法とで違うのは、機能を分離できるかどうかかなと思います。ハンドラはフレームクラスの一部なので、機能を変えるには工夫が必要です。一方クラスによる方法ではオブジェクトを入れ替えることで機能を変える事ができます。ただその分クラスを新設するなどコード量は増えます。仕様に合わせて使い分けるのが良さそうですね。