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:
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
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.
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.
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
RecentFile(i).Tag = file
RecentFile(i).Value = file.NativePath
If i=3 Then Exit
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
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.