How do you reveal a file?

Sometimes it is useful to display a file (and you have a FolderItem Reference) in its window…

I previously used FolderItem.Show to do that in the near past.

How can I do that today ?

Here’s the idea using AppleScript:

…and why I used the world “reveal” in the subject line.

Here is what I use:

Protected Sub doRevealFolderItem(f As FolderItem)
  'www.xojoitaliablog.com/evidenziare-un-file-sul-desktop
  If f = Nil Or Not f.Exists Then Return
  #If TargetDesktop Then
    If f.IsFolder Then
      'if f.Name.IndexOf(0, ".") >= 0 then f = f.parent 'dot in folder name therefore reveal the parent e.g. pages
      If f.isBundleMBS Then f = f.Parent
      f.Open(True)
      Return
      
    Else
      #If TargetMacOS Then
        '[Fix] Reveal in Finder now highlights the file itself, not just opens the folder, 
        'using shell.execute(""open --reveal /path/to/my\ file"") or Var ok as Boolean, ok = sendItemAE(""misc"", ""mvis"", ""com.apple.finder"", f) or NSWorkSpaceMBS.selectFile"
        'tempString = "open -R """ + f.NativePath + """"
        'tempShell.Execute(tempString)
        'if tempShell.ExitCode <> 0 then
        'f.parent.Open
        'end if
        'tempShell
        'Return
        
        Call NSWorkSpaceMBS.selectFile(f)
        Return
        
      #ElseIf TargetWindows Then
        Var tempShell As New Shell 'used by Linux
        Var tempString As String 'used by Linux
        
        'returnValue = WinOpenFolderAndSelectItemsMBS(f.parent, Array(f))
        'if returnValue <> 0 then
        'f.parent.Open
        'end if
        
        'tempString = "explorer.exe /select," + f.NativePath 'not revealing folder for MVV
        'tempString = "explorer.exe /select,""" + f.NativePath + """"
        tempString = "explorer.exe /root,/separate,/select,""" + f.NativePath + """" 'run on separate new process
        tempShell.Execute(tempString)
        'If tempShell.ExitCode <> 0 Then
        If tempShell.ExitCode > 1 Then 'Error 1 seems to be success, so don't open the window twice!
          f.Parent.Open(True)
        End If
        tempShell.Close
        
        Return
        
      #ElseIf TargetLinux Then
        Var tempShell As New Shell 'used by Linux
        Var tempString As String 'used by Linux
        
        tempString = "xdg-open """ + f.Parent.NativePath + """"
        tempShell.Execute(tempString)
        If tempShell.ExitCode <> 0 Then
          f.parent.Open
        End If
        Return
        
      #Else
        f = f.Parent
        f.Open
      #EndIf
    End If
  #EndIf
End Sub
2 Likes

For Windows, you can use WinOpenFolderAndSelectItemsMBS function in MBS Xojo Plugin, which may be better than your explorer.exe trick.

Thank you for your answers.

I am retired (nearly 70) and do not have money to invest (sorry Christian).

My workaround is to use:

If Reference_File_FI.Parent <> Nil Then
  Reference_File_FI.Parent.Open
End If

Reference_File_FI is the file FolderItem.

Better than nothing; of course it does not select the target file, but at last, it open and display its window.

David, I have a function I made with the help I found here on this forum, and someone gave me the Windows instruction:
CdeShell.execute("explorer.exe /select, " + CetElt.shellpath)
why do you write:
"explorer.exe /root,/separate,/select,""" + f.NativePath + """"
?
What means " /root,/separate," please? Should I add it?

Emile, you can download on my webpage my “Zx_External” Module which contains a function “RevealEltFinder” (I repeat, I made it with the help of the Xojo community gave me on this forum). I think you will understand how to use the function. But you can download one of my project in which I may use this function like (example my project " ScreenCaptCoord"). My projects with a lock are protected because I spent many time on them.

Thanks Thomas.

IIFC the window might not appear unless it was launched by Explorer on its own process. I cannot remember where I found this out, possibly StackFlow. I seem to remember testing it until the file appeared correctly on Windows 10.

I’m using the following in macOS:

Public Sub showInFinder(f as FolderItem)
  declare function objc_getClass lib "libobjc.dylib" (name as CString) as ptr
  declare function sharedWorkspace Lib "AppKit" selector "sharedWorkspace" (obj As ptr) As ptr
  declare function selectFile lib "AppKit" selector "selectFile:inFileViewerRootedAtPath:" (obj as ptr, fPath as CFStringRef, rootFullPath as CFStringRef) as boolean
  var workspace as ptr = sharedWorkspace(objc_getClass("NSWorkspace"))
  try
    call selectFile(workspace, f.NativePath, "")
  catch
    System.DebugLog("showInFinder: error")
  end try
End Sub

2 Likes

Grzegorz Pawlik, that’s exactly the code I pasted in my function, except I did not write the Try Catch for the call selectFile(workspace, f.NativePath, “”) . I never get an error, I suppose error are handles in the function selectFile and it return something depending of error or not. I don’t remember the Try Catch on the code I copy from this forum. I will search the topic where I got it.

Edit: I found, it was in this topic.

1 Like

I have no idea why I’m using try-catch here, it must have been my own overeager addition at the time I copied the code to my projects :slight_smile:

In case f is nil (not checked before ?)

I ´m not front of my computer but usually, in all those function (reveals in Finder, check if package, get extension, etc. ) in don’t check in the function but I have to do it before. Like the function included in Xogo like get the path, get the parent, get the name , etc. )

Sometimes, people forget elementary stuff…

Like I do with a project for a Windows declare code (5 lines) who worked fine in Xojo 2015, but returns complains in far later Xojo.

Now I use #If TargetXXX even if this is a one OS runner around OS specific code…

Aren’t you asking for a macOS solution? Thomas’ is for Windows. At least you asked this in the mac sub forum, right?

Also, why not keep using the AppleScript? you know that you can run applescripts directly from Xojo, right?

I too used that code but sometime ago I replaced it with a shell:

Public Sub RevealInFinder(extends f as FolderItem)
//# Reveal the FolderItem in the Finder (or third party replacement)

//or a shell, accepted by MAS

Dim sh As New shell
sh.Execute "open -R "+chrb(34)+f.NativePath+chr(34)

'#if TargetMacOS
'Declare Function objc_getClass Lib “Cocoa” ( name As CString ) As ptr
'Declare Function sharedWorkspace Lib “AppKit” selector “sharedWorkspace” ( obj As ptr ) As ptr
'Declare Function selectFile Lib “AppKit” selector “selectFile:inFileViewerRootedAtPath:” ( obj As ptr, fPath As CFStringRef, rootFullPath As CFStringRef ) As Boolean
'Dim workspace As ptr = sharedWorkspace( objc_getClass( “NSWorkspace” ) )

'Call selectFile( workspace, f.Nativepath, “”)
'#else
'#pragma unused f
'#endif
End Sub

Yes, I’m using macOS.

Yes, I thank @ AppleScript, but I forgot how to use it, then search in the (new) documentation found nothing, then seach in docs (the previous one) and found it, search back in the new documentation and found the same page and stopped there (after adding an Issue, a premiere for all the past 5 ? years).

'm tired of spending hours researching documentation with interesting results.

I may go back there one day; I just show the folder (it’s much better than searching in Finder).

Thanks.

PS: I am not a searcher; I only want to develop applications (solutions)…

I have the Shell version but I don’t use it. And I copy from here (the Xojo forum) the method for the 3 plateform (I don’t use Linux). Here is the code which is in my Module I gave the link above:

If CetElt.Parent = Nil Then
  If CetElt.DossPasPack_f Then ' C'est forcément un Dossier si pas de parent mais je teste quand même
    CetElt.Open ' Comme c'est un dossier (un volume en fait) ça l'ouvre dans le Finder
  Else
    #If DebugBuild Then
      MessageBox "Bizarre Tom, RevealEltFinder, '" + CetElt.AbsPath_f + "' n'a pas de parent (volume ?) et est un Package ???!!!"
    #EndIf
  End If
Else
  #If TargetMacOS Then
    #If True Then
      Declare Function objc_getClass Lib "libobjc.dylib" ( name as CString ) as ptr
      Declare Function sharedWorkspace Lib "AppKit" selector "sharedWorkspace" ( obj As ptr ) as ptr
      Declare Function selectFile Lib "AppKit" selector "selectFile:inFileViewerRootedAtPath:" ( obj as ptr, fPath as CFStringRef, rootFullPath as CFStringRef ) as Boolean
      Dim workspace as ptr = sharedWorkspace( objc_getClass( "NSWorkspace" ) )
      ' assert ( workspace <> nil, CurrentMethodName + " is Nil")
      Call selectFile( workspace, CetElt.NativePath, "")
      
    #ElseIf False Then ' Autre méthode via Cde Shell :
      Dim CdeShell as New Shell
      
      CdeShell.Execute "open -R " + CetElt.ShellPath
      ' TampNbre = CdeShell.ErrorCode
      ' TampText = CdeShell.Result
      CdeShell.Close
      
    #Else ' Ancienne méthode via AppleScript :
      CetElt.Parent.Launch ' Launch le dossier parent donc l'ouvre dans le Finder, et ne place que cette fenêtre au 1er plan, pas toutes celles du Finder comme
      Activate d'AppleScript. Mais le Reveal d'AppleScript ci-dessous ne fait qu'ouvrir la fenêtre du dossier, ça ne la place pas au 1er plan
      RevealThisItem(CetElt.NativePath) ' .ShellPath_fAS) ' Mieux que ci-dessus, en plus d'ouvrir la fenêtre du dossier, ça sélectionne l'élément dans la fenêtre, n'existe pas dans RealBasicEn
    #EndIf
    
  #ElseIf TargetWindows Then
    #If True Then
      Dim CdeShell as New Shell ' https://winaero.com/file-explorer-command-line-arguments-in-windows-10/
      
      ' J'ai rajouté "/root,/separate," : CdeShell.execute("explorer.exe /select, " + CetElt.shellpath) ' Les espaces sont déjà escapés, inutile de mettre  Encodings.UTF8.Chr(34)
      ' Inutile, voir commentaire ci-dessus : CdeShell.execute("explorer.exe /select, " + Encodings.UTF8.Chr(34) + CetElt.shellpath + Encodings.UTF8.Chr(34))
      CdeShell.execute("explorer.exe /root,/separate,/select, " + CetElt.shellpath) ' Ajouter  "/root,"  pour afficher le path depuis /root , et ajouter  "/separate,"  pour ouvrir une nouvelle fenêtre
      ' TampNbre = CdeShell.ErrorCode
      ' TampText = CdeShell.Result
      CdeShell.Close
      
    #Else ' Merde parfois
      ' ***** This is a cheap way of doing things, which has its problems *****
      '  1. Can only select one file
      '  2. If folder is already opened, it doesn't select the file
      Declare Function ShellExecuteW lib "shell32" (hwnd as Integer, lpOperation as WString, lpFile as WString, lpParameters as WString, lpDirectory as Integer, nShowCmnd as Integer) as Integer
      
      Dim TpErrNo as Integer
      Dim TpParam As String
      
      TpParam = "/select, " + Encodings.UTF8.Chr(34) + CetElt.NativePath + Encodings.UTF8.Chr(34) ' CetElt.AbsolutePath
      
      TpErrNo = ShellExecuteW(App.WindowAt(0).Handle.Integer, "Open", "explorer", TpParam, 0, 1)
    #EndIf
    
  #ElseIf TargetLinux Then
    ' Use xdg-open which is desktop-independent, and should be available in most newer distros
    ' Otherwise fallback on the two major desktop dependent command tools
    Dim CdeShell As New Shell
    Dim CommandTools() As String = Array( "xdg-open", "gnome-open", "kde-open" )
    
    For Each TpTool As String In CommandTools
      CdeShell.Execute("which " + TpTool)
      If CdeShell.Result.Left(1) = "/" Then
        CdeShell.Execute(TpTool + " " + CetElt.Parent.ShellPath)
        Exit
      End If
    Next
    
  #Else ' ???
    CetElt.Parent.Open ' Launch le dossier parent donc l'ouvre dans le Finder
    MessageBox "Error ! Please contact the authors, RevealEltFinder, '" + CetElt.AbsPath_f + "' Which Target ???!!!"
    
  #EndIf
End If
1 Like

I forgot but I have to put the code of a function I call (this function return true if the Folderitem is a folder but not a package):

Public Function DossPasPack_f(Extends CetElt as FolderItem) As Boolean
  
  Dim TampDrap as Boolean ' Retourne Vrai si  CetElt  est un directory mais pas un Package
  
  If CetElt.IsFolder Then
    TampDrap = (not CetElt.IsPackage_f) ' Si pas TargetMacOS ça retourne (not False) donc True
  Else
    TampDrap = False
  End If
  
  ' Je pourrais faire : TampDrap = CetElt.IsFolder and (not CetElt.IsPackage_f)  car si 1er test à False alors on ne fait pas le 2ième mais je n'aime pas
  
  Return TampDrap
  
End Function

And this function call the function I copy from here (the Xojo forum) which return True if the FolderItem (which must be a folder) is a package :

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