With the novice’s usual techniques of searching, piecing together, trial and error, etc, I have managed to implement an NSImageView drag-and-drop, the details of which are described below. My implementation does work as expected. I am submitting this post rather for educational purposes, i.e., to ask if there is a better way of doing things than my solution, which seems somewhat complex. (It seems that in Cocoa, there is more often than not an easier way of doing things.)
My goal was for the image view to accept a drag-and-drop from any the following:
- an image file of any of the following types: JPEG, PNG, TIFF, GIF, BMP, PICT
- a folder or volume
- a symlink or alias pointing to either of the preceding two categories
and to reject any other type of drag-and-drop. With a successful drag-and-drop, the image view should set its itemPath property to the posix path of the dropped item (in the case of a symlink or alias, the posix path of the original item).
I placed an image well NSImageView in my window, then subclassed it as follows:
property NSFileManager : class "NSFileManager"
property |NSURL| : class "NSURL"
script myImageView
property parent : class "NSImageView"
property itemPath : ""
on draggingEntered_(sender)
-- Get the URL of the dragged item
set theURL to sender's draggingPasteboard()'s readObjectsForClasses_options_({|NSURL|}, missing value)'s lastObject()
-- If the dragged item is a symlink or alias, get the URL of the original item
tell theURL
set {isSymlink, isAliasFile} to {false, false}
tell its getResourceValue_forKey_error_(reference, current application's NSURLTypeIdentifierKey, missing value)
try
set {valueFound, uniformTypeIdentifier} to {first item as integer = 1, "" & second item}
if valueFound then tell uniformTypeIdentifier to set {isSymlink, isAliasFile} to {it = "public.symlink", it = "com.apple.alias-file"}
end try
end tell
if isSymlink then
set resolvedPath to NSFileManager's defaultManager()'s destinationOfSymbolicLinkAtPath_error_(its |path|(), missing value)
tell resolvedPath to if it ≠missing value then set theURL to |NSURL|'s fileURLWithPath_(it)
else if isAliasFile then
set bookmarkData to |NSURL|'s bookmarkDataWithContentsOfURL_error_(it, missing value)
set theURL to |NSURL|'s URLByResolvingBookmarkData_options_relativeToURL_bookmarkDataIsStale_error_(bookmarkData, current application's NSURLBookmarkResolutionWithoutUI, missing value, missing value, missing value)
end if
end tell
-- Determine if the item should be accepted for drag-and-drop depending on its type
set shouldAcceptDrag to false
tell theURL's getResourceValue_forKey_error_(reference, current application's NSURLTypeIdentifierKey, missing value)
try
set {valueFound, uniformTypeIdentifier} to {first item as integer = 1, "" & second item}
set shouldAcceptDrag to valueFound and (uniformTypeIdentifier is in {"public.jpeg", "public.jpeg-2000", "public.png", "public.tiff", "com.compuserve.gif", "com.microsoft.bmp", "com.apple.pict", "public.folder", "public.volume"})
end try
end tell
-- If the item is a valid type, accept entry into the image view in preparation for drag-and-drop, and set the subclass's itemPath property to the item's posix path
if shouldAcceptDrag then
set my itemPath to "" & theURL's |path|()
return current application's NSDragOperationCopy
else
return current application's NSDragOperationNone
end if
end draggingEntered_
on performDragOperation_(sender)
-- Finalize the drag-and-drop
log my itemPath --@@
return true
end performDragOperation_
end script
I registered the myImageView subclass for drag-and-drop in the app delegate’s initialize handler as follows:
script myAppDelegate
property parent : class "NSObject"
property imageView : missing value -- this is an outlet to the window's image view
on initialize()
-- Instantiate the image view, and register it for drag-and-drop of an NSURL
set my imageView to current application's myImageView's alloc()'s init()
tell my imageView to its registerForDraggedTypes_({"public.file-url"})
end initialize
on applicationWillFinishLaunching_(aNotification)
end applicationWillFinishLaunching_
on applicationShouldTerminate_(sender)
return current application's NSTerminateNow
end applicationShouldTerminate_
end script
The questions I would like to ask are:
Is there a simpler or more elegant way to resolve a symlink or alias to its original item?
Is there a simpler or more elegant way for the image view to screen dragged items for acceptance or rejection based on their Uniform Type Identifier type?
What other ways might the drag-and-drop be made more optimal?