Recent File Menu Handler

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
toxicBunny
Super wx Problem Solver
Super wx Problem Solver
Posts: 424
Joined: Tue Jul 12, 2005 8:44 pm
Location: Alabama, USA

Recent File Menu Handler

Post by toxicBunny » Tue Aug 09, 2005 4:27 pm

After a seeing a recent post about handling menu items for recently used files, I decided to post a class I've created to handle my recent files.

wxRecentFiles.h

Code: Select all

#ifndef _WXRECENTFILES_H_
#define _WXRECENTFILES_H_

// For compilers that support precompilation, includes "wx/wx.h".
#ifdef WX_PRECOMP
	#include "wx/wxprec.h"
#else
    #include "wx/wx.h"
#endif

#if defined(__GNUG__) && !defined(__APPLE__)
#pragma interface "wxRecentFiles.cpp"
#endif

////@begin includes
////@end includes

#define ID_FILE_RECENT	10300

class wxRecentFiles
{
public:
	wxRecentFiles(wxString ApplicationName, wxMenu* recentItemsMenu);
	~wxRecentFiles(void);

	void Add(wxString filePathAndName);
	wxString GetPath(int index);

private:
	wxString		m_appName;
	int		m_numFiles;
	wxArrayString	m_files;
	wxMenu*		m_menu;
	int		m_firstItem;

private:
	void LoadConfig();
	void StoreConfig();
	void SetBitmap(wxMenuItem* item, int i);
};

#endif // _WXRECENTFILES_H_
wxRecentFiles.cpp

Code: Select all

#if defined(__GNUG__) && !defined(__APPLE__)
#pragma implementation "wxRecentFiles.h"
#endif

#ifdef __BORLANDC__
#pragma hdrstop
#endif

////@begin includes
#include "wxRecentFiles.h"
#include <wx/config.h>
#include <wx/filename.h>
////@end includes

////@begin image includes
#include "images/one.xpm"
#include "images/two.xpm"
#include "images/three.xpm"
#include "images/four.xpm"
#include "images/five.xpm"
#include "images/six.xpm"
#include "images/seven.xpm"
#include "images/eight.xpm"
#include "images/nine.xpm"
#include "images/ten.xpm"
////@end image includes

wxRecentFiles::wxRecentFiles(wxString ApplicationName, wxMenu* recentItemsMenu)
{
	m_appName = ApplicationName;
	m_menu = recentItemsMenu;
	m_firstItem = (int)m_menu->GetMenuItemCount();
	LoadConfig();
}

wxRecentFiles::~wxRecentFiles(void)
{
	StoreConfig();
}

void wxRecentFiles::LoadConfig()
{
	wxConfig *config = new wxConfig(m_appName);
	wxString str;

	config->Read("NumRecent", &m_numFiles, 10);

	for (int i=1; i<=m_numFiles; i++)
	{
		if (config->Read(wxString::Format("Recent%i", i), &str))
		{
			m_files.Add(str);

			if (i == 1) m_menu->AppendSeparator();

			wxFileName name(str);
			wxMenuItem *item = new wxMenuItem(m_menu, ID_FILE_RECENT + i, name.GetFullName(), wxString::Format("Open %s", str));
			SetBitmap(item, i);
			m_menu->Append(item);
		}
	}

	delete config;
}

void wxRecentFiles::StoreConfig()
{
	wxConfig *config = new wxConfig(m_appName);

	config->Write("NumRecent", m_numFiles);

	for (int i=1; i<=(int)m_files.GetCount(); i++)
		config->Write(wxString::Format("Recent%i", i), m_files[i-1]);

	delete config;
}

void wxRecentFiles::Add(wxString filePathAndName)
{
	// add separator if necessary
	if ((int)m_files.GetCount() < 1)
		m_menu->InsertSeparator(m_firstItem);

	// remove all recent items from menu
	for (int i=1; i<=(int)m_files.GetCount(); i++)
		delete( m_menu->Remove(ID_FILE_RECENT + i) );

	// look for item in list
	for (int i=1; i<=(int)m_files.GetCount(); i++)
	{
		if (filePathAndName.CompareTo(m_files[i-1], wxString::caseCompare::ignoreCase) == 0)
		{
			// move item to top of list
			m_files.RemoveAt(i-1);
			m_files.Insert(filePathAndName, 0);

			break;
		}
	}

	// remove extra items in list
	if (i > (int)m_files.GetCount())
	{
		while ((int)m_files.GetCount() >= m_numFiles)
			m_files.RemoveAt(m_files.GetCount() - 1);

		m_files.Insert(filePathAndName, 0);
	}

	// add items to menu
	for (int i=1; i<=(int)m_files.GetCount(); i++)
	{
		wxFileName name(m_files[i-1]);
		wxMenuItem *item = new wxMenuItem(m_menu, ID_FILE_RECENT + i, name.GetFullName(), wxString::Format("Open %s", name.GetFullPath()));
		SetBitmap(item, i);
		m_menu->Insert(m_firstItem + i, item);
	}
}

wxString wxRecentFiles::GetPath(int index)
{
	return m_files[index - ID_FILE_RECENT - 1];
}

void wxRecentFiles::SetBitmap(wxMenuItem* item, int i)
{
	switch( i )
	{
	case 1:
		item->SetBitmap(wxBitmap((const char**)one_xpm));
		break;
	case 2:
		item->SetBitmap(wxBitmap((const char**)two_xpm));
		break;
	case 3:
		item->SetBitmap(wxBitmap((const char**)three_xpm));
		break;
	case 4:
		item->SetBitmap(wxBitmap((const char**)four_xpm));
		break;
	case 5:
		item->SetBitmap(wxBitmap((const char**)five_xpm));
		break;
	case 6:
		item->SetBitmap(wxBitmap((const char**)six_xpm));
		break;
	case 7:
		item->SetBitmap(wxBitmap((const char**)seven_xpm));
		break;
	case 8:
		item->SetBitmap(wxBitmap((const char**)eight_xpm));
		break;
	case 9:
		item->SetBitmap(wxBitmap((const char**)nine_xpm));
		break;
	case 10:
		item->SetBitmap(wxBitmap((const char**)ten_xpm));
		break;
	}
}
It handles creation of the menu items with unique IDs and uses XPMs for the numbers from 1 - 10 to show with the menu item. I've assumed a limit of ten recent items, but this can be modified. It uses the wxConfig classes to store and retrieve the recent items between sessions. I create it by passing it the application name and the wxMenu where I want it to appear:

Code: Select all

/// load the recent files
wxRecentFiles* recent = new wxRecentFiles("MyApp", menu_file);
This will add a menu separator and then the list of recently used files. I generally create this near the end of my main "File" menu and, after creating the recent files list, I usually add another menu separator and then the "Exit" menu item. When I open a file I call ::Add with the full path to the file:

Code: Select all

/// add recent file
recent->Add("PathToFile");
For the event handler, I use:

Code: Select all

EVT_MENU_RANGE( ID_FILE_RECENT, ID_FILE_RECENT+10, MyFrame::OnRecent )
When a recent item is clicked, I get the path to the file in the ::OnRecent event handler:

Code: Select all

wxString fileName = recent->GetPath(event.GetId());
Even after opening a file from the recent list, you should still call ::Add with the full path to the file because the class will re-order the items to put the most recently used file at the top of the list.

I hope this helps someone. Feel free to modify it as you like.

Scott Fant

vdell
Moderator
Moderator
Posts: 536
Joined: Fri Jan 07, 2005 3:44 pm
Location: Finland
Contact:

Post by vdell » Tue Aug 09, 2005 4:31 pm

There is also wxFileHistory.
Visual C++ 9.0 / Windows XP Pro SP3 / wxWidgets 2.9.0 (SVN) | Colligere

toxicBunny
Super wx Problem Solver
Super wx Problem Solver
Posts: 424
Joined: Tue Jul 12, 2005 8:44 pm
Location: Alabama, USA

Post by toxicBunny » Tue Aug 09, 2005 4:46 pm

vdell wrote:There is also wxFileHistory.
Very true... I guess I never noticed that before. I assume it handles re-ordering of the files if a file is added which already exists in the history, but it doesn't specifically say this. I guess mine is a little simpler since I never work with MDI applications and have no need to manage multiple recent file menus. I also wanted the ability to show a bitmap with the menu item indicating the number associated with it, e.g. "1" for the first item.

Anyway, thanks for pointing that out.

-Scott

Jorg
Moderator
Moderator
Posts: 3971
Joined: Fri Aug 27, 2004 9:38 pm
Location: Delft, Netherlands
Contact:

Post by Jorg » Tue Aug 09, 2005 4:57 pm

wxFileHistory needs to be renamed. It has a name that is just too generic. I always forget the name, and look for something called wxMRUList or wxRecentFilesMenu ..

- Jorgen
Forensic Software Engineer
Netherlands Forensic Insitute
http://english.forensischinstituut.nl/
-------------------------------------
Jorg's WasteBucket
http://www.xs4all.nl/~jorgb/wb

toxicBunny
Super wx Problem Solver
Super wx Problem Solver
Posts: 424
Joined: Tue Jul 12, 2005 8:44 pm
Location: Alabama, USA

Post by toxicBunny » Tue Aug 09, 2005 5:38 pm

Jorg wrote:wxFileHistory needs to be renamed. It has a name that is just too generic. I always forget the name, and look for something called wxMRUList or wxRecentFilesMenu ..

- Jorgen
That's probably one reason why I've never noticed it before. It sounds as though it's used to track changes to a given file on disk...

-Scott

NinjaNL
Moderator
Moderator
Posts: 899
Joined: Sun Oct 03, 2004 10:33 am
Location: Oosterwolde, Netherlands

Post by NinjaNL » Tue Aug 09, 2005 8:16 pm

vdell wrote:There is also wxFileHistory.
And if that wasn't enough there is also Otto Wyss' Guidlines which include a recent files implementation http://wxguide.sourceforge.net/guidelines/menus.html
Follow the development of my screenplay authoring program at http://wxscreenplaywriter.blogspot.com/

Jorg
Moderator
Moderator
Posts: 3971
Joined: Fri Aug 27, 2004 9:38 pm
Location: Delft, Netherlands
Contact:

Post by Jorg » Wed Aug 10, 2005 6:58 am

I posted this to the wx-users mailinglist;
Hi Guys,

This is simply a comment or suggestion. Many people seem to miss out the purpose of this class wxFileHistory and start implementing their own way of a most recent used file list. On the forum people even admitted they thought it was a class to keep track of versions of a file or something.

Could a small addition be made to the wxWidgets lib like this?

#define wxRecentFileMenu wxFileHistory

Simply link the help definition of wxRecentFileMenu to wxFileHistory, I think this would really help the visibility of the class. :-) Also maybe mentioning it in the wxMenu section might help.
Regards,
- Jorgen
Forensic Software Engineer
Netherlands Forensic Insitute
http://english.forensischinstituut.nl/
-------------------------------------
Jorg's WasteBucket
http://www.xs4all.nl/~jorgb/wb

Post Reply