Edama2さんからの投稿Scriptです。アラートダイアログ中に各種GUI部品を詰め込む「箱庭インタフェース」シリーズ。テーブルビューを表示して、Finderからのアプリケーションのドラッグ&ドロップを受け付けるAppleScriptです。
–> Download Editable & Executable Script Bundle Document
カスタムクラスの作り方がわかったので、懲りずにTable Viewネタです。 Thanks Shane Stanley! アプリケーションフォルダ直下のアプリを、NSValueTransformerを使ってコラムにローカライズ名とアイコンを表示するのと、finderからのドラッグ&ドロップでアイテムの追加です。 追加時に重複チェックをしているので試す時はユーティリティフォルダか別の場所にあるアプリで試してください。
本Scriptは自分も作りたいと思いつつも調査が進んでいなかったものですが、TableViewでFinderからのドラッグ&ドロップを受け付けます。
これを鍛えて再利用しやすい部品に育てることで、ドロップレットの代替品にできると思っています。macOS 10.12以降、Finder上のファイルをAppleScriptドロップレットにドラッグ&ドロップで処理させても、OS側がダウンロードファイルにつけた拡張属性(Xattr)「com.apple.quarantine」がついているとドロップレット側で処理できなかったりします(回避方法はありますけれども)。
このドロップレットという仕組みを使わずに、同様にドラッグ&ドロップによる処理を手軽にできる代替インタフェースとしてこのようなものを考えていた次第です。自分で作らなくてもEdama2さんが作ってくださったので助かりました、、、、
AppleScript名:アラートダイアログ上にTable Viewを表示 v9_アイコンを表示、Finderからのドラッグ&ドロップ |
use AppleScript version "2.4" — Yosemite (10.10) or later use framework "Foundation" use framework "AppleScriptObjC" use scripting additions on run my main() end run on main() # Script Bundle内のResourcesフォルダを求める set resourcePath to POSIX path of (path to me) & "Contents/Resources/" set theBundle to current application’s NSBundle’s bundleWithPath:resourcePath theBundle’s loadAppleScriptObjectiveCScripts() # set aFolder to path to applications folder set aPath to aFolder’s POSIX path set aURL to current application’s NSURL’s fileURLWithPath:aPath # 指定フォルダの直下のファイルを取得 set filePaths to current application’s NSFileManager’s defaultManager’s ¬ contentsOfDirectoryAtURL:aURL ¬ includingPropertiesForKeys:{current application’s NSURLNameKey} ¬ options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) ¬ |error|:(missing value) set filePaths to filePaths as list set thisFileType to "com.apple.application-bundle" set fileURLs to {} repeat with anItem in filePaths set aPath to anItem’s contents’s POSIX path set aURL to (current application’s NSURL’s fileURLWithPath:aPath) set {aResult, aUTI, aError} to (aURL’s getResourceValue:(reference) ¬ forKey:(current application’s NSURLTypeIdentifierKey) ¬ |error|:(reference)) if (aUTI as text) is thisFileType then set fileURLs’s end to aURL end if end repeat set optionRec to {fileURLs:fileURLs, fileType:thisFileType} set aMainMes to "アプリケーションの選択" set aSubMes to "適切なものを以下からえらんでください" set dateObj to my chooseItemByTableView(aMainMes, aSubMes, optionRec) end main # アラートダイアログでtableviewを表示 on chooseItemByTableView(aMainMes, aSubMes, optionRec) ### set up view set {theView, makeObj} to my makeContentView(optionRec) set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, setView:theView, myOption:makeObj} my performSelectorOnMainThread:"raizeAlert:" withObject:paramObj waitUntilDone:true if (my _retrieve_data) is missing value then error number -128 return (my _retrieve_data) end chooseItemByTableView ## retrieve date on raizeAlert:paramObj set mesText to paramObj’s myMessage set infoText to paramObj’s mySubMessage set theView to paramObj’s setView set aTableObj to paramObj’s myOption global _retrieve_data set _retrieve_data to missing value ### set up alert tell current application’s NSAlert’s new() setMessageText_(mesText) setInformativeText_(infoText) addButtonWithTitle_("OK") addButtonWithTitle_("Cancel") setAccessoryView_(theView) tell |window|() setInitialFirstResponder_(theView) end tell #### show alert in modal loop if runModal() is (current application’s NSAlertSecondButtonReturn) then return end tell ### retrieve date set aIndexSet to aTableObj’s selectedRowIndexes() set chooseItems to ((aTableObj’s dataSource())’s arrangedObjects()’s objectsAtIndexes:aIndexSet) as list set _retrieve_data to {} repeat with anItem in chooseItems set _retrieve_data’s end to anItem’s fileURL end repeat end raizeAlert: ## ContentView を作成 on makeContentView(paramObj) ## 準備 set fileURLs to (paramObj’s fileURLs) as list set thisFileType to (paramObj’s fileType) as text set keyrec to {column1:"Name"} set keyDict to (current application’s NSDictionary’s dictionaryWithDictionary:keyrec) set theDataSource to current application’s YKZArrayController’s new() ## NSTableView tell current application’s NSTableView’s alloc() tell initWithFrame_(current application’s CGRectZero) setAllowsEmptySelection_(false) setAllowsMultipleSelection_(true) setDataSource_(theDataSource) setDelegate_(theDataSource) setDoubleAction_("doubleAction:") setGridStyleMask_(current application’s NSTableViewSolidVerticalGridLineMask) setSelectionHighlightStyle_(current application’s NSTableViewSelectionHighlightStyleRegular) setTarget_(theDataSource) setUsesAlternatingRowBackgroundColors_(true) set thisRowHeight to rowHeight() as integer set theDataSource’s _table_view to it end tell end tell # data sourceに追加 tell (theDataSource) awakeFromNib() set its _file_type to thisFileType repeat with aURL in fileURLs addObject_({fileURL:aURL}) end repeat setSelectionIndex_(0) end tell ## NSTableColumn ### Columnの並び順を指定する set viewWidth to 320 set columnsCount to keyDict’s |count|() repeat with colNum from 1 to columnsCount set keyName to "column" & colNum as text set aTitle to (keyDict’s objectForKey:keyName) tell (current application’s NSTableColumn’s alloc()’s initWithIdentifier:(colNum as text)) tell headerCell() setStringValue_(aTitle) set thisHeaderHeight to cellSize()’s height end tell ### Columnの横幅を指定 setWidth_(viewWidth) ### バインディングのオプション — ソートを無効にする set anObj to (current application’s NSNumber’s numberWithBool:false) set aKey to current application’s NSCreatesSortDescriptorBindingOption set bOptions to (current application’s NSDictionary’s dictionaryWithObject:anObj forKey:aKey) ### 表示内容をNSArrayControllerにバインディング bind_toObject_withKeyPath_options_(current application’s NSValueBinding, theDataSource, "arrangedObjects", bOptions) (theDataSource’s _table_view’s addTableColumn:it) end tell end repeat ## NSScrollView ### Viewの高さを計算 set rowCount to 12 –> 行数を固定 set viewHeight to (thisRowHeight + 2) * rowCount + thisHeaderHeight –> 2を足さないと高さが合わない set vSize to current application’s NSMakeRect(0, 0, viewWidth, viewHeight) ### Viewを作成 tell current application’s NSScrollView’s alloc() tell initWithFrame_(vSize) setBorderType_(current application’s NSBezelBorder) setDocumentView_(theDataSource’s _table_view) –setHasHorizontalScroller_(true) setHasVerticalScroller_(true) set aScroll to it end tell end tell return {aScroll, theDataSource’s _table_view} end makeContentView |
AppleScript名:subClasses |
#MARK: – NSValueTransformer script YKZURLToIcon property parent : class "NSValueTransformer" property allowsReverseTransformation : false –>逆変換 property transformedValueClass : a reference to current application’s NSImage –>クラス #変換処理 on transformedValue:fileURL if fileURL is missing value then return set appPath to fileURL’s |path|() set iconImage to current application’s NSWorkspace’s sharedWorkspace’s iconForFile:appPath return iconImage end transformedValue: end script script YKZURLToDisplayedName property parent : class "NSValueTransformer" property allowsReverseTransformation : false –>逆変換 property transformedValueClass : a reference to current application’s NSString –>クラス #変換処理 on transformedValue:fileURL if fileURL is missing value then return set appPath to fileURL’s |path|() set displayedName to current application’s NSFileManager’s defaultManager’s displayNameAtPath:appPath return displayedName end transformedValue: end script #MARK: – Array Controller script YKZArrayController property parent : class "NSArrayController" #MARK: IBOutlets property _table_view : missing value #MARK: property _data_type : do shell script "uuidgen" property _file_type : "" on awakeFromNib() –log "YKZArrayController – awakeFromNib" tell _table_view registerForDraggedTypes_({current application’s NSFilenamesPboardType, _data_type}) setDraggingSourceOperationMask_forLocal_(current application’s NSDragOperationCopy, false) end tell #Transformerの登録 set tNames to {} set tNames’s end to "YKZURLToIcon" set tNames’s end to "YKZURLToDisplayedName" repeat with aTransformer in tNames set theTransformer to current application’s class aTransformer’s new() (current application’s NSValueTransformer’s setValueTransformer:theTransformer forName:aTransformer) end repeat end awakeFromNib #MARK: Data Source Overrides on numberOfRowsInTableView:aTableView return my content()’s |count|() end numberOfRowsInTableView: on tableView:aTableView viewForTableColumn:aColumn row:aRow –log "viewForTableColumn" set aCellView to aTableView’s makeViewWithIdentifier:"YKZTableCellView" owner:me –set isNull to aCellView’s isEqual:(current application’s NSNull’s |null|()) –log isNull as text if aCellView is missing value then set frameRect to current application’s NSMakeRect(0, 0, aColumn’s width, aTableView’s rowHeight()) set aCellView to current application’s YKZTableCellView’s alloc’s initWithFrame:frameRect set anObj to "YKZURLToIcon" set aKey to current application’s NSValueTransformerNameBindingOption set bOptions to (current application’s NSDictionary’s dictionaryWithObject:anObj forKey:aKey) (aCellView’s imageView())’s bind:(current application’s NSValueBinding) toObject:aCellView withKeyPath:"objectValue.fileURL" options:bOptions set anObj to "YKZURLToDisplayedName" set aKey to current application’s NSValueTransformerNameBindingOption set bOptions to (current application’s NSDictionary’s dictionaryWithObject:anObj forKey:aKey) (aCellView’s textField())’s bind:(current application’s NSValueBinding) toObject:aCellView withKeyPath:"objectValue.fileURL" options:bOptions end if return aCellView end tableView:viewForTableColumn:row: # テーブル内のセルが編集できるか on tableView:aTableView shouldEditTableColumn:aColumn row:aRow return false end tableView:shouldEditTableColumn:row: # テーブル内をダブルクリックしたらOKボタンを押す on doubleAction:sender log "doubleAction" ## ヘッダをクリックした時は何もしない if (sender’s clickedRow()) is -1 then return set theEvent to current application’s NSEvent’s ¬ keyEventWithType:(current application’s NSEventTypeKeyDown) ¬ location:(current application’s NSZeroPoint) ¬ modifierFlags:0 ¬ timestamp:0.0 ¬ windowNumber:(sender’s |window|()’s windowNumber()) ¬ context:(current application’s NSGraphicsContext’s currentContext()) ¬ |characters|:return ¬ charactersIgnoringModifiers:(missing value) ¬ isARepeat:false ¬ keyCode:0 current application’s NSApp’s postEvent:theEvent atStart:(not false) end doubleAction: #MARK: Drag Operation Method #ドラッグを開始(ペーストボードに書き込む) on tableView:aTableView writeRowsWithIndexes:rowIndexes toPasteboard:pboard –log "writeRowsWithIndexes" set aData to current application’s NSKeyedArchiver’s archivedDataWithRootObject:rowIndexes pboard’s declareTypes:{_data_type} owner:(missing value) pboard’s setData:aData forType:_data_type return true end tableView:writeRowsWithIndexes:toPasteboard: #ドラッグ途中 on tableView:aTableView validateDrop:info proposedRow:row proposedDropOperation:operation –log "validateDrop" #列の間にドラッグ if (operation is current application’s NSTableViewDropAbove) then return current application’s NSDragOperationMove end if #項目の上にドラッグ set aTypes to info’s draggingPasteboard’s types() if (aTypes’s containsObject:_data_type) as boolean then return current application’s NSDragOperationNone end if return current application’s NSDragOperationNone end tableView:validateDrop:proposedRow:proposedDropOperation: #ドラッグ終了 on tableView:aTableView acceptDrop:info row:row dropOperation:operation –log "acceptDrop" # set pboard to info’s draggingPasteboard() set aTypes to pboard’s types() if (aTypes’s containsObject:_data_type) as boolean then set rowData to pboard’s dataForType:_data_type set rowIndexes to current application’s NSKeyedUnarchiver’s unarchiveObjectWithData:rowData if (rowIndexes’s firstIndex()) < row then set row to row – (rowIndexes’s |count|()) end if set aRange to current application’s NSMakeRange(row, rowIndexes’s |count|()) set aIndexSet to current application’s NSIndexSet’s indexSetWithIndexesInRange:aRange set anObj to ((my content())’s objectsAtIndexes:rowIndexes) my removeObjects:anObj my insertObjects:anObj atArrangedObjectIndexes:aIndexSet my rearrangeObjects() else set pathList to pboard’s propertyListForType:(current application’s NSFilenamesPboardType) –log result as list repeat with aPath in pathList set isAdd to addItem_({filePath:aPath, atIndex:row}) if isAdd then beep end repeat end if return true end tableView:acceptDrop:row:dropOperation: #追加する on addItem:sender –> {filePath:aPath, atIndex:aIndex} –log "addItem:" set isAdd to false #missing valueの時は最後に追加 set {filePath:aPath, atIndex:aIndex} to sender if aIndex is missing value then set aIndex to my content()’s |count|() as number end if #URLに変換 set appURL to (current application’s NSURL’s fileURLWithPath:aPath) #アプリケーション以外は追加しない set {aResult, aUTI, aError} to (appURL’s getResourceValue:(reference) ¬ forKey:(current application’s NSURLTypeIdentifierKey) ¬ |error|:(reference)) log aUTI if (aUTI as text) is not (my _file_type as text) then return isAdd #すでにあるか確認 set bList to my content() as list repeat with aRecord in bList if ((appURL’s isEqual:(aRecord’s fileURL)) as boolean) then set isAdd to true exit repeat end if end repeat #なければ追加 if not isAdd then set anObj to {fileURL:appURL} (my insertObject:anObj atArrangedObjectIndex:aIndex) end if return isAdd end addItem: end script #MARK: – NSTableCellView script YKZTableCellView property parent : class "NSTableCellView" on initWithFrame:rect –log "initWithFrame" continue initWithFrame:rect setAutoresizingMask_(current application’s NSViewWidthSizable) tell current application’s NSImageView’s alloc tell initWithFrame_(current application’s NSMakeRect(0, 0, 16, 16)) setImageScaling_(current application’s NSImageScaleProportionallyUpOrDown) setImageAlignment_(current application’s NSImageAlignCenter) my setImageView:it my addSubview:it end tell end tell tell current application’s NSTextField’s alloc tell initWithFrame_(current application’s NSMakeRect(20, 2, 200, 14)) setBordered_(false) setDrawsBackground_(false) setEditable_(false) my setTextField:it my addSubview:it end tell end tell return me end initWithFrame: end script |