Page 1 of 1

PB_Gadget_SetOpenFinderFiles() is broken

Posted: Sat Aug 12, 2023 8:04 pm
by Wolfram
If I try my programs on macOS 11 (Big Sur) the PB_Gadget_SetOpenFinderFiles() function works not as on older OS versions.
Normally I can start my program by double click on a file or drop a file on the program and the programs loads the file after it is started.
On macOS 11 and later the program starts, but dose't load the file.
If the program is already started and I double click on a file it is loaded.

Re: PB_Gadget_SetOpenFinderFiles() is broken

Posted: Sun Aug 13, 2023 1:29 pm
by mk-soft
I had a look at this in the Purebasic IDE, where it also works.
There are some rules to be observed and entries still have to be made in the info.plist.

Example:

Code: Select all

;-TOP

#ProgramTitle = "Main Window - Drop Files"
#ProgramVersion = "v1.01.2"

ImportC ""
  PB_Gadget_SetOpenFinderFiles(*Callback)
EndImport

Enumeration Windows
  #Main
EndEnumeration

Enumeration MenuBar
  #MainMenu
EndEnumeration

Enumeration MenuItems
  #MainMenuAbout
  #MainMenuExit
EndEnumeration

Enumeration Gadgets
  #MainList
EndEnumeration

Enumeration StatusBar
  #MainStatusBar
EndEnumeration

; ----

Enumeration CustomEvent #PB_Event_FirstCustomValue
  #MyEvent_OpenFiles
EndEnumeration
  
Global NewList OpenFilesCommandLine.s()

ProcedureC OpenDocument(*FilesUTF8)
  Protected Files$, File$, k
  ; Files are separated by a tab
  ;
  Files$ = PeekS(*FilesUTF8, -1, #PB_UTF8)
  
  Repeat
    File$ = StringField(Files$, k+1, #TAB$) ; index is 1 based
    If File$ <> ""
      If FileSize(File$) >= 0 ; no directories here...
        Debug "passed file: "+File$
        
        ; Use the queue, as loading a document from this callback crash the IDE (probably because we do GUI operations in startup callback
        ; It will be loaded in the main loop
        LastElement(OpenFilesCommandLine())
        AddElement(OpenFilesCommandLine())  ; will cause this file to be loaded when the IDE is ready for it
        OpenFilesCommandLine() = File$
      EndIf
      k+1
    EndIf
  Until File$ = ""
  PostEvent(#MyEvent_OpenFiles)
  
EndProcedure

; ----

Procedure GetCommandLineFiles()
  Protected cnt, index, file.s
  cnt = CountProgramParameters()
  For index = 0 To cnt - 1
    file = ProgramParameter(index)
    If file <> ""
      AddElement(OpenFilesCommandLine())
      OpenFilesCommandLine() = file
    EndIf
  Next
EndProcedure

; ----

Procedure UpdateWindow()
  Protected dx, dy
  dx = WindowWidth(#Main)
  dy = WindowHeight(#Main) - StatusBarHeight(#MainStatusBar) - MenuHeight()
  ; Resize gadgets
  ResizeGadget(#MainList, 0, 0, dx, dy)
EndProcedure

Procedure Main()
  Protected dx, dy
  
  ; Set before first OpenWindow
  PB_Gadget_SetOpenFinderFiles(@OpenDocument())
    
  #MainStyle = #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget
  
  If OpenWindow(#Main, #PB_Ignore, #PB_Ignore, 800, 600, #ProgramTitle , #MainStyle)
    ; Menu
    CreateMenu(#MainMenu, WindowID(#Main))
    MenuTitle("&File")
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      MenuItem(#PB_Menu_About, "")
    CompilerElse
      MenuItem(#MainMenuAbout, "About")
    CompilerEndIf
    ; Menu File Items
    
    CompilerIf Not #PB_Compiler_OS = #PB_OS_MacOS
      MenuBar()
      MenuItem(#MainMenuExit, "E&xit")
    CompilerEndIf
    
    ; StatusBar
    CreateStatusBar(#MainStatusBar, WindowID(#Main))
    AddStatusBarField(#PB_Ignore)
    
    ; Gadgets
    dx = WindowWidth(#Main)
    dy = WindowHeight(#Main) - StatusBarHeight(#MainStatusBar) - MenuHeight()
    ListViewGadget(#MainList, 0, 0, dx, dy)
    
    ; Bind Events
    BindEvent(#PB_Event_SizeWindow, @UpdateWindow(), #Main)
    
    ;GetCommandLineFiles()
    
    ForEach OpenFilesCommandLine()
      AddGadgetItem(#MainList, -1, OpenFilesCommandLine())
    Next
    ClearList(OpenFilesCommandLine())
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Select EventWindow()
            Case #Main
              Break
              
          EndSelect
          
        Case #PB_Event_Menu
          Select EventMenu()
            CompilerIf #PB_Compiler_OS = #PB_OS_MacOS   
              Case #PB_Menu_About
                PostEvent(#PB_Event_Menu, #Main, #MainMenuAbout)
                
              Case #PB_Menu_Preferences
                
              Case #PB_Menu_Quit
                PostEvent(#PB_Event_CloseWindow, #Main, #Null)
                
            CompilerEndIf
              
            Case #MainMenuExit
              PostEvent(#PB_Event_CloseWindow, #Main, #Null)
              
            Case #MainMenuAbout
              MessageRequester("About", #ProgramTitle + #LF$ + #ProgramVersion, #PB_MessageRequester_Info)
              
          EndSelect
          
          
        Case #PB_Event_Gadget
          Select EventGadget()
            Case #MainList
              Select EventType()
                Case #PB_EventType_Change
                  ;
              EndSelect
              
          EndSelect
          
        Case #MyEvent_OpenFiles
          ForEach OpenFilesCommandLine()
            AddGadgetItem(#MainList, -1, OpenFilesCommandLine())
          Next
          ClearList(OpenFilesCommandLine())
          
      EndSelect
    ForEver
    
  EndIf
  
EndProcedure : Main()

info.plist

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDocumentTypes</key>
  <array>
    <dict>
      <key>CFBundleTypeExtensions</key>
      <array>
        <string>pb</string>
        <string>pbi</string>
        <string>pbp</string>
      </array>
      <key>CFBundleTypeName</key>
      <string>MyDropFiles</string>
      <key>CFBundleTypeIconFile</key>
      <string>FileIcon.icns</string>
      <key>CFBundleTypeRole</key>
      <string>Editor</string>
    </dict>
  </array>
  <key>CFBundleDevelopmentRegion</key>
  <string>English</string>
  <key>CFBundleExecutable</key>
  <string>DropFiles</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>CFBundleSignature</key>
  <string>PURE</string>
  <key>CFBundleVersion</key>
  <string>0.1</string>
  <key>CSResourcesFileMapped</key>
  <true/>
  <key>NSHighResolutionCapable</key>
  <true/>
</dict>
</plist>

Re: PB_Gadget_SetOpenFinderFiles() is broken

Posted: Sun Aug 13, 2023 10:33 pm
by WilliamL
mk-soft,

I really appreciate you're looking into this. It is very interesting to see the IDE example.

I've given some of my experience in this thread viewtopic.php?t=79365

The code you posted is essentially the same as Fred's original code. The ProcedureC OpenDocument(*FilesUTF8) callback is not triggered when the file is double clicked in my programs. If the program is running, dragging the file triggers the callback.

I've looked at your examples (both code and plist) and what I am using and I can't find any significant differences. The code worked for years.. now it doesn't so I don''t think it is a case of doing it wrong.

Your code obviously works in the IDE. It's a mystery to me.

Re: PB_Gadget_SetOpenFinderFiles() is broken

Posted: Mon Aug 14, 2023 9:56 am
by Piero
This works (based on mk-soft help)

Code: Select all

ImportC ""
   PB_Gadget_SetOpenFinderFiles(*Callback)
EndImport

Enumeration CustomEvent #PB_Event_FirstCustomValue
   #MyEvent_OpenFiles
EndEnumeration

Global NewList OpenFilesCommandLine.s()

ProcedureC OpenDocument(*FilesUTF8)
   Protected Files$, File$, k
   ; Files are separated by a tab
   Files$ = PeekS(*FilesUTF8, -1, #PB_UTF8)
   
   Repeat
      File$ = StringField(Files$, k+1, #TAB$) ; index is 1 based
      If File$ <> ""
         If FileSize(File$) >= 0 ; no directories here...
            LastElement(OpenFilesCommandLine())
            AddElement(OpenFilesCommandLine()) 
            OpenFilesCommandLine() = File$
         EndIf
         k+1
      EndIf
   Until File$ = ""
   PostEvent(#MyEvent_OpenFiles)
   
EndProcedure

; Set before first OpenWindow
PB_Gadget_SetOpenFinderFiles(@OpenDocument())

; Example:

If OpenWindow(0, 0, 0, 820, 620, "OpenFinderFiles", #PB_Window_SystemMenu | #PB_Window_ScreenCentered )
   
   EditorGadget(0, 10, 10, 800, 600)
   
   Repeat
      Select WaitWindowEvent()
         Case #MyEvent_OpenFiles
            ForEach OpenFilesCommandLine()
               AddGadgetItem(0, -1, OpenFilesCommandLine())
            Next
            ClearList(OpenFilesCommandLine()) ; Remember to clear the queue
         Case #PB_Event_CloseWindow
            end
      EndSelect
   Until EventMenu() = #PB_Menu_Quit
   
EndIf

Re: PB_Gadget_SetOpenFinderFiles() is broken

Posted: Mon Aug 14, 2023 5:11 pm
by mk-soft
But it won't work without adjusting the info.plist.

Edit and add this to info.plist inside <dict>

Code: Select all

<key>CFBundleDocumentTypes</key>
  <array>
    <dict>
      <key>CFBundleTypeExtensions</key>
      <array>
        <string>pb</string>
        <string>pbi</string>
        <string>pbp</string>
      </array>
      <key>CFBundleTypeName</key>
      <string>FinderFiles</string>
      <key>CFBundleTypeIconFile</key>
      <string>FileIcon.icns</string>
      <key>CFBundleTypeRole</key>
      <string>Editor</string>
    </dict>
  </array>

Re: PB_Gadget_SetOpenFinderFiles() is broken

Posted: Mon Aug 14, 2023 9:28 pm
by WilliamL
Ok, I've spent a few hours on this...

I put Fred's original code before any Procedures but it still didn't work.

It appears that the call is made before my procedures to load the menus and preferences. I tried all ways of getting these procedures done before the OpenFiles call but with marginal success. But, in the end, I gave up.

I modified my program using a PostEvent(). These were some of the lines I added and they are from Piero's and mk-soft examples. (see their examples)

Code: Select all

Enumeration CustomEvent #PB_Event_FirstCustomValue
  #MyEvent_OpenFiles
EndEnumeration

PostEvent(#MyEvent_OpenFiles)

Case #MyEvent_OpenFiles
	LoadFile(filename$)
[These are not complete instructions of how to add the info.]

I would guess that the PostEvent method is the way to go and that is what I will use from now on.

Re: PB_Gadget_SetOpenFinderFiles() is broken

Posted: Mon Aug 14, 2023 9:55 pm
by Piero
WilliamL wrote: Mon Aug 14, 2023 9:28 pm I would guess
It seems to me that the problem was solved with this:

Code: Select all

        ; Use the queue, as loading a document from this callback crash the IDE
        ; (probably because we do GUI operations in startup callback)
        ; It will be loaded in the main loop
        LastElement(OpenFilesCommandLine())
        AddElement(OpenFilesCommandLine())  ; will cause this file to be loaded when the IDE is ready for it
        OpenFilesCommandLine() = File$

Re: PB_Gadget_SetOpenFinderFiles() is broken

Posted: Mon Aug 14, 2023 10:55 pm
by WilliamL
mk-soft & Piero,

We agree that the loading should be done in the event loop. :wink:

Thank you. I wouldn't have been able to fix it without your help.

PS Using a NewList and disposing of it was perfect for this use.

Re: PB_Gadget_SetOpenFinderFiles() is broken

Posted: Wed Aug 16, 2023 4:25 am
by Piero
WilliamL wrote: Mon Aug 14, 2023 10:55 pmThank you. I wouldn't have been able to fix it out without your help.
You are very welcome!
Thanks mk-soft! I will need openfinderfiles soon... :wink:
PS:
Let's all remember that you can change the app that will open your file via Get Info in Finder ("Change all" to set the default app that will open that kind of files)

Re: PB_Gadget_SetOpenFinderFiles() is broken

Posted: Tue Sep 05, 2023 11:15 pm
by WilliamL
The code to load files double-clicked works great! What I can't figure out is if I just run the app how do I (for instance) go to a load routine? Since I have to go through the event loop once to see the #MyEvent_OpenFiles event I can't just go to the Load routine when I first run the program.

Re: PB_Gadget_SetOpenFinderFiles() is broken

Posted: Fri Sep 08, 2023 8:45 pm
by Piero
WilliamL wrote: Tue Sep 05, 2023 11:15 pm The code to load files double-clicked works great! What I can't figure out is if I just run the app how do I (for instance) go to a load routine? Since I have to go through the event loop once to see the #MyEvent_OpenFiles event I can't just go to the Load routine when I first run the program.
Huh? Do you mean load previously-opened-but-never-closed files like on the PB IDE? (mk-soft's IDE code can help you)

Otherwise, mk-soft will probably suggest you to open threads and clean the pool