RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN RIEN

Utilisateur anonyme - 27 avril 2007 à 23:20
 florenth - 1 mars 2008 à 18:35
De rien !
Mais par contre, fais gaffe à la valeur [loAutoLoad] qui peut faire penser que la liste est chargée dès que le composant est crée alors que, en réalité, elle n'est chargée qu'une fois les composants ... chargés ! (via Loaded).
Donc si comme dans le code de l'exemple, tu charges un fichier dans le OnCreate, il faut penser à faire charger manuellement la liste juste avant. (via LoadFileList)

++
cs_Jean-Pierre Messages postés 82 Date d'inscription jeudi 25 septembre 2003 Statut Membre Dernière intervention 20 avril 2010
1 mars 2008 à 18:28
Salut florenth,

Je pensais être encore bourré lol !

Merci pour cette rectif très rapide, cette fois après tests, je te donne 12/10, si si.

Purée j'avais pourtant cherché partout, mais toi, tu as tout de suite trouvé, bravo !

Bon dev'
Salut !

Tu n'as raté aucune marche... mais moi oui !
J'avais pourtant testé le truc mais dans des circonstances qui ont fait que la liste existante de fichier n'était pas détruite (car j'étais dans un répertoire du %PATH% de Windows)

J'ai donc rajouté le bout de code manquant dans la procédure LoadFileList et maintenant tout roule !

Merci d'avoir signalé ce bug et bonne prog' !
cs_Jean-Pierre Messages postés 82 Date d'inscription jeudi 25 septembre 2003 Statut Membre Dernière intervention 20 avril 2010
1 mars 2008 à 16:45
Bonjour,

Merci à florenth pour cet exemple impeccable et très instructif.

Mais veuillez pardonnez mon ignorance, je n'arrive pas à trouver pour quelle raison, le fichier de stockage des récents se trouve systématiquement effacé lorsque j'ouvre un fichier texte directement en cliquant sur un fichier texte.

Je veux dire : lorsque j'ouvre des fichiers texte via le "OpenDialog" de cette démo ça fonctionne nickel, mais pas en affectant aux fichiers texte cette ouverture via cette appli de démo... ("Ouvrir avec..." de Windows)

Je dois avoir loupé une marche, mais laquelle ?

S'il y a un petit malin dans la salle, je le remercie beaucoup d'avance pour éclairer ma lanterne.
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
2 mai 2007 à 19:44
pitite correction a apporter :


property MaxFileCount: Integer read FMaxFileCount write SetMaxFileCount default 12;
property Options: TRecentFileListOptions read FOptions write FOptions default [loAutoSave, loAutoLoad, loAutoCheck];


sinon, ça n'apparait pas en valeur par defaut dans l'editeur de propriétés !
Hou là, que de bonnes remarques : ^^

- Pour l'histoire du ParamStr(0), ce que tu dis est faux puisque regarde à quoi ressemble l'implémentation de TApplication.GetExeName (Forms.pas ligne 8003 chez moi):

function TApplication.GetExeName: string;
begin
Result := ParamStr(0);
end;

- Tu as parfaitement raison pour le "set of": ce sera pour la prochaine MAJ.

- J'ai ajouté une fonction de format des chaines que l'on ajoute au Mémo ou au MenuItem: maintenant, il est possible, tout comme on le fait avec Format() de choisir comment sera présentée la liste ! On peut donc faire des trucs assez sympa. Bien entendu, je n'ai pas enlevé la limite de largeur maximale.

Maintenant, la déclaration de FillMenu() fait trois lignes tellement il y a de paramètres (six en fait) !

Et voila: comment transformer un code assez banal en un truc cent fois mieux grâce à l'esprit ouvert des gens ici: merci à tous !
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
1 mai 2007 à 22:17
ah aussi :

# procedure TRecentFileList.DoChange;
# begin
# if Assigned(FOnListChange) and not (csDesigning in ComponentState) then
# FOnListChange(Self);
# end;
#
# procedure TRecentFileList.LoadFileList;
# begin
# if FileExists(FListFileName) then
# FFileList.LoadFromFile(ExtractFilePath(ParamStr(0)) + FListFileName);
# end;

attention, j'ai remarqué que si on enregistre le composant dans la palette, au moment de la conception ParamStr(0) renvois le dossier de Delphi et non du projet (puisqu'il n'est pas encore enregistrer!)

fait le test, tu vas voir c'est trés trés con comme erreur :)

donc la, application.exename ;)
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
1 mai 2007 à 22:15
en fait les inhibiteurs permettent d'appeler la procedure Change en excluant certain partie.
l'avantage, c'est qu'on evite de recopier du code redondant comme par exemple :

if moAutoSave in fOptions then SaveList ...

aprés chaque modification de la liste ....

l'inconvénient c'est effectivement que ça fait un casse tete dans le code :)

par contre je pense que tu peu repiquer l'idée du Set Of pour les options, pour ces propriétés :

FAutoLoad: Boolean;
FAutoSave: Boolean;
FAutoCheck: Boolean;

TRecentListOption = (loAutoLoad, loAutoSave, loAutoCheck);
TRecentListOptions = set of TRecentListOption;

TRecentFileList = class(TComponent)
private
fOptions : TRecentListOptions;
published
property Options : TRecentListOptions read fOptions write fOptions default [loAutoLoad, loAutoSave, loAutoCheck];
end;

procedure TRecentFileList.BeforeDestruction;
begin
inherited BeforeDestruction;
if loAutoSave in fOptions then
SaveFileList;
end;

procedure TRecentFileList.Loaded;
begin
inherited Loaded;
if loAutoLoad in fOptions then
LoadFileList;
end;

etc ...

ça evite de declarer 3 boolean et 3 property et au moins les options sont "rangées" dans l'editeur de propriété.
gros avantage aussi, c'est qu'on definit les options en une seule ligne de code :

MyRecentFileList.Options := [loAutoSave, loAutoLoad];

avantage aussi si on derive Assign ou AssignTo, on eviteras de recopier 3 parametres ...
autre avantage, si on ajoute des fonctionnalitées, on pourras les integrée plus facilement !

...Options = (loAutoSave, loAutoLoad, loAutoCheck, loExcractFileName, loShowIndex, loShowModDate ...);

tu trouve pas ?
Voila c'est fait !
Avec une petite nouvelle fonctionnalité en plus.
Régalez-vous ! ^^
@f0xi: en effet, avec tes inhibiteurs, tu t'es un peu compliqué la vie.
Néanmoins, quand je lis ton code, je remarque qu'il y a beaucoup de points communs.

Personnellement, j'ai préféré ne pas mettre de propriété "ParantMenu" pour justement laissez le choix au programmeur d'afficher cette liste là où il le souhaite. Ce qui soulage le code de l'unité et n'alourdit vraiment pas celui de celle qui l'utilise.

@ThWilliam:
Merci pour tes appréciations.
J'avais aussi remarqué ce bug et je pensais même l'avoir corrigé. Je devais être dans le brouillard ce jour là. Je fais une MAJ dès que possible.
ThWilliam Messages postés 418 Date d'inscription mardi 3 janvier 2006 Statut Membre Dernière intervention 26 novembre 2013 4
1 mai 2007 à 11:02
Salut Florent.

Beau code et très utile.
Juste un petit truc : quand la limite de MaxFileCount est atteinte (j'ai testé avec 4), les fichiers qu'on ouvre par après ne sont plus insérés dans la liste. Il faudrait, "pour faire de la place", supprimer le dernier élément de la liste.

A +
Thierry
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
1 mai 2007 à 02:30
bah le miens est un composant qu'on depose sur la fiche et auquel on associe un menuitem existant prevus pour cette effet.

dans le principe ça reste identique on vas dire, j'ai plus d'options, comme l'affichage des indexs de fichier (1, 2, 3, 4,...), affichage des noms de fichier seul (sans path) et mmm une approche un chouilla differente, notement pour la gestion des options avec un set of et un bordel innomable dans ma procedure Change qui m'a obligé de créer un ensemble d'inhibiteur pour empecher les appels recurssif (et donc les plantages du au debordement de pile).

mais, j'ai laisser de coté car y'a quelques bugs persistants ....

voila le source pour rigoler :

{ RecentFM.pas

Recent Files Menu

Haaaa my brain!!!!


}
unit RecentFM;

interface

uses Windows, SysUtils, Classes, Controls, Forms, Menus, Graphics, Dialogs;

type
MaxRecentFilesRange = 1..200;

TRecentFilesChangeInhibitor = (ihChange, ihSave, ihRemove, ihAdjust, ihRecreate);
TRecentFilesChangeInhibitors = set of TRecentFilesChangeInhibitor;

TRecentFilesOption = (moAutoSave, moAutoRemoveDeadFiles, moAutoAdjustListSize,
moOnlyFileName, moShowIndexs);

TRecentFilesOptions = set of TRecentFilesOption;
TRecentFilesClickEvent = procedure(Sender : Tobject; const AFileName : string;
const AFileExists : boolean) of object;

TRecentFilesMenu = class(TComponent)
private
fParentMenu : TMenuItem;

fOptions : TRecentFilesOptions;

fList : TStringList;
fListName : TFileName;
fInhibitors : TRecentFilesChangeInhibitors;
fMaxRecentFiles: MaxRecentFilesRange;

fOnRFIClick : TNotifyEvent;
fOnRFIDrawItem : TMenuDrawItemEvent;
fOnRFIAdvDrawItem : TAdvancedMenuDrawItemEvent;
fOnRFIMeasureItem : TMenuMeasureItemEvent;

fOnRFClick : TRecentFilesClickEvent;
fOnChange : TNotifyEvent;

procedure SetOptions(val : TRecentFilesOptions);
procedure SetMaxRecentFiles(val : MaxRecentFilesRange);

protected
{ herited }
procedure Notification(AComponent:TComponent; Operation:TOperation); override;
procedure Change; virtual;
procedure RecreateMenu; virtual;

procedure DoRFIClick(Sender : TObject);
procedure DoRFIDrawItem(Sender:TObject;ACanvas:TCanvas;ARect:TRect;
Selected:Boolean);
procedure DoRFIAdvDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect;
State: TOwnerDrawState);
procedure DoRFIMeasureItem(Sender: TObject; ACanvas: TCanvas; var Width,
Height: Integer);

public { before }
constructor Create(AOwner : TComponent); override;

procedure Update(const FileName : string); overload;
procedure Update(const Files : TStrings); overload;

procedure LoadList;
procedure SaveList;

procedure RemoveDeadFiles;
procedure AdjustListSize;
procedure ClearList;

published
property ParentMenu : TMenuItem
read fParentMenu
write fParentMenu;

property ListName : TFileName
read fListName
write fListName;

property MaxRecentFiles : MaxRecentFilesRange
read fMaxRecentFiles
write SetMaxRecentFiles
default 8;

property Options : TRecentFilesOptions
read fOptions
write SetOptions
default [moAutoSave, moAutoRemoveDeadFiles,
moAutoAdjustListSize, moOnlyFileName,
moShowIndexs];

property OnRecentFilesClick : TRecentFilesClickEvent
read fOnRFClick
write fOnRFClick;

property OnChange : TNotifyEvent
read fOnChange
write fOnChange;

property OnMenuClick : TNotifyEvent
read fOnRFIClick
write fOnRFIClick;

property OnMenuDrawItem : TMenuDrawItemEvent
read fOnRFIDrawItem
write fOnRFIDrawITem;

property OnMenuAdvancedDrawItem : TAdvancedMenuDrawItemEvent
read fOnRFIAdvDrawItem
write fOnRFIAdvDrawItem;

property OnMenuMeasureItem : TMenuMeasureItemEvent
read fOnRFIMeasureItem
write fOnRFIMeasureItem;

public { after }
destructor Destroy; override;
procedure BeforeDestruction; override;

end;

implementation

{ TRecentFilesMenu }

procedure TRecentFilesMenu.AdjustListSize;
var N : integer;
begin
if fList.Count <= fMaxRecentFiles then
exit;

fList.BeginUpdate;
for N := fList.Count-1 downto fMaxRecentFiles do
fList.Delete(N);
fList.EndUpdate;

if not (ihChange in fInhibitors) then
begin
fInhibitors := fInhibitors + [ihAdjust];
Change;
end;
end;

procedure TRecentFilesMenu.BeforeDestruction;
begin
inherited BeforeDestruction;
fInhibitors := [ihChange, ihRecreate];
SaveList;
end;

procedure TRecentFilesMenu.Change;
begin
if ihChange in fInhibitors then
exit;

fInhibitors := fInhibitors + [ihChange];

if (moAutoRemoveDeadFiles in fOptions) and not (ihRemove in fInhibitors) then
RemoveDeadFiles;

if (moAutoAdjustListSize in fOptions) and not (ihAdjust in fInhibitors) then
AdjustListSize;

if (moAutoSave in fOptions) and not (ihSave in fInhibitors) then
SaveList;

if Assigned(fParentMenu) and not (ihRecreate in fInhibitors) then
RecreateMenu;

fInhibitors := [];
end;

procedure TRecentFilesMenu.ClearList;
begin
fList.Clear;
fInhibitors := [ihRemove, ihAdjust];
Change;
end;

constructor TRecentFilesMenu.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fInhibitors := [];

fMaxRecentFiles := 8;

fList := TStringList.Create;

fListName := ExtractFilePath(Application.ExeName)+'recents.dat';

fOptions := [moAutoSave, moAutoRemoveDeadFiles, moAutoAdjustListSize,
moOnlyFileName, moShowIndexs];
end;

destructor TRecentFilesMenu.Destroy;
begin
fList.Free;
inherited Destroy;
end;

procedure TRecentFilesMenu.DoRFIAdvDrawItem(Sender: TObject;
ACanvas: TCanvas; ARect: TRect; State: TOwnerDrawState);
begin
if Assigned(fOnRFIAdvDrawItem) then
fOnRFIAdvDrawItem(Sender, ACanvas, ARect, State);
end;

procedure TRecentFilesMenu.DoRFIClick(Sender: TObject);
var CMI : TMenuItem;
FI : integer;
begin
CMI := (Sender as TMenuItem);
FI := CMI.Tag;

CMI.Enabled := FileExists(fList[FI]);

if FI <> 0 then
begin
fList.Move(FI,0);

if moAutoSave in fOptions then
begin
fInhibitors := fInhibitors + [ihRemove, ihAdjust, ihRecreate];
SaveList;
end;

RecreateMenu;
end;

if Assigned(fOnRFClick) then
fOnRFClick(Sender, fList[0], CMI.Enabled);
end;

procedure TRecentFilesMenu.DoRFIDrawItem(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; Selected: Boolean);
begin
if Assigned(fOnRFIDrawItem) then
fOnRFIDrawItem(Sender, ACanvas, ARect, Selected);
end;

procedure TRecentFilesMenu.DoRFIMeasureItem(Sender: TObject;
ACanvas: TCanvas; var Width, Height: Integer);
begin
if Assigned(fOnRFIMeasureItem) then
fOnRFIMeasureItem(Sender, ACanvas, Width, Height);
end;

procedure TRecentFilesMenu.LoadList;
begin
if not FileExists(fListName) then
exit;

fList.BeginUpdate;
fList.LoadFromFile(fListName);
fList.EndUpdate;

fInhibitors := fInhibitors + [ihSave];
Change;
end;

procedure TRecentFilesMenu.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent <> nil) then
if AComponent = fParentMenu then
fParentMenu := nil;
end;

procedure TRecentFilesMenu.RecreateMenu;
var N,MX : integer;
NMI : TMenuItem;
begin
if not Assigned(fParentMenu) then
exit;

fParentMenu.Clear;

if fList.Count > fMaxRecentFiles then
MX := fMaxRecentFiles
else
MX := fList.Count;

for N := 0 to MX-1 do
begin
NMI := TMenuItem.Create(fParentMenu);

if moOnlyFileName in fOptions then
NMI.Caption := ExtractFileName(fList[N])
else
NMI.Caption := fList[N];

if moShowIndexs in fOptions then
NMI.Caption := IntToStr(N+1)+'. '+NMI.Caption;

NMI.Enabled := FileExists(fList[N]);

NMI.Tag := N;
NMI.OnClick := DoRFIClick;
NMI.OnDrawItem := DoRFIDrawItem;
NMI.OnAdvancedDrawItem := DoRFIAdvDrawItem;
NMI.OnMeasureItem := DoRFIMeasureItem;

fParentMenu.Add(NMI);
end;

fParentMenu.Enabled := fParentMenu.Count <> 0;
end;

procedure TRecentFilesMenu.RemoveDeadFiles;
var N : integer;
begin
fList.BeginUpdate;
for N := fList.Count-1 downto 0 do
if not FileExists(fList[N]) then
fList.Delete(N);
fList.EndUpdate;

if not (ihChange in fInhibitors) then
begin
fInhibitors := fInhibitors + [ihRemove];
Change;
end;
end;

procedure TRecentFilesMenu.SaveList;
begin
fList.SaveToFile(fListName);
end;

procedure TRecentFilesMenu.SetMaxRecentFiles(val: MaxRecentFilesRange);
begin
if fMaxRecentFiles <> val then
begin
fMaxRecentFiles := val;
if not (ihChange in fInhibitors) then
Change;
end;
end;

procedure TRecentFilesMenu.SetOptions(val: TRecentFilesOptions);
begin
if fOptions <> Val then
begin
fOptions := Val;
if not (ihChange in fInhibitors) then
Change;
end;
end;

procedure TRecentFilesMenu.Update(const FileName: string);
var I : integer;
begin
if (moAutoRemoveDeadFiles in fOptions) and not FileExists(FileName) then
exit;

fList.BeginUpdate;

if fList.Count = 0 then
fList.Add(FileName)
else
begin
I := fList.IndexOf(FileName);
if I = -1 then
fList.Insert(0,FileName)
else
fList.Move(I,0);
end;

fList.EndUpdate;

fInhibitors := [];
Change;
end;


procedure TRecentFilesMenu.Update(const Files: TStrings);
var N,I : integer;
begin
fList.BeginUpdate;
try
for N := 0 to Files.Count-1 do
begin
if fList.Count = 0 then
fList.Add(Files[N])
else
begin
I := fList.IndexOf(Files[N]);
if I = -1 then
fList.Insert(0,Files[N])
else
fList.Move(I,0);
end;
end;
finally
fList.EndUpdate;
end;

fInhibitors := [];
Change;
end;

end.


repique des idées si t'as envie.
Ah ben comme quoi !
Il avait des choses en plus le tien ou pas ?

Sinon, correction eauR-thau-grah-fiQ :
"Les grandS esprits se rencontreNT, j'avais MOI AUSSI commencÉ un tel composant ... :,(

lol je sais, sur un clavier, on fait toujours plus de fautes... d'où l'intérêt d'avoir un compilateur strict au niveau du langage pour éviter les *bugs du C++* ^^
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
30 avril 2007 à 02:07
Les grand esprits se rencontre, j'avais commencer aussi un tel composant ... :,(
Merci Japee pour tes appréciations.

Mais tu sais, j'essaie au maximum de poster quelque chose d'original justement pour éviter l'effet de *blaséification*...
De toutes façons, c'est bien de but du site, non ?
japee Messages postés 1727 Date d'inscription vendredi 27 décembre 2002 Statut Modérateur Dernière intervention 6 novembre 2021 8
28 avril 2007 à 13:34
Salut Florenth,

Comme tu nous y a habitués*, voilà un code intéressant, rigoureux, agréable à parcourir (présentation irréprochable) et à analyser, et qui nous enrichit (intellectuellement, lol).

* attention, j'ai pas dit qu'on était blasés, hein ?
Merci Cirec.
c'est vrai que, même si ce code compile sans problème sous D6 (à condition d'ignorer les erreurs de chargement du dfm), j'aurais du penser aux autres et faire une source 100% compatible.

C'est désormais chose faite.
J'ai ajouté Directives.inc car elle contient une liste de directives de compilation qui sont bien pratiques lors de l'échange de codes (pour éviter par exemple, les désagréments que j'ai eu avec la source d'effet d'eau ^^).

Donc voila, maintenant, tout devrait être OK.
Utilisateur anonyme
27 avril 2007 à 23:39
Salut,

comme toujours du très beau travail
et en plus très utile ... à conserver sous le coude ;)

2 petites choses tout de même :

-1. "Directives.inc" non fournis (mais il semblerait qu'il ne soit pas nécessaire)
-2. Tu utilises l'alignement avec marges (AlignWithMargins)
ce qui pose problème avec les versions antérieurs à Turbo Delphi

@+
Cirec
Rejoignez-nous