ページ

2008年2月22日金曜日

スクラップブックその10 - 画像をファインダへドラッグ&ドロップする

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

スクラップブック上の画像をファインダへドラッグ&ドロップできるようにする。こんな感じ↓



をつかみファインダ上でドロップするとその画像のファイルがコピーされる。

ソースコード:sp4-10.zip


他アプリへのドラッグ&ドロップは NSView#dragImage:at:offset:event:pasteboard:source:slideBack: を使えば容易に実現できる。方法は以前の投稿で紹介した(「Finderへドロップ」)。

実装はまず最初にアイコンが押された(ドラッグに入った)かどうかの判定から入る。

WorkView.m

- (void)mouseDown:(NSEvent *)theEvent
{
   :
   :
if ([_icon inRectAtPoint:start_point]) {
   // 他アプリへのドラッグ&ドロップ処理
  }
   :
  // 通常の画像のドラッグ処理
}


アイコンが押されたらペースとボードとドラッグ用の画像を用意して dragImage:... を呼出す。
まず新規に NSImage を作成し、これにコピー元となる画像を半透明で描画する。

  NSImage *dragged_image = [[[NSImage alloc] initWithSize:[item image].size] autorelease];
[dragged_image lockFocus];
[[item image] compositeToPoint:NSZeroPoint
operation:NSCompositeSourceOver
fraction:0.8f];
[dragged_image unlockFocus];


続いて NSPasteboardの準備。NSFilesPromisePboardType を登録しておく。登録しておくとドロップ時に namesOfPromisedFilesDroppedAtDestination: がコールバックされる。

  NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
[pboard declareTypes:[NSArray arrayWithObject:NSFilesPromisePboardType] owner:self];
[pboard setPropertyList:[NSArray arrayWithObject:[item.filename pathExtension]]
forType:NSFilesPromisePboardType];


最後に drawImage:... を呼出す。これでドラッグ操作が開始される。

  [self dragImage:dragged_image
at:NSMakePoint([item.x floatValue], [item.y floatValue]+[item.height floatValue])
offset:NSZeroSize
event:theEvent
pasteboard:pboard
source:self
slideBack:YES];



ペーストボードには NSFilesPromisePboardType が登録されているので、ファインダへドロップした時に namesOfPromisedFilesDroppedAtDestination: が呼出される。ここで画像のコピー処理を書いてやれば良い。

- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
{
id dst_path = [[dropDestination relativePath] stringByAppendingPathComponent:_dragged_filename];
id src_path = [[_controller pathToSave] stringByAppendingPathComponent:_dragged_filename];

[[NSFileManager defaultManager] copyPath:src_path
toPath:dst_path
handler:nil];
return [NSArray arrayWithObject:_dragged_filename];
}


ドラッグしている画像のファイル名はこのメソッドだけでは得られないので、ドラック開始前にあらかじめメンバ変数 _dragged_filename へ取っておく。



なおこのままだと自分自身へもドロップができてしまう。この場合、実体の画像ファイルが1つなのに画面上は2つの同じ画像が表示される状態になる。これはこれで使い道もあるのだが、現状では削除した場合に矛盾が生じてしまうので禁止することにする。draggingEntered: と performDragOperation: に自分自身へのドロップかどうかのチェックを入れておく。

- (NSDragOperation)draggingEntered:(id )sender
{
if (self == [sender draggingSource]) {
return NSDragOperationMove;
}
return NSDragOperationCopy;
}

- (BOOL)performDragOperation:(id )sender {

if (self == [sender draggingSource]) {
return NO;
}
 :


NSDraggingInfo#draggingSource にはドラッグ元のオブジェクト(id)が入る(ドラッグ元が他のアプリケーションの場合は nilとなっていた)。



ここまでで簡単なスクラップブックの機能が実装できた。振り返って見ると改めて cocoaの強力さを感じた。ところどころ良くできているのに感心し、それとともに流れが分かってくると結構楽しかった。
なお、検証目的でとにかく早く作っていったので、メモリ確保の後始末(release)や描画の効率化、エラー処理の実装などは手を抜いてきた。きちんと作るとこの辺りで結局コード量は膨らむと思う。


- - - -
スクラップブックはこの後も検証目的ですこしずつ改良を加えていくつもりだが、そろそろちゃんとしたアプリの制作にとりかかろうと思う。最終目的はスクラップブックに近いものだがその前に小さなツールをいくつか作ってみたい。今後はそのあたりの開発過程もこのブログで紹介していきたいと思う。