Page 1 of 1

PathRequester to find a specific file

Posted: Fri Mar 29, 2013 7:29 pm
by SniffTheGlove
Hello,
Looking for a Windows Only implementation...

I am looking for a way to replicate how windows shows a path selector (just like PB PathRequester) however the OK button is disabled until a path is chosen that includes a specific file.

In Windows this happens when you are asked to select the location of an ini file during installation of some drivers.

EG I want the user to locate a specific file say myfile.ini

If I use PB OpenFileRequester then I can specify a file however the verification check only happens once a user clicks the OPEN button and as happens the file does not have to be there for the OPEN to process, so you have to add code into the cancellation to recall the OpenFileRequestor and so on.

I much prefer PathRequester but only for the OK to be enabled as soon ans a path is clicked on and that file resides in that path or if there is a way to disable the OPEN button in OpenFileRequester until a path is chosen that includes the specified file.

I hope you understand me.

Thanks

Re: PathRequester to find a specific file

Posted: Fri Mar 29, 2013 7:44 pm
by IdeasVacuum
...At the end of the day, it's just a form, you can code it yourself in 5 minutes.

Re: PathRequester to find a specific file

Posted: Sat Mar 30, 2013 4:32 am
by pwd
Slightly modified version of this snippet.

Code: Select all

Procedure BrowseCallbackProc(hwnd, msg, lParam, lData)
  szDir$ = Space(#MAX_PATH)
  Select msg
    Case #BFFM_INITIALIZED
      SendMessage_(hwnd, #BFFM_SETSELECTION, #BFFM_INITIALIZED, lData)
      SendMessage_(hwnd, #BFFM_ENABLEOK, 0, 0)

    Case #BFFM_SELCHANGED
      If SHGetPathFromIDList_(lParam, @szDir$)
        SendMessage_(hwnd, #BFFM_SETSTATUSTEXT, 0, @szDir$)
        If szDir$ = "PATH\TO\YOUR\file.ini"
          SendMessage_(hwnd, #BFFM_ENABLEOK, 0, 1)
        Else
          SendMessage_(hwnd, #BFFM_ENABLEOK, 0, 0)
        EndIf        
      EndIf
  EndSelect
EndProcedure

Procedure.s BrowseForFolder(Style, Titel.s, Path.s)
  Folder.s = Space(#MAX_PATH)
  Dir.BROWSEINFO
  Dir\hwndOwner = GetActiveWindow_()
  Dir\pszDisplayName = @Folder
  Dir\lpszTitle = @Titel
  Dir\ulFlags = Style
  Dir\lpfn = @BrowseCallbackProc()
  Dir\lParam = @Path
  result.l = SHBrowseForFolder_(@Dir)
  SHGetPathFromIDList_(result, @Folder)
  If Folder <> ""
    If FileSize(Folder) = - 2
      If Right(Folder, 1) <> "\" : Folder + "\" : EndIf
    EndIf
  EndIf
  CoTaskMemFree_(result)
  ProcedureReturn Folder
EndProcedure

Style = #BIF_STATUSTEXT | #BIF_BROWSEINCLUDEFILES
Debug BrowseForFolder(Style, "Please choose your path", "")

Re: PathRequester to find a specific file

Posted: Sat Mar 30, 2013 1:11 pm
by SniffTheGlove
Thanks PWD, that got me to a good starting point. I made a few changes to the code and it now does what I want.

I only have one issue if anybody knows how to correct it I will be grateful.

From the following code I have had to set a global variable so I can search for that file when a folder is selected. So is there a way to send the variable FileName.s to the BrowseCallbackProc procedure with out using a global variable from the BrowseForFolder procedure.

If you run this code then the file win.ini is located in the Windows folder...

Code: Select all

Global FileName.s = "win.ini"

Procedure BrowseCallbackProc(hwnd, msg, lParam, lData)
  szDir$ = Space(#MAX_PATH)
  Select msg
    Case #BFFM_INITIALIZED
      SendMessage_(hwnd, #BFFM_SETSELECTION, #BFFM_INITIALIZED, lData)
      SendMessage_(hwnd, #BFFM_ENABLEOK, 0, 0)
      
    Case #BFFM_SELCHANGED
      If SHGetPathFromIDList_(lParam, @szDir$)
        If ExamineDirectory(0, szDir$, "*." + GetExtensionPart(FileName.s))
          While NextDirectoryEntry(0)
            If LCase(DirectoryEntryName(0)) = LCase(Filename.s)
              SendMessage_(hwnd, #BFFM_ENABLEOK, 0, 1)
            Else
              SendMessage_(hwnd, #BFFM_ENABLEOK, 0, 0)
            EndIf       
          Wend
          FinishDirectory(0)
        EndIf
      EndIf
  EndSelect
EndProcedure

Procedure.s BrowseForFolder(Style, Titel.s, Path.s, FileName.s)
  Folder.s = Space(#MAX_PATH)
  Dir.BROWSEINFO
  Dir\hwndOwner = GetActiveWindow_()
  Dir\pszDisplayName = @Folder
  Dir\lpszTitle = @Titel
  Dir\ulFlags = Style
  Dir\lParam = @Path
  Dir\lpfn = @BrowseCallbackProc()
  result.l = SHBrowseForFolder_(@Dir)
  SHGetPathFromIDList_(result, @Folder)
  If Folder <> ""
    If FileSize(Folder) = - 2
      If Right(Folder, 1) <> "\" : Folder + "\" : EndIf
    EndIf
  EndIf
  CoTaskMemFree_(result)
  ProcedureReturn Folder
EndProcedure

Style = 0
FullPathAndFileName.s = BrowseForFolder(Style, "Please choose your path", "C:\",FileName.s)
Debug FullPathAndFileName.s + FileName.s
.

Re: PathRequester to find a specific file

Posted: Sat Mar 30, 2013 6:44 pm
by luis
Since BROWSEINFO\lParam it's a custom parameter, probably you could just merge all the vars you want in path$ inside BrowseForFolder().
In the callback, split the string back into its components.
Or use a structure defined in BrowseForFolder() and store its address inside Dir\lParam.
Since you don't leave the procedure the struct should be accessible from the callback.


EDIT: Using your code, something like this:

Code: Select all

; Global FileName.s = "win.ini"

Structure T_BROWSE_DATA
 Path$
 FileName$
EndStructure

Procedure BrowseCallbackProc(hwnd, msg, lParam, *lData.T_BROWSE_DATA)
  szDir$ = Space(#MAX_PATH)
  
  FileName$ = *lData\FileName$
  Path$ = *lData\Path$
  
  Select msg
    Case #BFFM_INITIALIZED
      SendMessage_(hwnd, #BFFM_SETSELECTION, #BFFM_INITIALIZED, Path$)
      SendMessage_(hwnd, #BFFM_ENABLEOK, 0, 0)      
     
    Case #BFFM_SELCHANGED
      SendMessage_(hwnd, #BFFM_ENABLEOK, 0, 0) 
      
      If SHGetPathFromIDList_(lParam, @szDir$)
        If ExamineDirectory(0, szDir$, "*." + GetExtensionPart(FileName$))
          While NextDirectoryEntry(0)            
            If LCase(DirectoryEntryName(0)) = LCase(FileName$)
              SendMessage_(hwnd, #BFFM_ENABLEOK, 0, 1) 
            EndIf       
          Wend
          FinishDirectory(0)
        EndIf
      EndIf
  EndSelect
EndProcedure

Procedure.s BrowseForFolder(Style, Titel.s, Path.s, FileName.s)
  Protected MyData.T_BROWSE_DATA  
  
  MyData\Path$ = Path
  MyData\FileName$ = FileName
  
  Folder.s = Space(#MAX_PATH)
  Dir.BROWSEINFO
  Dir\hwndOwner = GetActiveWindow_()
  Dir\pszDisplayName = @Folder
  Dir\lpszTitle = @Titel
  Dir\ulFlags = Style
  Dir\lParam = @MyData ; -> this end up in *lData
  Dir\lpfn = @BrowseCallbackProc()
  result.l = SHBrowseForFolder_(@Dir)
  SHGetPathFromIDList_(result, @Folder)
  If Folder <> ""
    If FileSize(Folder) = - 2
      If Right(Folder, 1) <> "\" : Folder + "\" : EndIf
    EndIf
  EndIf
  CoTaskMemFree_(result)
  ProcedureReturn Folder
EndProcedure

Style = 0
FileName.s = "filename.ini"
FullPathAndFileName.s = BrowseForFolder(Style, "Please choose your path", "C:\", FileName)
Debug FullPathAndFileName + FileName

Re: PathRequester to find a specific file

Posted: Sun Mar 31, 2013 12:37 pm
by SniffTheGlove
Thanks Luis.

I went with the structure you posted. Works great