I’m using a recursive code to explore down the file system. I’m using FolderItem.isFolder to decide whether to recurse. It works, but it is not just recursing into real folders, but also into file packages (“files” like Keynote .key and .rtfd files). Is there a way that I can get my code to tell which is which and to treat a package like a file rather than a folder?
Any valid package will contain a Contents folder and inside that an Info.plist file. You could create an isPackage method to detect that. Something along the lines of:
// Put the method in a Module
Function isPackage( Extends oFolder as FolderItem ) as Boolean
Var Contents as FolderItem = oFolder.Child( "Contents" )
If Contents = Nil Then Return False
If Not Contents.Exists Then Return False
If not Contents.isFolder Then Return False
Var Infoplist as FolderItem = Contents.Child( "Info.plist" )
If Infoplist = Nil Then Return False
If Not Infoplist.Exists Then Return False
If Infoplist.isFolder Then Return False
If not Infoplist.Exists Then Return False
Return True
End Function
// Use it like:
Var oFolderItem As New FolderItem( "/Applications/Pages.app", FolderItem.PathModes.Shell )
MessageBox CStr( oFolderItem.isPackage )
Thanks. That makes sense. I will give it a try.
It seems odd that for something so pervasive on a Mac as packages there is not a built-in feature (say, a FolderItem property) to do this.
If you want to have the same behavior as Finder, you need to ask the OS via API. We have a couple of functions in the plugin which use various APIs to do this.
And technically you could mark any folder as package with some file attributes.
I asked long time ago the same question and someone gave me the answer.
MyFlag = MyFolderItem.IsPackage_f
Public Function IsPackage_f(Extends CetElt as FolderItem) As Boolean
Dim DrapPackage as Boolean ' Retourne Vrai si CetElt est un Package (.app .rtfd . pkg .mpkg etc.) et Faux sinon. Note : J'utilise Extends pour pouvoir écrire MonElt.IsPackage_f
#If DebugBuild Then ' Si CetElt n'existe pas ça plantera, doit être testé avant
If not CetElt.IsFolder Then MessageBox "Problème Tom, IsPackage_f , tu ne devrais (dois ?) y tester que des dossiers !"
#EndIf
#If TargetMacOS Then
' #If RBVersion < 2013 Then ' RealStudio donc 10.5 (et +) ' If TargetCarbon Then
' Declare Function LSCopyItemInfoForRef lib "ApplicationServices" (ref as Ptr, whichInfo as Integer, info as Ptr) as Integer
' Dim err as Integer
' Dim rec as New MemoryBlock(24)
'
' err = LSCopyItemInfoForRef(CetElt.FSRef_f, &h4, rec)
' DrapPackage = not(Bitwise.BitAnd(rec.Long(0), 2) = 0)
'
' #Else ' Xojo donc 10.6 et + ' #If TargetCocoa Then
Declare Function boolValue Lib "Foundation" selector "boolValue" ( obj as ptr ) as Boolean
Dim isPackage as ptr
If GetFileValue(CetElt, "NSURLIsPackageKey", isPackage) Then
DrapPackage = boolValue(isPackage)
Else
' At this point, isPackage isa NSError. Do something with it or just assume that it's not a package.
DrapPackage = False
End If
' #EndIf ' Xojo ou RealStudio
#Else ' Windows , Linux
DrapPackage = False
#EndIf
' Méthose Commande Shell
'#If TargetMacOS Then
'Dim CdeShell as New Shell
'' NE PAS utiliser "'" + MonFolderItem.NativePath = MonFolderItem.ShellPath_fAS + "'"
'' NON : CdeShell.Execute "mdls -name kMDItemContentTypeTree '" + CetElt.ShellPath + "'"
'' On n'utilise pas le Quotedform avec ' ' car s'il y a un ' dans le nom ça merde
'' CdeShell.Execute "mdls " + CetElt.ShellPath
'' MessageBox CdeShell.Result
'' Avant : CdeShell.Execute "mdls -name kMDItemContentType -raw " + CetElt.ShellPath
'CdeShell.Execute "mdls -name kMDItemContentTypeTree " + CetElt.ShellPath
'If CdeShell.ErrorCode = 0 Then
'' MessageBox CdeShell.Result
'DrapPackage = not(CdeShell.Result.IndexOf("com.apple.package", ComparisonOptions.CaseInsensitive, AlocaleProg) = -1)
'' Avant : DrapPackage = not((CdeShell.Result = "public.folder") or (CdeShell.Result = "public.volume"))
'Else ' Dossier sur lequel on n'a pas les droits ou etc.
'DrapPackage = False
'' MessageBox "Error code : " + Str(CdeShell.ErrorCode)
'End If
'CdeShell.Close
'
'' Avant avec AppleScript mais c'est plus lent :
'' DrapPackage = (IsPackage_s(CetElt.NativePath = CetElt.ShellPath_fAS) = "True") ' Méthode AppleScript plus lente que méthode avec Carbon
'
'#Else
'DrapPackage = False
'
'#EndIf
Return DrapPackage
End Function
I didn’t erase all the comment I wrote. You must test before if you FolderItem is a folder, and if yes test if it is a package.
Yes, thanks, FolderItem.isBundleMBS does the trick. BTW I like the overridden “S” method (with different behaviours for different parameters) in the File information example; I hadn’t thought about doing that before.