{
Article: Most Recently Used (MRU) menu component - TMRU
http://delphi.about.com/library/weekly/aa112503a.htm
Full source code of a TMRU component, a non-visual component
which simplifies implementing a "Most Recently Used" file list
in a menu (or a popup menu). The TMRU component allows for
quick selection of a file that was recently accessed (opened)
in an application.
MRU - Most Recently Used
Many applications offer a list of most recently used files
(like Delphi's File | Reopen menu) - a list that reflects the user's most
recently accessed files in an application.
The MRU list allows for quick selection of a file that was recently opened
without having to select an Open menu item and search to locate a specific
lately accessed file.
The TMRU Delphi component is a non-visual component which simplifies
implementing a "Most Recently Used" file list in a menu (or a popup menu).
Here are the TadpMRU component's features:
MRU menu items are attached to a "ParentMenuItem"
Dynamic, supports *unlimited* number of MRU items.
A maximum number of MRU entries can be defined.
Files can be added or removed from the list.
The files are listed with the most recently used at the top of the list
(from the "most recent" to "least recent.")
Each file can be displayed using the full path name or just the file name.
The MRU list is saved to the Registry upon application termination and
loaded upon application startup.
Enables more Registry storage areas by providing the RegistryPath property
Exposes an OnClick event with the Filename as a parameter.
and more...
http://www.angusj.com/delphi/mruunit.html for <IniFile>
Copyright 2003 Angus Johnson
}
unit uMRU;
interface
uses
Windows, Messages, SysUtils, Classes, Menus, IniFiles, Registry;
const
MRU_MAX_ITEMS = 16;
MRU_NAME = 'MRU';
type
// riggered when a "MRU menu item" is clicked.
TMRUClickEvent = procedure( Sender : TObject; const FileName : String )
of object;
TMRU = class( TComponent )
private
FItems : TStringList;
FMaxItems : cardinal;
FShowFullPath : boolean;
FRegistryPath : string;
FIniFileName : string;
FParentMenuItem : TMenuItem;
FOnClick : TMRUClickEvent;
procedure SetMaxItems( const Value : cardinal );
procedure SetShowFullPath( const Value : boolean );
procedure SetParentMenuItem( const Value : TMenuItem );
procedure SetIniFileName( const Value : string );
procedure SetRegistryPath( const Value : string );
procedure LoadMRU;
procedure SaveMRU;
procedure LoadFromRegistry;
procedure SaveToRegistry;
procedure LoadFromIniFile;
procedure SaveToIniFile;
procedure ItemsChange( Sender : TObject );
procedure ClearParentMenu;
protected
procedure Loaded; override;
procedure Notification( AComponent : TComponent;
Operation : TOperation ); override;
procedure DoClick( Sender : TObject );
public
constructor Create( AOwner : TComponent ); override;
destructor Destroy; override;
// Adds the Filename to the MRU list, as the first item.
procedure AddItem( const FileName : string );
// Removes a MRU item provided with a file name.
// Returns true if an existing item was removed; false otherwise.
function RemoveItem( const FileName : string ) : boolean;
published
// Gets or sets the string value representing the key in Registry
// where MRU entries are saved.
// If omitted the MRU list is NOT saved upon application termination.
// The root key used is HKEY_CURRENT_USER
property RegistryPath : string read FRegistryPath write SetRegistryPath;
// Gets or sets the string value representing the Ini FileName
// where MRU entries are saved.
// If omitted the MRU list is NOT saved upon application termination.
property IniFileName : string read FIniFileName write SetIniFileName;
// Gets or sets the maximum number of files in the MRU list.
property MaxItems : cardinal read FMaxItems write SetMaxItems
default MRU_MAX_ITEMS;
// Gets or sets the value that indicates whether files as menu items
// are displayed with full file name (or just using the file name).
property ShowFullPath : boolean read FShowFullPath write SetShowFullPath
default True;
// Determines the menu item that the MRU items will be
// added to as child menu items.
property ParentMenuItem : TMenuItem read FParentMenuItem
write SetParentMenuItem;
// riggered when a "MRU menu item" is clicked.
property OnClick : TMRUClickEvent read FOnClick write FOnClick;
end;
procedure Register;
implementation
type
// to be able to recognize MRU menu item when deleting
TMRUMenuItem = class( TMenuItem );
procedure Register;
begin
RegisterComponents( 'delphi.about.com', [ TMRU ] );
end;
{ TMRU }
constructor TMRU.Create( AOwner : TComponent );
begin
inherited;
FParentMenuItem := nil;
FItems := TStringList.Create;
FItems.OnChange := ItemsChange;
FMaxItems := MRU_MAX_ITEMS;
FShowFullPath := True;
end; (* Create *)
destructor TMRU.Destroy;
begin
if not( csDesigning in ComponentState ) then
SaveMRU;
FItems.OnChange := nil;
FItems.Free;
inherited;
end; (* Destroy *)
procedure TMRU.Loaded;
begin
inherited Loaded;
if not( csDesigning in ComponentState ) then
LoadMRU;
end; (* Loaded *)
procedure TMRU.Notification( AComponent : TComponent; Operation : TOperation );
begin
inherited;
if ( Operation = opRemove ) and ( AComponent = FParentMenuItem ) then
FParentMenuItem := nil;
end; (* Notification *)
procedure TMRU.AddItem( const FileName : string );
begin
if FileName <> '' then
begin
FItems.BeginUpdate;
try
if FItems.IndexOf( FileName ) > -1 then
FItems.Delete( FItems.IndexOf( FileName ) );
FItems.Insert( 0, FileName );
while FItems.Count > MaxItems do
FItems.Delete( MaxItems );
finally
FItems.EndUpdate;
end;
end;
end; (* AddItem *)
function TMRU.RemoveItem( const FileName : string ) : boolean;
begin
if FItems.IndexOf( FileName ) > -1 then
begin
FItems.Delete( FItems.IndexOf( FileName ) );
Result := True;
end
else
Result := False;
end; (* RemoveItem *)
procedure TMRU.SetMaxItems( const Value : cardinal );
begin
if Value <> FMaxItems then
begin
if Value < 1 then
FMaxItems := 1
else if Value > MaxInt then
FMaxItems := MaxInt - 1
else
begin
FMaxItems := Value;
FItems.BeginUpdate;
try
while FItems.Count > MaxItems do
FItems.Delete( FItems.Count - 1 );
finally
FItems.EndUpdate;
end;
end;
end;
end; (* SetMaxItems *)
procedure TMRU.SetIniFileName( const Value : string );
begin
if FIniFileName <> Value then
begin
FIniFileName := Value;
LoadMRU;
end;
end;
procedure TMRU.SetRegistryPath( const Value : string );
begin
if FRegistryPath <> Value then
begin
FRegistryPath := Value;
LoadMRU;
end;
end; (* SetRegistryPath *)
procedure TMRU.SetShowFullPath( const Value : boolean );
begin
if FShowFullPath <> Value then
begin
FShowFullPath := Value;
ItemsChange( Self );
end;
end; (* SetShowFullPath *)
procedure TMRU.LoadFromRegistry;
var
i : cardinal;
begin
with TRegistry.Create do
try
RootKey := HKEY_CURRENT_USER;
if OpenKey( FRegistryPath, False ) then
begin
FItems.BeginUpdate;
FItems.Clear;
try
for i := 1 to FMaxItems do
if ValueExists( MRU_NAME + IntToStr( i ) ) then
FItems.Add( ReadString( MRU_NAME + IntToStr( i ) ) );
finally
FItems.EndUpdate;
end;
CloseKey;
end;
finally
Free;
end;
end; (* LoadFromRegistry *)
procedure TMRU.SaveToRegistry;
var
i : integer;
begin
with TRegistry.Create do
try
RootKey := HKEY_CURRENT_USER;
if OpenKey( FRegistryPath, True ) then
begin
// delete old mru
i := 1;
while ValueExists( MRU_NAME + IntToStr( i ) ) do
begin
DeleteValue( MRU_NAME + IntToStr( i ) );
Inc( i );
end;
// write new mru
for i := 0 to -1 + FItems.Count do
WriteString( MRU_NAME + IntToStr( i + 1 ), FItems[ i ] );
CloseKey;
end;
finally
Free;
end;
end;
procedure TMRU.LoadFromIniFile;
var
i : cardinal;
LItemName : string;
IniFilePath : string;
IniFileName : string;
begin
IniFilePath := ExtractFilePath( FIniFileName );
if IniFilePath <> '' then
IniFileName := FIniFileName // Valid IniFile with path
else if FIniFileName = '' then
IniFileName := ChangeFileExt( paramStr( 0 ), '.ini' ) // default IniFile
else // FIniFileName <> ''
IniFileName := ExtractFilePath( paramStr( 0 ) ) + FIniFileName;
if FileExists( IniFileName ) then
begin
with TIniFile.Create( IniFileName ) do
begin
try
FItems.BeginUpdate;
FItems.Clear;
try
for i := 1 to FMaxItems do
begin
LItemName := ReadString( MRU_NAME, IntToStr( i ), '' );
if LItemName = '' then
break;
FItems.Add( LItemName );
end;
finally
FItems.EndUpdate;
end;
finally
Free;
end;
end;
end;
end;
procedure TMRU.SaveToIniFile;
var
i : cardinal;
IniFilePath : string;
IniFileName : string;
begin
IniFilePath := ExtractFilePath( FIniFileName );
if IniFilePath <> '' then
IniFileName := FIniFileName // Valid IniFile with path
else if FIniFileName = '' then
IniFileName := ChangeFileExt( paramStr( 0 ), '.ini' ) // default IniFile
else // FIniFileName <> ''
IniFileName := ExtractFilePath( paramStr( 0 ) ) + FIniFileName;
with TIniFile.Create( IniFileName ) do
begin
try
for i := 1 to FMaxItems do
begin
if i <= FItems.Count then
WriteString( MRU_NAME, IntToStr( i ), FItems[ i - 1 ] );
end;
finally
Free;
end;
end;
end;
procedure TMRU.LoadMRU;
begin
if FRegistryPath <> '' then
LoadFromRegistry
else
LoadFromIniFile;
end;
procedure TMRU.SaveMRU;
begin
if FRegistryPath <> '' then
SaveToRegistry
else
SaveToIniFile;
end;
(* SaveMRU *)
procedure TMRU.ItemsChange( Sender : TObject );
var
i : integer;
NewMenuItem : TMenuItem;
FileName : String;
begin
if ParentMenuItem <> nil then
begin
ClearParentMenu;
for i := 0 to -1 + FItems.Count do
begin
if ShowFullPath then
FileName := StringReplace( FItems[ i ], '&', '&&',
[ rfReplaceAll, rfIgnoreCase ] )
else
FileName := StringReplace( ExtractFileName( FItems[ i ] ), '&', '&&',
[ rfReplaceAll, rfIgnoreCase ] );
NewMenuItem := TMRUMenuItem.Create( Self );
NewMenuItem.Caption := Format( '%s', [ FileName ] );
NewMenuItem.Tag := i;
NewMenuItem.OnClick := DoClick;
ParentMenuItem.Add( NewMenuItem );
end;
end;
end; (* ItemsChange *)
procedure TMRU.ClearParentMenu;
var
i : integer;
begin
if Assigned( ParentMenuItem ) then
for i := -1 + ParentMenuItem.Count downto 0 do
if ParentMenuItem.Items[ i ] is TMRUMenuItem then
ParentMenuItem.Delete( i );
end; (* ClearParentMenu *)
procedure TMRU.DoClick( Sender : TObject );
begin
if Assigned( FOnClick ) and ( Sender is TMRUMenuItem ) then
FOnClick( Self, FItems[ TMRUMenuItem( Sender ).Tag ] );
end; (* DoClick *)
procedure TMRU.SetParentMenuItem( const Value : TMenuItem );
begin
if FParentMenuItem <> Value then
begin
ClearParentMenu;
FParentMenuItem := Value;
ItemsChange( Self );
end;
end; (* SetParentMenuItem *)
end. (* MRU.pas *)
{
********************************************
Zarko Gajic
About.com Guide to Delphi Programming
http://delphi.about.com
email: delphi@aboutguide.com
free newsletter: http://delphi.about.com/library/blnewsletter.htm
forum: http://forums.about.com/ab-delphi/start/
********************************************
}