ドラッグ&ドロップのスクリプティング
ドラッグ&ドロップがちとやっかいだったので、練習と実益を兼ねてサンプルを作ってみました。スタックウィンドウ内の指定フィールドにフォルダをドロップすると、そのフォルダ内の「空のフォルダ」を削除します。スクリプトは全てフィールドスクリプトにあります。スクリプトはここに載せてありますが、是非実際のスタックの動作を見ながら解説を読んで下さい。
http://udimac.web.fc2.com/RunRev/deleteEmptyFolders.rev.hqx
−−−−
まず「何かがドラッグされてきた」時に dragEnter ハンドラが実行されます。このハンドラでやるべきは、ドラッグされたものを受け付けるか否かを決め、そのレスポンスをユーザーに返すことです。ドラッグされたものを受け取るなら set the acceptdrop to true を実行して、システムに「このオブジェクトは受付可能である」旨を知らせます。 OSX ではこの時カーソルの形が '+' になり、ドロップが可能であることを示します。
もしそのオブジェクトを受け取らないなら、the acceptdrop をセットせずにそのまま終了します。これはシステムに「このオブジェクトは受け取らない」と返事をしたことになります。OS によって異なるかも知れませんが、MacOS ではこの状態でユーザーがマウスのボタンを離すと、「ドラッグしたオブジェクトがドラッグ元へ帰っていく」アニメーションが表示されます。これによってユーザーに「そのタイプのオブジェクトは受け取らない」という意思表示が出来ます。
このサンプルではフォルダだけを受け取りたいので、the keys of the dragData に "files" が含まれていない時は exit しています。
ここで the dragData プロパティはドラッグされてきたオブジェクトの配列です。その配列のキーである the keys を調べることによって、ドラッグしてきたオブジェクトの種類を知ることが出来ます。( テキストなら "text"、グラフィックデータなら"image"、ファイルなら中身に関わらず "files" です。ディスクやフォルダも "files" になります)
さらに the dragData の中身をひとつづつ(複数の場合もあるので)調べて、そこにフォルダが無ければ exit します。このチェック処理は checkContainsFolder ハンドラで行っています。
以上のチェックを通ったら(つまりフォルダがドラッグされてきているのなら)、ドラッグを受け取るオブジェクトのフォーカスをハイライトさせます。
focusOn ハンドラでは set the traversalOn of me to true を実行して、オブジェクトの「フォーカス可能」プロパティをオンにしています。オブジェクトにもよりますが、ドロップ専用のフィールドは普段はフォーカス不可にしておいた方が都合がよいので、この処理が必要になります。(ドロップ処理が済んだら再度 false に戻しておきます) そして focus on me で実際にフォーカスリングをハイライトします。
これによって、ユーザーがオブジェクトの上にフォルダをドラッグすると、そのオブジェクトがハイライトするようになります。これでユーザーは「ドロップを受け付けてくれる」と判断出来ます。ドラッグの途中でドラッグ先を変えるなど、ユーザーがドラッグ処理を中止した場合は、dragLeave メッセージが送られて来ます。ハイライトさせたオブジェクトはここでハイライトを解除して、そのオブジェクトに対するドラッグ処理が中断されたことを明確にします。つまりこれら一連の「ドラッグ」処理は、dragEnter ハンドラと dragLeave ハンドラを基点に行っています。
--2018.7.1 修正 15年も経って何を言ってるのかと言われそうだけど、間違いを見つけたので直しておきます orz
ファインダーアイテム(ファイル、フォルダ、ディスクなど)がドラッグされてきた時、the dragData には files が入っているものと思っていましたが、これは MacOS 9 の話でした。 MacOSX と Linux では files と text の両方が入っています。つまりこの下のスクリプトで「 files でなかったら」 if ( the keys of the dragData ) is not "files" then としてあるところは、「 files が入っていなかったら」 if NOT( ( the keys of the dragData ) contains "files" ) then としないと、何もドロップ出来なくなります。当時は OSX のテスト環境が無かったためのミスでした。申し訳ない。サンプルスタックは修正前のものをそのまま置いておきます。修正前と後で動作がどう変わるか見てみて下さい。
global gSaveDefaultFolder on dragEnter -- 何かがドラッグされてきた -- 2018.7 if ( the keys of the dragData ) is not "files" then if NOT ( ( the keys of the dragData ) contains "files" ) then exit dragEnter -- ファインダーアイテムがない「そんなデータは知らない」 end if if checkContainsFolder( the dragData ) is false then exit dragEnter -- フォルダが含まれていない「そんなデータは知らない」 end if set the acceptdrop to true -- ドロップ可 focusOn -- フォーカスを得る「ドロップ出来るよ」 end dragEnter on dragLeave -- ドラッグ中にマウスがオブジェクトから外れた fucousOff -- フォーカスを解除「ドラッグやめたよ」 end dragLeave function checkContainsFolder dragList -- ドラッグリストにフォルダがあるか? repeat for each line aFolder in dragList -- オブジェクトをひとつづつ調べる if ( there is a folder aFolder ) then -- それはフォルダか? return true -- ドロップリストにはフォルダが含まれている end if end repeat return false -- ドロップリストにはフォルダが含まれていない end checkContainsFolder
on dragDrop put the dragData into dragList put the defaultFolder into saveDefaultFolder -- 現在のデフォルトフォルダを保存 repeat for each line aFolder in dragList -- ドロップしたオブジェクトの数だけループ if ( there is not a folder aFolder ) then next repeat -- ドロップしたアイテムがフォルダでなかったらスキップ end if set the defaultFolder to aFolder put line 2 to -1 of the folders into folderList -- ドロップしたフォルダ内のアイテム名リスト repeat for each line tgFolder in folderList set the cursor to busy put ( aFolder & "/" & tgFolder ) into tgFolderPath -- 親フォルダのパスと合成 if ( there is not a folder tgFolderPath ) then next repeat -- アイテムがフォルダでなかったらスキップ end if set the defaultFolder to tgFolderPath -- ドロップしたフォルダ内のフォルダの中のアイテム名リスト if ( the number of lines of the folders ) = 1 then -- リストが空( ".." のみ)なら set the defaultFolder to saveDefaultFolder -- デフォルトフォルダを戻してから delete folder tgFolderPath -- そのフォルダを削除 end if end repeat end repeat fucousOff set the defaultFolder to gSaveDefaultFolder end dragDrop on focusOn -- 自分をフォーカスする set the traversalOn of me to true focus on me end focusOn on fucousOff -- 自分のフォーカスを外す select empty set the traversalOn of me to false end fucousOff