SaveFileRequester, multi-OS

Share your advanced PureBasic knowledge/code with the community.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

SaveFileRequester, multi-OS

Post by Keya »

On Mac the native API that PB's SaveFileRequester() calls handles the file existence + overwrite confirmation checks for us, but on Windows and Linux the programmer handles that (better i think as more flexible - we can add for example a Cancel button whereas Mac only has Yes/No).

Code: Select all

Procedure SaveToFile(*buf, buflen)
  DefFile$ = "Untitled.txt"
  Pattern$ = "All files (*.*)|*.*": Pattern = 0
  Repeat
    
    File$ = SaveFileRequester("Save File As...", DefFile$, Pattern$, Pattern)
    If File$  
      
      CompilerIf #PB_Compiler_OS <> #PB_OS_MacOS
        Select FileSize(File$)
          Case -1: ;Not found
          Case -2: MessageRequester("Error", "Name already exists as a directory"): Continue ;re-ask
          Default:
            Select MessageRequester("File Exists","Overwrite?", #PB_MessageRequester_YesNoCancel)
              Case #PB_MessageRequester_No: Continue ;re-ask
              Case #PB_MessageRequester_Cancel: Break
            EndSelect
        EndSelect
      CompilerEndIf
      
      ;// At this stage it's safe to create and/or overwrite the file
      hFile = CreateFile(#PB_Any, File$)
      If hFile
        If *buf: WriteData(hFile, *buf, buflen): EndIf
        CloseFile(hFile)
        MessageRequester("Saved", "Saved to "+ File$)
      Else
        MessageRequester("Error", "Couldn't create file")
        Continue ;re-ask
      EndIf
    EndIf
    Break
    
  ForEver    
EndProcedure


*buf=AllocateMemory(1)
SaveToFile(*buf,1) ;write 1 byte
There is probably no need for the "Case -2:" line (detect if name is a Directory) as I don't think any of the SaveFileRequester API's let you select a directory anyway



;--------------------

Another version, this one simply returns a filename if it's ok to create and/or overwrite, so basically just the SaveFileRequester on its own but with non-Mac handler:

Code: Select all

Procedure.s SaveFilename(DefFile$, Pattern$, PatIndex)
  Repeat
    
    File$ = SaveFileRequester("Save File As...", DefFile$, Pattern$, PatIndex)
    If File$  
      
      CompilerIf #PB_Compiler_OS <> #PB_OS_MacOS
        Select FileSize(File$)
          Case -1: ;Not found
          Case -2: MessageRequester("Error", "Name already exists as a directory"): Continue ;re-ask
          Default:
            Select MessageRequester("File Exists","Overwrite?", #PB_MessageRequester_YesNoCancel)
              Case #PB_MessageRequester_No: Continue ;re-ask
              Case #PB_MessageRequester_Cancel: Break
            EndSelect
        EndSelect
      CompilerEndIf
      ProcedureReturn File$
      
    EndIf
    Break    
  ForEver    
EndProcedure



TryAgain:
File$ = SaveFilename("Untitled.txt", "All files (*.*)|*.*", 0)
If File$ 
  ;// At this stage it's safe to create and/or overwrite the file
  hFile = CreateFile(#PB_Any, File$)
  If hFile
    If *buf: WriteData(hFile, *buf, buflen): EndIf
    CloseFile(hFile)
    MessageRequester("Saved", "Saved to "+ File$)
  Else
    MessageRequester("Error", "Couldn't create file")
    Goto TryAgain
  EndIf  
EndIf

But I prefer this version, which also takes care of checking if the file can actually be created, returning both the filename and handle:

Code: Select all

Procedure.i SaveFilename(DefFile$, Pattern$, PatIndex, *pname)
  Repeat
    
    File$ = SaveFileRequester("Save File As...", DefFile$, Pattern$, PatIndex)
    If File$  
      
      CompilerIf #PB_Compiler_OS <> #PB_OS_MacOS
        Select FileSize(File$)
          Case -1: ;Not found
          Case -2: MessageRequester("Error", "Name already exists as a directory"): Continue ;re-ask
          Default:
            Select MessageRequester("File Exists","Overwrite?", #PB_MessageRequester_YesNoCancel)
              Case #PB_MessageRequester_No: Continue ;re-ask
              Case #PB_MessageRequester_Cancel: Break
            EndSelect
        EndSelect
      CompilerEndIf
      
      hFile = CreateFile(#PB_Any, File$)
      If Not hFile: Continue: EndIf
      *Outname.String = @*pname
      *Outname\s = File$
      ProcedureReturn hFile
   
    EndIf
    Break    
  ForEver    
EndProcedure


Define Fname$=Space(8192) ;must prealloc
hFile = SaveFilename("Untitled.txt", "All files (*.*)|*.*", 0, @Fname$)
If hFile
  ;WriteData(hFile...)
  CloseFile(hFile)
  MessageRequester("Saved","Saved to " + Fname$)  
EndIf 
Last edited by Keya on Fri Mar 10, 2017 6:38 pm, edited 4 times in total.
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: SaveFileRequester, multi-OS

Post by nco2k »

so does windows, with proper flags. i requested that almost 8 years ago: http://purebasic.fr/english/viewtopic.php?f=3&t=40424

c ya,
nco2k
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: SaveFileRequester, multi-OS

Post by Keya »

i wonder if the Mac api also supports some flags - the possibility to disable its overwrite check so i can do my own to match Windows/Linux would be good
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: SaveFileRequester, multi-OS

Post by nco2k »

no idea, i havent used a mac in a very very long time. :)

>> the possibility to disable its overwrite check so i can do my own to match Windows/Linux would be good
honestly, it would be much better the other way around.

your method would be good if the os didnt provide such functionalities, but since it does, we should always let the os handle such things. instead of trying to workaround this issue, it would be much better if purebasic added this as the default behavior (like pretty much every other app does) or simply add optional #PB_FileRequester_* flags. closing one requester to open another, is just weird and unnecessary. with the proper #OFN_PATHMUSTEXIST | #OFN_FILEMUSTEXIST / #OFN_OVERWRITEPROMPT flags, the os does all the necessary checks and requests, without ever closing the Open-/SaveFileRequester(). and again, im not criticizing your code as such, but the fact that we have to either workaround this issue or write our own file requester.

i dont know about linux, but i would be surprised if it didnt provide such functionalities. so yea, i still hope this will be added one day. :)

c ya,
nco2k
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
Post Reply