● Carbon視点でCocoa探求(2008/12/06)

  このニュースは、MOSAの会員にのみ配布されているデベロッパー向けの
  デジタルマガジンMOSADeNのに掲載された記事です。ほぼ一ヶ月遅れで
  ここに掲載されて行きます

 〜 起動時とドラッグ&ドロップでの登録 〜


今回は、ImageBrowserへの画像登録の「別の道筋」である「アプリ・アイコンへのドロップで新規ウィンドウを開き画像を登録」と「ドラッグ&ドロップでウィンドウに画像を追加登録」について解説いたします。

まず最初は「アプリ・アイコンへのドロップで新規ウィンドウを開き画像を登録」についてです。この処理の実行には、NSApplicationのデリゲート(NSApplicationDelegate)であるapplication:openFiles:メソッドを実装します。ユーザが、画像ファイルやフォルダをアプリ(もしくはドッグ)アイコン上にドラッグ&ドロップすると、このメソッドが呼ばれるわけです。NSApplicationにどのようなデリゲート・メソッドが存在しているかは、NSApplication.hを参照してみてください。

- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
{
  MyDocument    *mydoc=nil;
  NSMutableArray  *files=nil;
  NSError      *err=nil;
  NSString      *path;
  int         i,ct;
  BOOL       dir;

  ct=[filenames count]; // 情報にいくつのパス名が含まれているのか?
  for( i=0;i<ct;i++ )   // パス名の個数分ループする
  {
    path=[filenames objectAtIndex:i]; // 対象パス名を得る
    [[NSFileManager defaultManager] fileExistsAtPath:path
                        isDirectory:&dir];
    if( dir || [MyDocument isImageFile:path] ) // フォルダか画像ファイルの場合
    {
      if( ! mydoc ) // まだ新規ドキュメントが存在しない
      {
        files=[[NSMutableArray alloc] init]; // パス名保存用の配列作成
        [[NSDocumentController sharedDocumentController]
                             newDocument:self];
                         // ドキュメント作成
        mydoc=[[NSDocumentController sharedDocumentController]
                            currentDocument];
                         // カレントにセット
      }
      if( mydoc )
        [files addObject:path]; // 対象パス名を配列に保存する
    }
    else  // 「しんぶんし 3.0」のドキュメントかどうかトライしてみる
      [[NSDocumentController sharedDocumentController]
          openDocumentWithContentsOfURL:
          [NSURL fileURLWithPath:path]display:YES error:&err];
  }
  if( mydoc ) // ドキュメントが新規に作成された場合
  {
    [mydoc addImagesThread:files]; // ImageBrowserに画像を追加する
    [files release];         // 配列のリリース
  }
}

この場合、アイコンにドロップされるファイルは画像ファイルやそれを含んだフォルダだけではなく、「しんぶんし 3.0」自身のドキュメントファイルである可能性もありますので注意してください。その場合には、通常のドキュメント・オープン処理となります。

続いて「ドラッグ&ドロップでウィンドウに画像を追加登録」の方です。Cocoaアプリケーションで「ドラッグ&ドロップ」を実現するには、ドロップ先のオブジェクト(例えばNSViewなど)に、以下の6つのメソッド( NSDraggingDestinationプロトコル)を実装します。ちなみに、Mac OS X 10.5以降では1つ増えて7つになったようです。

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
// ドラッグがエリアに入った場合
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender;
// エリア内にいる場合
- (void)draggingExited:(id <NSDraggingInfo>)sender;
// エリアから出た場合
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender;
// 受け入れ処理がOKかどうか
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
// 受け入れ処理を実行
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender;
// 処理が終了した
- (void)draggingEnded:(id <NSDraggingInfo>)sender;
// Mac OS X 10.5以降のみ

Carbonでドラッグ&ドロップを経験されている方は、どのメソッドがどんな処理を担当するのか、そのメソッド名からほぼ理解できると思います(笑)。ImageBrowser上へのドラッグ&ドロップに関しては、このうちの3つだけを、デリゲートとして用意したオブジェクトに実装すればOKです。デリゲートは、MyDocumentクラスのawakeFromNibメソッドにおいてsetDraggingDestinationDelegate:メソッドを使いセットします。今回のセット先は自分自身(self)です。

- (void)awakeFromNib
{
  [imageBrowser setDraggingDestinationDelegate:self]; // デリゲートをセット
  [imageBrowser setAllowsMultipleSelection:NO];
  [imageBrowser setAllowsEmptySelection:NO];
  [imageBrowser setZoomValue:0.5];
}

以下が、実装しなければいけない3つのメソッドですが、そのうち2つの実装は簡単でして、単純にNSDragOperationCopy(定数)を返すだけです。

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
return NSDragOperationCopy;
}

- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
{
return NSDragOperationCopy;
}

最後のperformDragOperation:メソッドに実際の画像登録処理を記述します。このメソッドでNOを返すと、ドロップしたアイコンが元の場所へ戻っていくアニメーションが実行されるわけです。

- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
  BOOL      ret=NO;
  NSArray     *paths;
  NSData      *data;
  NSString     *err;
  NSPasteboard  *pb;

  pb=[sender draggingPasteboard]; // 渡されたペーストボード・オブジェクト
  if ([[pb types] containsObject:NSFilenamesPboardType] ) // ファイル名ある?
  {
    if( data=[pb dataForType:NSFilenamesPboardType] ) // そのデータを抽出
    {
      paths=[NSPropertyListSerialization propertyListFromData:data
          mutabilityOption:kCFPropertyListImmutable format:nil
                         errorDescription:&err];
                        // 対象となるパス名を得る
      [self addImagesPaths:paths]; // パス名を使い画像の登録を行う
      ret=YES; // ドラッグ&ドロップ処理は正しく実行された
    }
  }
  return ret;
}

次回は「ImageBrowserで選択した画像をいかに対称表示させるのか?」についてCore Graphics APIに関する解説をしたいと思います。が、ここからの話は少しCocoaから離れてしまいいますので、iPhone SDKのNDAも解除されたことですし、ひょっとすると連載そのもを方向転換するかもしれません(笑)

copyright 2008 Ottimo, Inc. All rights reserved
無断転載・引用禁止