Mirror

How to load a menu from a file (Views: 103)


Problem/Question/Abstract:

How do I load or recreate a menu stored in a text file? I'm looking for a recursive function.

Answer:

The following seems to work if the data is always organized the way you gave (depth-first recursion).

Level|Name|Caption|
0|miItem1|Item 1|
1|miItem11|Sub Item 1-1|
1|miItem12|Sub Item 1-2|
2|miItem121|Sub sub  Item 1-2-1|
0|miItem2|Item 2|
1|miItem21|Sub Item 2-1|
1|miItem22|Sub Item 2-2|

I found it easier to use a stack instead of recursion, however:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls, Menus;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    MainMenu1: TMainMenu;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure MenuClick(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses contnrs;

{$R *.DFM}

function IScan(ch: Char; const S: string; fromPos: Integer): Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := fromPos to Length(S) do
  begin
    if S[i] = ch then
    begin
      Result := i;
      Break;
    end;
  end;
end;

procedure SplitString(const S: string; separator: Char; substrings: TStringList);
var
  i, n: Integer;
begin
  if Assigned(substrings) and (Length(S) > 0) then
  begin
    i := 1;
    repeat
      n := IScan(separator, S, i);
      if n = 0 then
        n := Length(S) + 1;
      substrings.Add(Copy(S, i, n - i));
      i := n + 1;
    until
      i > Length(S);
  end;
end;

procedure LoadMenuFromText(aMenu: TMenu; text: TStrings; aHandler: TNotifyEvent);
type
  TMenuData = record
    level: Integer;
    name: string;
    caption: string
  end;

  procedure SplitLine(const line: string; var data: TMenuData);
  var
    sl: TStringlist;
  begin
    sl := TStringlist.Create;
    try
      SplitString(line, '|', sl);
      Assert(sl.count >= 3);
      data.level := StrToInt(sl[0]);
      data.name := sl[1];
      data.caption := sl[2];
    finally
      sl.free
    end;
  end;

var
  itemStack: TStack;
  level: Integer;
  i: Integer;
  menudata: TMenuData;
  newitem: TMenuItem;
begin
  level := 0;
  itemstack := TStack.Create;
  try
    itemstack.Push(aMenu.Items);
    {skip header line}
    for i := 1 to text.count - 1 do
    begin
      SplitLine(text[i], menudata);
      newitem := Menus.NewItem(menudata.caption, 0, false, true, aHandler, 0,
        menudata.name);
      while level > menudata.level do
      begin
        itemstack.Pop;
        Dec(level);
      end;
      TMenuItem(itemstack.Peek).Add(newitem);
      Itemstack.Push(newitem);
      Inc(level)
    end;
  finally
    itemstack.free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  LoadMenuFromText(mainmenu1, memo1.lines, MenuClick);
end;

procedure TForm1.MenuClick(Sender: TObject);
begin
  label1.caption := (Sender as TMenuItem).Name;
end;

end.

<< Back to main page