Loading file list from a folder

Thank you Shane.

I misunderstood what I read in Everyday AppleScriptObjC 3ed and tried: “%K[cd] CONTAINS %@[cd]”.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 6 février 2019 10:05:31

Is it normal that using %K IN[c] %@ has no effect upon the test upon the extension?

With : set theExtensions to {“RTF”}

the script return:
{file “SSD 500:Users::desktop:Barbara studio.rtf", file "SSD 500:Users::desktop:BARBARA, Comme Un Soleil Noir.numbers”, file “SSD 500:Users:**********:desktop:Barbara.rtf”}

With : set theExtensions to {“rtf”} it return :
{file “SSD 500:Users:**********:desktop:BARBARA, Comme Un Soleil Noir.numbers”}

It’s not frequent to see the extension spelled “RTF” but the problem is common with “pdf” versus “PDF”.

Of course, if there is no need for a list of extensions we may use :
(%K ==[c] %@) and set theExtensions to “RTF” which return
{file “SSD 500:Users:**********:desktop:BARBARA, Comme Un Soleil Noir.numbers”}

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 6 février 2019 11:42:12

I think so. In fact, I suspect my example above doesn’t actually work – it probably only works with direct text comparisons, such as == or LIKE.

Thanks Shane.

I tested CONTAINS, LIKE and ==.
They apply to string comparison, not to comparison upon items of a list.

%K CONTAINS[c] %@ return file names containing “BARBARA” as well as “Barbara”

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 6 février 2019 12:19:58

Hi.

As a practical solution, this variation on Shane’s handler seems to work. I don’t know if it’s supposed to! :wink:

use AppleScript version "2.5" -- 10.11 or later
use framework "Foundation"
use scripting additions

on listFilesIn:sourceAliasOrFile fileStub:fileStub excludedExtensions:theExtensions
	set fileManager to current application's NSFileManager's defaultManager()
	set theURLs to fileManager's contentsOfDirectoryAtURL:sourceAliasOrFile includingPropertiesForKeys:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
	
	set lowercaseExtensions to (current application's NSArray's arrayWithArray:theExtensions)'s valueForKey:"lowercaseString" -- List of lower-cased extensions.
	set thePred to current application's NSPredicate's predicateWithFormat:"%K ==[c] %@ AND !(%K.lowercaseString IN %@)" argumentArray:{"lastPathComponent.stringByDeletingPathExtension", fileStub, "pathExtension", lowercaseExtensions} -- Modified predicate.
	set theURLs to theURLs's filteredArrayUsingPredicate:thePred
	return theURLs as list
end listFilesIn:fileStub:excludedExtensions:

Thank you Nigel.
It works. It’s exactly what I hoped to get.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 6 février 2019 15:52:30

Thanks fellows. I’ve updated my script to include the updated handler and it works fine.

Ingenious :wink:

Actually, this is simpler: :slight_smile:

use AppleScript version "2.5" -- 10.11 or later
use framework "Foundation"
use scripting additions

on listFilesIn:sourceAliasOrFile fileStub:fileStub excludedExtensions:theExtensions
	set fileManager to current application's NSFileManager's defaultManager()
	set theURLs to fileManager's contentsOfDirectoryAtURL:sourceAliasOrFile includingPropertiesForKeys:{} options:(current application's NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
	
	set thePred to current application's NSPredicate's predicateWithFormat:"lastPathComponent.stringByDeletingPathExtension ==[c] %@ AND NOT ANY %@ ==[c] pathExtension" argumentArray:{fileStub, theExtensions}
	set theURLs to theURLs's filteredArrayUsingPredicate:thePred
	return theURLs as list
end listFilesIn:fileStub:excludedExtensions:

I’ve downloaded this updated version, Shane. Will give it a try.

Shane;

I tried the last routine you posted and it worked well.

I do have one other item to ask about this topic.

When using the list to open files, one can end up switching back-and-forth between default apps. This interleaving of the apps, used to open the files,seems to cause the order on the screen to not match the order in which they were opened. However; if the app calls are grouped, this appears not to be an issue.

Unfortunately; I can’t think of a “fast” way to order the URL list by the app type. It would be great if there was a way to augment your routine to do this. As I’m not that familiar with the AppleScriptObjC world yet, I could use a nudge in the right direction.

Model: MacBook Pro (retina)
AppleScript: 2.9
Browser: Firefox 64.0
Operating System: macOS 10.14

Well…

My guess was correct. I wrote an AppleScriptObjC sorting routine, for a list of app-filename lists, to make sure that all files with the same app were grouped together. When the list of files was opened, using the default apps, the windows were stacked in the order they had been commanded to open.

It’s a bit slow, but I’ve a lot of debug and logging code in it at present. I hope the lean version will run faster.

The following are the relevant snippets from my code (which I’ll post when I have the move and size windows portion working:

		-- --------------------------------------------------------------------------------
		-- create a list of the app and the file it should open
		-- --------------------------------------------------------------------------------
		set theSortedList to {}
		repeat with theFile in theList -- this is the output of Shane's routine
			-- --------------------------------------------------------------------------------
			-- get the name of the default_app for the file that is to be opened.
			-- --------------------------------------------------------------------------------
			set default_app to getDefaultApp(pathExtensionFromFileReference(theFile))
			copy {theFile:(theFile as string), theApp:(default_app as string)} to the end of theSortedList
		end repeat
		
		set anArray to current application's NSArray's arrayWithArray:theSortedList
		set theDesc to current application's NSSortDescriptor's sortDescriptorWithKey:"theApp" ascending:true selector:"caseInsensitiveCompare:"
		set theDesc2 to current application's NSSortDescriptor's sortDescriptorWithKey:"theFile" ascending:true selector:"caseInsensitiveCompare:"
		set theSortedList to (anArray's sortedArrayUsingDescriptors:{theDesc, theDesc2}) as list


-------------------------------------------------------
-- code removed here for posting purposes --
-------------------------------------------------------


-- -------------------------------------------------------------
-- handler to get the default app for a given filename extension.
-- -------------------------------------------------------------
on getDefaultApp(pExtStr)
	
	-- Based on @ShaneStanley's script & enhancements
	-- http://forum.latenightsw.com/t/how-do-i-get-the-default-app/830/2
	--  2017-11-26
	
	set thePath to (POSIX path of (path to temporary items)) & "temp." & pExtStr
	
	set nsCurApp to current application
	
	--- Create a Temp File with Extension ---
	nsCurApp's NSFileManager's defaultManager()'s createFileAtPath:thePath |contents|:(missing value) attributes:(missing value)
	
	set ws to nsCurApp's NSWorkspace's sharedWorkspace()
	
	--- Get URL of Default App for That Extension ---
	set nsAppURL to ws's URLForApplicationToOpenURL:(nsCurApp's |NSURL|'s fileURLWithPath:thePath)
	
	--- Get the FileName of the App ---
	set {nsResult, nsAppName, nsError} to nsAppURL's getResourceValue:(reference) forKey:(nsCurApp's NSURLLocalizedNameKey) |error|:(reference)
	
	--- Get Just the Root Name of the App ---
	set appName to nsAppName as text
	set appName to text 1 thru ((offset of "." in (appName as text)) - 1) of appName
	
	return appName
	
end getDefaultApp


You can potentially speed it up a fair bit by eliminating duplicate calls to the expensive getDefaultApp() handler, something like this:

set theSortedList to {}
set appDetails to current application's NSMutableDictionary's dictionary()
repeat with theFile in theList 
	-- --------------------------------------------------------------------------------
	-- get the name of the default_app for the file that is to be opened.
	-- --------------------------------------------------------------------------------
	set theExt to pathExtensionFromFileReference(theFile)
	set default_app to (appDetails's objectForKey:theExt)
	if default_app is missing value then
		set default_app to getDefaultApp(theExt)
		(appDetails's setObject:default_app forKey:theExt)
	end if

Thank you, Shane.

I tried it out and it does work a bit quicker. I can understand what you were doing, so I guess I must be learning something (although it hasn’t been easy).

Also, you could adapt the getDefaultApp() handler to work with the current file instead of making a new file with the current file’s extension.

Hi Shane;

Yes, I wondered about creating the temp file to find the app, since one already has a typecase in the input file. I’ll give it a try.

(Have you considered querying Launch Services? I did a bit of reading and have a niggling suspicion that it mag also be able to tell one the default app. Would it be a better way? I don’t know.)

My name’s Nigel. :slight_smile:

Sorry Nigel. I’m a little out of it. Not sleeping well. Thanks for your suggestion.

Launch Service has a C-style API, not Objective-C, so it’s out of reach of ASObjC.

I’m going back over the code I’ve extracted from the previous posts and would like to ask a question about the variables passed from AppleScript to ObjC routines.

In some code I see a step that converts AppleScript strings to “Cocoa” strings. I believe that this has been used to indicate a conversion to what the Language Reference calls, (NSString *)string

set cocoaString to current application's NSString's stringWithString:theFilename

However; I don’t see this conversions being done in all code examples, nor for other types of AppleScript variables.

I suspect the answer lies in some form of casting of certain AppleScript data types that is going on, but I’m still learning and don’t know for sure.

Can one of the skilled coders shed some light on this?

Meanwhile, I’m also going to go back and read “Everyday AppleScriptObjC” again and see if I can figure out why.