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.