Open Recent Menu item

I am finally getting around to implementing an Open Recent submenu in my app and was delighted to find the example project “OpenRecentMenu” inside the example projects folder. While this project does show how to implement the menu while the program is running, it does not include how to save the list of recent files so that the menu can persist across quitting and restarting. I note that there is a SpecialFolder.RecentDocuments and I assume the list goes in there but have been unable to find any documentation about that folder, how to format the information for saving to that folder, nor can I even find the folder itself in ~/Library on my mac. While I can kludge something together, is there a way that this is supposed to be done? I compile the app for both Mac and Windows.

I found SpecialFolder.recentItems, but like you , cannot find any folder like that on my machines.
The RecentMenuItems example project is more an example of how to add menu items , than how to implement a ‘recent files’ system.
Sandboxing causes other complications.

The simplest method I can suggest is a combination of the OpenRecentMenu , plus serialisation of the data when the app closes.
The example adds menu items indiscrimiately… if you save 200 in a session, that menu is going to look busy very quickly.

So, consider this:

Most apps offer a ‘maximum recent items’ preference.
Let’s call that 10

Create an empty array of folderitems on startup.
Call InitialiseRecentItems() //see later
When you save a file, check the array…
if it has reached 10 items, remove the 10th
Insert your new file at position 0

When your app closes, create a file in Specialfolder.applicationdata.child(“YOURAPP”).child(“RECENTFILES.TXT”)
Loop through your array, and for each item, save into that text file the nativepath of each of the folderitems in turn.

Next time your app opens, use the InitialiseRecentItems method to set up the menu and array based on last use:

Sub InitialiseRecentItems() open the text file, and for each row in the list, TRY to get a folderitem based on the path If the file exists (it may have been deleted) , add to both the array and recent items menu end sub

CAVEAT: On Macs, files can be moved around and yet still be findable. (I think these are security-scoped bookmarks, but I dont know how to use those, and I hear bad things of them anyway)
The method above should work cross platform.

Thanks, Jeff for your detailed response. I was afraid it was something like that. and had considered as a solution pretty much what you have suggested (although less elegantly than you describe!). I find it interesting that

f = SpecialFolder.RecentItems

Does not return Nil but does indeed return a path of ~/Library/RecentDocuments. I have invisible files visible in the library and still don’t see that folder in Library. I gather from the reading I’ve done that maybe this location is a global plist somewhere??

Fortunately, my app is not bound for the app store so the worst aspects of sandboxing may be avoided so I will proceed along the lines that you’ve suggested which will probably be better cross platform anyway unless someone else chimes in with an alternative suggestion/explanation.

you must save your own recent files in the prefs file of your application.

What Jean-Yves said. SpecialFolder.RecentItems is a system-wide thing used by the OS and not really appropriate. I would think you’d want your app to display items recently used by it only. Pick a file format for your app’s preferences (I use plists, but JSON is popular, or you can even use simple text as Jeff mentions) and store a list of recent files there as Jeff describes.

Use FromSaveInfo/SaveInfo…

I stored the data inside a single .txt file.

The “hardest” part is add / remove, remove all and remove one Recent is easier.

Thom McGrath made a code to manage these recent menu items.
it is old code from realbasic or studio time, but it should open in current xojo.
https://www.cjoint.com/doc/16_06/FFCl0PgzJ1H_Open-Recent-Menu-13.05.18-original-.zip

my module “Recent” to add,save,load recent used files

Public Sub Load()
  List.RemoveAllRows
  
  Dim f As FolderItem = File
  
  If f = Nil Then Return
  If f.Exists = False Then Return
  
  Dim stream As TextInputStream = TextInputStream.Open(f) 
  
  While Not stream.EndOfFile 
    
    Dim path As String = stream.ReadLine(Encodings.UTF8)
    
    #Pragma BreakOnExceptions Off 
    Try
      Dim file As FolderItem = New FolderItem(path)
      List.AddRow(file)
    Catch er As RuntimeException
      System.DebugLog("error " + er.Message)
      System.DebugLog("can't use " + path)
    End Try
    
  Wend
  
  stream.Close
  
End Sub

Public Sub Save()
  
  Dim file As FolderItem = File
  
  Dim stream As TextOutputStream = TextOutputStream.Create(file) // Overwrite if exists
  
  For Each f As FolderItem In List
    stream.WriteLine(f.NativePath)
  Next
  
  stream.Close
  
End Sub

Public Sub Add(f As FolderItem)
  For i As Integer = List.FirstRowIndex To List.LastRowIndex
    If List(i).NativePath = f.NativePath Then 
      List.RemoveRowAt(i) 'remove
      Exit
    End If
  Next
  
  List.AddRowAt(0,f) 'add first
  
End Sub

Public Function File() as FolderItem
  
  Var path As FolderItem = SpecialFolder.ApplicationData.Child("YourApp")
  If path.Exists = False Then
    path.CreateFolder
  End If
  
  Return path.Child("YourApp.recent.txt")
  
End Function


Public Property List() as FolderItem

i used this to update the menu items and store the FolderItem into the menu tag

[code]Public Sub RecentFilesMenu()
Var i As Integer = 0
For Each file As FolderItem In Recent.List
i=i+1
RecentFile(i).Tag = file
RecentFile(i).Value = file.NativePath
If i=3 Then Exit
Next

End Sub
[/code]

in the save as menu it looks llike

Board1.SaveAs f
Recent.Add(f)
Recent.Save
RecentFilesMenu

at RecentFile Menu Handler you get the item back

Dim f As FolderItem = RecentFile(index).Tag

If f = Nil Then
  Call MessageDialog.Show("This Menu is still Empty ..") 
  Return True
End If

If f.Exists = False Then 
  Call MessageDialog.Show("The stored Name did not Exists ..") 
  Return True
End If

http://documentation.xojo.com/api/files/textinputstream.html#textinputstream-encoding

allows you to set the encoding for all reading, instead of what you used in the While loop:

Thanks to all for your very helpful suggestions. My Open Recent submenu is now successfully reimplemented across restarts. I’ll give Jeff the credit but you’ve all been great.