Hi, I just finished this script which groups selected Finder items by common prefix.
Two questions:
I’ve used checkResourceIsReachableAndReturnError: to test whether a directory exists but am not sure that’s the right one. Should I use fileExistsAtPath:isDirectory: instead?
I’m also not sure about creating the directory with createDirectoryAtURL. Can I run into permission trouble or is this automatically handled even if I pass no attributes?
First time I’m doing something like this so it’s potentially harmful … but it seems to work
-- Group selected Finder items
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
property theGenericFolderName : "Neuer Ordner mit Objekten"
tell application "Finder"
try
set theSelection to selection as alias list
if theSelection = {} then error "Keine Auswahl vorhanden"
on error error_message number error_number
if the error_number is not -128 then display alert "Finder" message error_message as warning
return
end try
end tell
set theURLs to current application's NSArray's arrayWithArray:theSelection
set theNames to current application's NSMutableArray's arrayWithArray:{}
repeat with thisURL in theURLs
(theNames's addObject:(thisURL's lastPathComponent()))
end repeat
set theNames_Count to theNames's |count|()
if theNames_Count > 2 then
set theNames_sortedByLength to (theNames's sortedArrayUsingDescriptors:{current application's NSSortDescriptor's sortDescriptorWithKey:"length" ascending:true selector:"compare:"})
set thePrefix to (theNames_sortedByLength's objectAtIndex:0)'s commonPrefixWithString:(theNames_sortedByLength's objectAtIndex:1) options:(current application's NSCaseInsensitiveSearch)
if (thePrefix's isEqualToString:"") then
set theCommonPrefix to (current application's NSString's stringWithString:theGenericFolderName)
else
set theCommonPrefix to my getCommonPrefix(thePrefix, theNames_sortedByLength, theNames_Count)
end if
else if theNames_Count = 2 then
set theCommonPrefix to (theNames's objectAtIndex:0)'s commonPrefixWithString:(theNames's objectAtIndex:1) options:(current application's NSCaseInsensitiveSearch)
if (theCommonPrefix's isEqualToString:"") then set theCommonPrefix to (current application's NSString's stringWithString:theGenericFolderName)
else if theNames_Count = 1 then
set theCommonPrefix to (((current application's |NSURL|'s fileURLWithPath:(POSIX path of item 1 of theSelection))'s URLByDeletingPathExtension())'s pathComponents())'s lastObject()
end if
if (theCommonPrefix's isEqualToString:theGenericFolderName) = false then
set theCharacterSet to current application's NSMutableCharacterSet's alloc()'s init()
theCharacterSet's formUnionWithCharacterSet:(current application's NSCharacterSet's punctuationCharacterSet())
theCharacterSet's formUnionWithCharacterSet:(current application's NSCharacterSet's whitespaceAndNewlineCharacterSet())
set theCommonPrefix to theCommonPrefix's stringByTrimmingCharactersInSet:theCharacterSet
end if
set theFolderURL to (current application's |NSURL|'s fileURLWithPath:(POSIX path of item 1 of theSelection))'s URLByDeletingLastPathComponent()
set theNewFolderURL to theFolderURL's URLByAppendingPathComponent:theCommonPrefix isDirectory:true
set createdDirectory to current application's NSFileManager's defaultManager's createDirectoryAtURL:theNewFolderURL withIntermediateDirectories:false attributes:(missing value) |error|:(missing value)
if createdDirectory = false then
set i to 2
repeat
set theNewFolderName to (current application's NSString's stringWithString:((theCommonPrefix as string) & space & i))
set theNewFolderURL to (theFolderURL's URLByAppendingPathComponent:theNewFolderName isDirectory:true)
set existsURL to (theNewFolderURL's checkResourceIsReachableAndReturnError:(missing value))
if existsURL = false then
exit repeat
else
set i to i + 1
end if
end repeat
set {createdDirectory, theError} to current application's NSFileManager's defaultManager's createDirectoryAtURL:theNewFolderURL withIntermediateDirectories:false attributes:(missing value) |error|:(reference)
if createdDirectory = false then
display alert "Error" message ((theError's localizedDescription()) as string) buttons {"Ok"} default button {"Ok"} as critical
return
end if
end if
set theNewFolderPath to (theNewFolderURL's |path|()) as string
tell application "System Events"
try
move theSelection to folder theNewFolderPath
on error error_message number error_number
if the error_number is not -128 then display alert "System Events" message error_message as warning
return
end try
end tell
on getCommonPrefix(theCommonPrefix, theNames, theNames_Count)
try
repeat with i from 2 to (theNames_Count - 1)
if (((theNames's objectAtIndex:i))'s hasPrefix:theCommonPrefix) = false then
set theCommonPrefix_Length to theCommonPrefix's |length|()
if theCommonPrefix_Length > 1 then
set theCommonPrefix_shortened to (theCommonPrefix's substringToIndex:(theCommonPrefix_Length - 1))
set theCommonPrefix to my getCommonPrefix(theCommonPrefix_shortened, theNames, theNames_Count)
else
set theCommonPrefix to (current application's NSString's stringWithString:theGenericFolderName)
exit repeat
end if
end if
end repeat
return theCommonPrefix
on error error_message number error_number
activate
display alert "Error: Handler \"getCommonPrefix\"" message error_message as warning
error number -128
end try
end getCommonPrefix
Thanks! I’ve added a check whether all selected items are in one folder, if not ask where to create the new one. However choose folder run via Finder is painfully slow and using System Events doesn’t help much. Tried the script in Everyday AppleScriptObjC’s chapter “Richer Interfaces” but it doesn’t seem to work anymore. Is it still possible to use ASObjC dialogs? Are they faster than choose folder?
-- Group selected Finder items
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
property theGenericFolderName : "Neuer Ordner mit Objekten"
tell application "Finder"
try
set theSelection to selection as alias list
if theSelection = {} then error "Keine Auswahl vorhanden"
on error error_message number error_number
if the error_number is not -128 then display alert "Finder" message error_message as warning
return
end try
end tell
set theURLs to current application's NSArray's arrayWithArray:theSelection
set theURLs_Count to theURLs's |count|()
if theURLs_Count ≥ 2 then
-- Check whether items are all in one folder
set theURLs_sortedByPath to (theURLs's sortedArrayUsingDescriptors:{current application's NSSortDescriptor's sortDescriptorWithKey:"path" ascending:true selector:"compare:"})
set theFirstFolderURL to theURLs_sortedByPath's firstObject()'s URLByDeletingLastPathComponent()
set theLastFolderURL to theURLs_sortedByPath's lastObject()'s URLByDeletingLastPathComponent()
if theFirstFolderURL's isEqualTo:theLastFolderURL then
set theFolderURL to theFirstFolderURL
else
set theFirstFolderURLComponents to theFirstFolderURL's pathComponents()
set theLastFolderURLComponents to theLastFolderURL's pathComponents()
set theCommonFolderURLComponentsArray to current application's NSMutableArray's arrayWithArray:{}
repeat with i from 1 to ((theFirstFolderURLComponents's |count|()) - 1)
if ((theFirstFolderURLComponents's objectAtIndex:i)'s isEqualTo:(theLastFolderURLComponents's objectAtIndex:i)) then
(theCommonFolderURLComponentsArray's addObject:(theFirstFolderURLComponents's objectAtIndex:i))
else
exit repeat
end if
end repeat
if theCommonFolderURLComponentsArray's |count|() > 0 then
set theCommonFolderPath to (current application's |NSURL|'s fileURLWithPathComponents:theCommonFolderURLComponentsArray)'s |path|()
else
set theCommonFolderPath to current application's NSHomeDirectory()
end if
tell application "System Events"
try
activate
set theFolder to choose folder default location (POSIX file (theCommonFolderPath as string) as alias)
activate application "Finder"
on error
activate application "Finder"
return
end try
end tell
set theFolderURL to current application's |NSURL|'s fileURLWithPath:(POSIX path of theFolder)
end if
-- Get names
set theNames to current application's NSMutableArray's arrayWithArray:{}
repeat with thisURL in theURLs
(theNames's addObject:(thisURL's lastPathComponent()))
end repeat
-- Get common prefix
if theURLs_Count > 2 then
set theNames_sortedByLength to (theNames's sortedArrayUsingDescriptors:{current application's NSSortDescriptor's sortDescriptorWithKey:"length" ascending:true selector:"compare:"})
set thePrefix to (theNames_sortedByLength's objectAtIndex:0)'s commonPrefixWithString:(theNames_sortedByLength's objectAtIndex:1) options:(current application's NSCaseInsensitiveSearch)
if (thePrefix's isEqualToString:"") then
set theCommonPrefix to (current application's NSString's stringWithString:theGenericFolderName)
else
set theCommonPrefix to my getCommonPrefix(thePrefix, theNames_sortedByLength, theURLs_Count)
end if
else if theURLs_Count = 2 then
set theCommonPrefix to (theNames's objectAtIndex:0)'s commonPrefixWithString:(theNames's objectAtIndex:1) options:(current application's NSCaseInsensitiveSearch)
if (theCommonPrefix's isEqualToString:"") then
set theCommonPrefix to (current application's NSString's stringWithString:theGenericFolderName)
end if
end if
else if theURLs_Count = 1 then
set theFolderURL to (theURLs's objectAtIndex:0)'s URLByDeletingLastPathComponent()
set theCommonPrefix to (theURLs's objectAtIndex:0)'s URLByDeletingPathExtension()'s pathComponents()'s lastObject()
end if
-- Trim folder name
if (theCommonPrefix's isEqualToString:theGenericFolderName) = false then
set theCharacterSet to current application's NSMutableCharacterSet's alloc()'s init()
theCharacterSet's formUnionWithCharacterSet:(current application's NSCharacterSet's punctuationCharacterSet())
theCharacterSet's formUnionWithCharacterSet:(current application's NSCharacterSet's whitespaceAndNewlineCharacterSet())
set theCommonPrefix to theCommonPrefix's stringByTrimmingCharactersInSet:theCharacterSet
end if
-- Create folder URL
set theNewFolderURL to theFolderURL's URLByAppendingPathComponent:theCommonPrefix isDirectory:true
-- Create folder
set createdDirectory to current application's NSFileManager's defaultManager's createDirectoryAtURL:theNewFolderURL withIntermediateDirectories:false attributes:(missing value) |error|:(missing value)
if createdDirectory = false then
set i to 2
repeat
set theNewFolderName to (current application's NSString's stringWithString:((theCommonPrefix as string) & space & i))
set theNewFolderURL to (theFolderURL's URLByAppendingPathComponent:theNewFolderName isDirectory:true)
set existsURL to (theNewFolderURL's checkResourceIsReachableAndReturnError:(missing value))
if existsURL = false then
exit repeat
else
set i to i + 1
end if
end repeat
set {createdDirectory, theError} to current application's NSFileManager's defaultManager's createDirectoryAtURL:theNewFolderURL withIntermediateDirectories:false attributes:(missing value) |error|:(reference)
if createdDirectory = false then
display alert "Error" message ((theError's localizedDescription()) as string) buttons {"Ok"} default button {"Ok"} as critical
return
end if
end if
set theNewFolderPath to (theNewFolderURL's |path|()) as string
-- Move items
tell application "System Events"
try
move theSelection to folder theNewFolderPath
on error error_message number error_number
if the error_number is not -128 then display alert "System Events" message error_message as warning
return
end try
end tell
on getCommonPrefix(theCommonPrefix, theNames, theURLs_Count)
try
repeat with i from 2 to (theURLs_Count - 1)
if (((theNames's objectAtIndex:i))'s hasPrefix:theCommonPrefix) = false then
set theCommonPrefix_Length to theCommonPrefix's |length|()
if theCommonPrefix_Length > 1 then
set theCommonPrefix_shortened to (theCommonPrefix's substringToIndex:(theCommonPrefix_Length - 1))
set theCommonPrefix to my getCommonPrefix(theCommonPrefix_shortened, theNames, theURLs_Count)
else
set theCommonPrefix to (current application's NSString's stringWithString:theGenericFolderName)
exit repeat
end if
end if
end repeat
return theCommonPrefix
on error error_message number error_number
activate
display alert "Error: Handler \"getCommonPrefix\"" message error_message as warning
error number -128
end try
end getCommonPrefix