diff --git a/ideintf/menuintf.pas b/ideintf/menuintf.pas index 7f903ba0a1..025f5830e5 100644 --- a/ideintf/menuintf.pas +++ b/ideintf/menuintf.pas @@ -19,6 +19,11 @@ - implement creating Top and Bottom separator - Root items - OnPopup + - Create MainBar menu with the menu interface + - Create Source Editor Popupmenu with the menu interface + - Create CodeExplorer Popupmenu with the menu interface + - Create Project Inspector Popupmenu with the menu interface + - Create Messages Popupmenu with the menu interface } unit MenuIntf; @@ -27,7 +32,7 @@ unit MenuIntf; interface uses - Classes, SysUtils, Menus, ImgList, Graphics, IDECommands; + Classes, SysUtils, Menus, ImgList, Graphics, TextTools, IDECommands; type TAddMenuItemProc = @@ -36,6 +41,7 @@ type TIDEMenuSection = class; + { TIDEMenuItem A menu item in one of the IDE's menus. This is only the base class for TIDEMenuSection and TIDEMenuCommand } @@ -87,10 +93,11 @@ type end; TIDEMenuItemClass = class of TIDEMenuItem; + { TIDEMenuSection An TIDEMenuItem with childs, either in a sub menu or separated with separators. } - + TIDEMenuSection = class(TIDEMenuItem) private FBottomSeparator: TMenuItem; @@ -116,6 +123,10 @@ type function IndexOf(AnItem: TIDEMenuItem): Integer; function NeedTopSeparator: Boolean; function NeedBottomSeparator: Boolean; + function GetContainerMenuItem: TMenuItem; + function IndexByName(const AName: string): Integer; + function FindByName(const AName: string): TIDEMenuItem; + function CreateUniqueName(const AName: string): string; public property ChildsAsSubMenu: boolean read FChildsAsSubMenu write SetChildsAsSubMenu default true; @@ -127,11 +138,11 @@ type end; TIDEMenuSectionClass = class of TIDEMenuSection; + { TIDEMenuCommand A leaf menu item. No childs. - An IDE command can be assigned, which defines the shortcut. + Hint: The shortcut is defined via the Command property. } - TIDEMenuCommand = class(TIDEMenuItem) private FAutoCheck: boolean; @@ -165,26 +176,89 @@ type end; TIDEMenuCommandClass = class of TIDEMenuCommand; - { TIDEMenuRoots } + + { TIDEMenuRoots + These are the top level menu items of the IDE. } TIDEMenuRoots = class(TPersistent) private - FItems: TFPList; - function GetItems(Index: integer): TIDEMenuItem; + FItems: TFPList;// list of TIDEMenuSection + function GetItems(Index: integer): TIDEMenuSection; public constructor Create; destructor Destroy; override; - procedure RegisterMenuRoot(Section: TIDEMenuItem); - procedure UnregisterMenuRoot(Section: TIDEMenuItem); + procedure RegisterMenuRoot(Section: TIDEMenuSection); + procedure UnregisterMenuRoot(Section: TIDEMenuSection); function Count: Integer; procedure Clear; procedure Delete(Index: Integer); + function IndexByName(const Name: string): Integer; + function FindByName(const Name: string): TIDEMenuSection; + function CreateUniqueName(const Name: string): string; + function FindByPath(const Path: string; + ErrorOnNotFound: boolean): TIDEMenuItem; public - property Items[Index: integer]: TIDEMenuItem read GetItems; default; + property Items[Index: integer]: TIDEMenuSection read GetItems; default; end; +var + IDEMenuRoots: TIDEMenuRoots = nil;// created by the IDE + +function RegisterIDEMenuRoot(const Name: string; MenuItem: TMenuItem + ): TIDEMenuSection; +function RegisterIDEMenuSection(const Path, Name: string): TIDEMenuSection; +function RegisterIDESubMenu(const Path, Name, Caption: string; + const OnClick: TNotifyEvent): TIDEMenuSection; +function RegisterIDEMenuItem(const Path, Name, Caption: string; + const OnClick: TNotifyEvent; + const Command: TIDECommandKeys): TIDEMenuCommand; + implementation +function RegisterIDEMenuRoot(const Name: string; MenuItem: TMenuItem + ): TIDEMenuSection; +begin + Result:=TIDEMenuSection.Create(Name); + IDEMenuRoots.RegisterMenuRoot(Result); + Result.MenuItem:=MenuItem; +end; + +function RegisterIDEMenuSection(const Path, Name: string): TIDEMenuSection; +var + Parent: TIDEMenuSection; +begin + Parent:=IDEMenuRoots.FindByPath(Path,true) as TIDEMenuSection; + Result:=TIDEMenuSection.Create(Name); + Result.ChildsAsSubMenu:=false; + Parent.AddLast(Result); +end; + +function RegisterIDESubMenu(const Path, Name, Caption: string; + const OnClick: TNotifyEvent): TIDEMenuSection; +var + Parent: TIDEMenuSection; +begin + Parent:=IDEMenuRoots.FindByPath(Path,true) as TIDEMenuSection; + Result:=TIDEMenuSection.Create(Name); + Result.ChildsAsSubMenu:=true; + Result.Caption:=Caption; + Result.OnClick:=OnClick; + Parent.AddLast(Result); +end; + +function RegisterIDEMenuItem(const Path, Name, Caption: string; + const OnClick: TNotifyEvent; const Command: TIDECommandKeys): TIDEMenuCommand; +var + Parent: TIDEMenuSection; +begin + Parent:=IDEMenuRoots.FindByPath(Path,true) as TIDEMenuSection; + Result:=TIDEMenuCommand.Create(Name); + Result.Caption:=Caption; + Result.OnClick:=OnClick; + Result.Command:=Command; + Parent.AddLast(Result); +end; + { TIDEMenuItem } procedure TIDEMenuItem.SetEnabled(const AValue: Boolean); @@ -308,7 +382,10 @@ end; function TIDEMenuItem.Size: Integer; begin - Result:=1; + if Visible then + Result:=1 + else + Result:=0; end; { TIDEMenuSection } @@ -358,6 +435,7 @@ end; procedure TIDEMenuSection.Insert(Index: Integer; AnItem: TIDEMenuItem); begin + AnItem.fName:=CreateUniqueName(AnItem.Name); FItems.Insert(Index,AnItem); AnItem.FSection:=Self; CreateChildMenuItem(Index); @@ -368,21 +446,42 @@ var Item: TIDEMenuItem; SubSection: TIDEMenuSection; i: Integer; + ContainerMenuItem: TMenuItem; + MenuIndex: Integer; begin - if MenuItem=nil then exit; - + ContainerMenuItem:=GetContainerMenuItem; + if (ContainerMenuItem=nil) then exit; + MenuIndex:=GetChildsStartIndex+Index; + + if NeedTopSeparator then begin + if (TopSeparator=nil) then begin + // create TopSeparator + FTopSeparator:=MenuItemClass.Create(nil); + FTopSeparator.Caption:='-'; + MenuItem.Insert(GetChildsStartIndex,FTopSeparator); + end; + inc(MenuIndex); + end; Item:=Items[Index]; // create the child TMenuItem Item.CreateMenuItem; - MenuItem.Insert(Index+GetChildsStartIndex,Item.MenuItem); + MenuItem.Insert(MenuIndex,Item.MenuItem); // create the subsections if Item is TIDEMenuSection then begin SubSection:=TIDEMenuSection(Item); for i:=0 to SubSection.Count-1 do SubSection.CreateChildMenuItem(i); end; + + if (Index=Count-1) and NeedBottomSeparator and (BottomSeparator=nil) then + begin + // create bottom separator + FBottomSeparator:=MenuItemClass.Create(nil); + FBottomSeparator.Caption:='-'; + MenuItem.Insert(MenuIndex+1,FBottomSeparator); + end; end; function TIDEMenuSection.GetChildsStartIndex: Integer; @@ -396,30 +495,22 @@ begin inc(Result,Section[SiblingIndex].Size); inc(SiblingIndex); end; + if not Section.ChildsAsSubMenu then + inc(Result,Section.GetChildsStartIndex); end; function TIDEMenuSection.Size: Integer; -var - SelfIndex: LongInt; - NextSibling: TIDEMenuItem; begin - Result:=1; - if (Section<>nil) and (not ChildsAsSubMenu) then begin - // childs are not in a submenu but directly added to parents menuitem - Result:=Count; - SelfIndex:=Section.IndexOf(Self); - if (SelfIndex>0) then begin - // a top separator is needed - inc(Result); - end; - if (SelfIndexnil) and (not ChildsAsSubMenu) then begin + // childs are not in a submenu but directly added to parents menuitem + Result:=Count; + if NeedTopSeparator then inc(Result); + if NeedBottomSeparator then inc(Result); end; + end else begin + Result:=0; end; end; @@ -454,6 +545,44 @@ begin end; end; +function TIDEMenuSection.GetContainerMenuItem: TMenuItem; +var + IDEMenuItem: TIDEMenuSection; +begin + IDEMenuItem:=Self; + while (IDEMenuItem<>nil) and (not IDEMenuItem.ChildsAsSubMenu) do + IDEMenuItem:=IDEMenuItem.Section; + if (IDEMenuItem<>nil) then + Result:=IDEMenuItem.MenuItem; +end; + +function TIDEMenuSection.IndexByName(const AName: string): Integer; +begin + Result:=Count-1; + while (Result>=0) and (CompareText(AName,Items[Result].Name)<>0) do + dec(Result); +end; + +function TIDEMenuSection.FindByName(const AName: string): TIDEMenuItem; +var + i: LongInt; +begin + i:=IndexByName(AName); + if i>=0 then + Result:=Items[i] + else + Result:=nil; +end; + +function TIDEMenuSection.CreateUniqueName(const AName: string): string; +begin + Result:=AName; + if IndexByName(Result)=0 then exit; + Result:=CreateFirstIdentifier(Result); + while IndexByName(Result)>=0 do + Result:=CreateNextIdentifier(Result); +end; + function TIDEMenuSection.GetItems(Index: Integer): TIDEMenuItem; begin Result:=TIDEMenuItem(FItems[Index]); @@ -548,7 +677,7 @@ end; { TIDEMenuRoots } -function TIDEMenuRoots.GetItems(Index: integer): TIDEMenuItem; +function TIDEMenuRoots.GetItems(Index: integer): TIDEMenuSection; begin Result:=TIDEMenuSection(FItems[Index]); end; @@ -564,12 +693,13 @@ begin inherited Destroy; end; -procedure TIDEMenuRoots.RegisterMenuRoot(Section: TIDEMenuItem); +procedure TIDEMenuRoots.RegisterMenuRoot(Section: TIDEMenuSection); begin + Section.FName:=CreateUniqueName(Section.Name); FItems.Add(Section); end; -procedure TIDEMenuRoots.UnregisterMenuRoot(Section: TIDEMenuItem); +procedure TIDEMenuRoots.UnregisterMenuRoot(Section: TIDEMenuSection); begin FItems.Remove(Section); end; @@ -589,12 +719,73 @@ end; procedure TIDEMenuRoots.Delete(Index: Integer); var - OldItem: TIDEMenuItem; + OldItem: TIDEMenuSection; begin OldItem:=Items[Index]; UnregisterMenuRoot(OldItem); OldItem.Free; end; +function TIDEMenuRoots.IndexByName(const Name: string): Integer; +begin + Result:=Count-1; + while (Result>=0) and (CompareText(Name,Items[Result].Name)<>0) do + dec(Result); +end; + +function TIDEMenuRoots.FindByName(const Name: string): TIDEMenuSection; +var + i: LongInt; +begin + i:=IndexByName(Name); + if i>=0 then + Result:=Items[i] + else + Result:=nil; +end; + +function TIDEMenuRoots.CreateUniqueName(const Name: string): string; +begin + Result:=Name; + if IndexByName(Result)=0 then exit; + Result:=CreateFirstIdentifier(Result); + while IndexByName(Result)>=0 do + Result:=CreateNextIdentifier(Result); +end; + +function TIDEMenuRoots.FindByPath(const Path: string; + ErrorOnNotFound: boolean): TIDEMenuItem; +var + StartPos: Integer; + EndPos: LongInt; + Name: String; +begin + Result:=nil; + StartPos:=1; + while StartPos<=length(Path) do begin + EndPos:=StartPos; + while (EndPos<=length(Path)) and (Path[EndPos]<>'/') do inc(EndPos); + if EndPos>StartPos then begin + Name:=copy(Path,StartPos,EndPos-StartPos); + if Result=nil then + // search root + Result:=FindByName(Name) + else if Result is TIDEMenuSection then + // search child + Result:=TIDEMenuSection(Result).FindByName(Name) + else + // path too long -> we are already at a leaf + Result:=nil; + if Result=nil then break; + end; + StartPos:=EndPos+1; + end; + if Result=nil then begin + if ErrorOnNotFound then begin + raise Exception.Create('IDE Menu path not found: '+Path); + end; + end; +end; + end. diff --git a/ideintf/texttools.pas b/ideintf/texttools.pas index 281a438082..0cf88a0066 100644 --- a/ideintf/texttools.pas +++ b/ideintf/texttools.pas @@ -82,6 +82,14 @@ function RESplit(const TheText, SeparatorRegExpr: string; procedure RESplit(const TheText, SeparatorRegExpr: string; Pieces: TStrings; const ModifierStr: string = ''); +// identifier +function CreateFirstIdentifier(const Identifier: string): string; +function CreateNextIdentifier(const Identifier: string): string; + +// xml paths +function GetPathElement(const Path: string; StartPos: integer; + Stopper: char): string; + //------------------------------------------------------------------------------ // Internal stuff. @@ -149,5 +157,36 @@ begin RESplit(TheText,SeparatorRegExpr,Result,ModifierStr); end; +function CreateFirstIdentifier(const Identifier: string): string; +// example: Ident59 becomes Ident1 +var + p: Integer; +begin + p:=length(Identifier); + while (p>=1) and (Identifier[p] in ['0'..'9']) do dec(p); + Result:=copy(Identifier,1,p)+'1'; +end; + +function CreateNextIdentifier(const Identifier: string): string; +// example: Ident59 becomes Ident60 +var + p: Integer; +begin + p:=length(Identifier); + while (p>=1) and (Identifier[p] in ['0'..'9']) do dec(p); + Result:=copy(Identifier,1,p) + +IntToStr(1+StrToIntDef(copy(Identifier,p+1,length(Identifier)-p),0)); +end; + +function GetPathElement(const Path: string; StartPos: integer; + Stopper: char): string; +var + p: LongInt; +begin + p:=StartPos; + while (p<=length(Path)) and (Path[p]<>Stopper) do inc(p); + Result:=copy(Path,StartPos,p-StartPos); +end; + end.