diff --git a/components/ideintf/menuintf.pas b/components/ideintf/menuintf.pas index f625150a55..3383574c72 100644 --- a/components/ideintf/menuintf.pas +++ b/components/ideintf/menuintf.pas @@ -36,20 +36,19 @@ type TIDEMenuItem = class(TIDESpecialCommand) private - FAutoFreeMenuItem: boolean; FBitmap: TBitmap; FMenuItem: TMenuItem; FMenuItemClass: TMenuItemClass; FSection: TIDEMenuSection; // parent section FSectionIndex: Integer;// index in parent section - FSize: integer; // size of this item including separators and children in parent section (e.g. a submenu has size=1) FTag: Integer; FUserTag: PtrUInt; - FLastVisibleActive: boolean; FVisible: Boolean; + FVisibleCommandCount: integer; procedure MenuItemDestroy(Sender: TObject); procedure BitmapChange(Sender: TObject); protected + procedure RealizeVisible; procedure SetCommand(const AValue: TIDECommand); override; procedure MenuItemClick(Sender: TObject); virtual; function GetBitmap: TBitmap; virtual; @@ -69,14 +68,15 @@ type destructor Destroy; override; function GetImageList: TCustomImageList; virtual; function HasBitmap: Boolean; - procedure CreateMenuItem; virtual; + procedure CreateMenuItem; virtual; // only create and set properties, does not add to Section.MenuItem function GetPath: string; function GetRoot: TIDEMenuItem; function VisibleActive: boolean; virtual; function RealVisible: boolean; // this one and all parent sections are visible function GetContainerSection: TIDEMenuSection; // returns nearest sub menu section function GetContainerMenuItem: TMenuItem; // returns nearest sub menu - function Size: integer; virtual; // size of this item including separators and children in parent section (e.g. a submenu has size=1) + function GetNextSameContainer: TIDEMenuItem; + function GetPrevSameContainer: TIDEMenuItem; function HasAsParent(Item: TIDEMenuItem): boolean; procedure WriteDebugReport(const Prefix: string; MenuItemDebugReport: boolean); virtual; @@ -84,13 +84,13 @@ type public property Bitmap: TBitmap read GetBitmap write SetBitmap; property Section: TIDEMenuSection read FSection write SetSection; - property MenuItem: TMenuItem read FMenuItem write SetMenuItem; // Note: the root section MenuItem = TMenu.Items + property MenuItem: TMenuItem read FMenuItem write SetMenuItem; // Note: root section MenuItem = TMenu.Items, setting a non root does not add to Section.MenuItem property MenuItemClass: TMenuItemClass read FMenuItemClass write FMenuItemClass; property SectionIndex: Integer read FSectionIndex; - property AutoFreeMenuItem: boolean read FAutoFreeMenuItem write FAutoFreeMenuItem; property Tag: Integer read FTag write FTag; property UserTag: PtrUInt read FUserTag write FUserTag; property Visible: Boolean read FVisible write SetVisible; + property VisibleCommandCount: integer read FVisibleCommandCount; // with grandchildren end; TIDEMenuItemClass = class of TIDEMenuItem; @@ -104,7 +104,7 @@ type imssClearing ); TIDEMenuSectionStates = set of TIDEMenuSectionState; - + TIDEMenuSectionHandlerType = ( imshtOnShow // called before showing. Use this to enable/disable context sensitive items. ); @@ -112,22 +112,14 @@ type TIDEMenuSection = class(TIDEMenuItem) private FBottomSeparator: TMenuItem; - FChildMenuItemsCreated: boolean; FChildrenAsSubMenu: boolean; - FInvalidChildEndIndex: Integer; - FInvalidChildStartIndex: Integer; FItems: TFPList; - FNeedBottomSeparator: boolean; - FNeedTopSeparator: boolean; FSectionHandlers: array[TIDEMenuSectionHandlerType] of TMethodList; FStates: TIDEMenuSectionStates; FSubMenuImages: TCustomImageList; FTopSeparator: TMenuItem; - FUpdateLock: Integer; - FVisibleCount: integer; + procedure OnSeparatorDestroy(Sender: TObject); function GetItems(Index: Integer): TIDEMenuItem; - procedure SeparatorDestroy(Sender : TObject); - procedure FreeSeparators; procedure AddHandler(HandlerType: TIDEMenuSectionHandlerType; const AMethod: TMethod; AsLast: boolean = false); procedure RemoveHandler(HandlerType: TIDEMenuSectionHandlerType; @@ -136,13 +128,17 @@ type procedure MenuItemClick(Sender: TObject); override; procedure SetMenuItem(const AValue: TMenuItem); override; procedure SetChildrenAsSubMenu(const AValue: boolean); virtual; + procedure SetVisible(const AValue: Boolean); override; procedure SetSubMenuImages(const AValue: TCustomImageList); virtual; procedure ClearMenuItems; override; - procedure ItemVisibleActiveChanged(AnItem: TIDEMenuItem); + procedure FreeTopSeparator; + procedure FreeBottomSeparator; procedure UpdateAllChildrenIndex(StartIndex: Integer); - procedure UpdateMenuStructure; - procedure UpdateSize(Diff: integer); - procedure Invalidate(FromIndex, ToIndex: integer); + procedure UpdateTopSeparator(ParentMenuItem: TMenuItem; var aMenuIndex: integer); + procedure UpdateBottomSeparator(ParentMenuItem: TMenuItem; var aMenuIndex: integer); + procedure UpdateContainer; + procedure UpdateSubMenus; + procedure UpdateVisibleCommandCount(Add: integer); public constructor Create(const TheName: string); override; destructor Destroy; override; @@ -153,16 +149,18 @@ type procedure Insert(Index: Integer; AnItem: TIDEMenuItem); procedure Remove(AnItem: TIDEMenuItem); procedure CreateMenuItem; override; - function GetContainerIndex(BehindSeparator: boolean): Integer; - function GetChildContainerIndex(Index: integer): Integer; function IndexOf(AnItem: TIDEMenuItem): Integer; function IndexByName(const AName: string): Integer; function FindByName(const AName: string): TIDEMenuItem; function CreateUniqueName(const AName: string): string; function VisibleActive: boolean; override; - function Size: integer; override; - procedure BeginUpdate; - procedure EndUpdate; + function NeedTopSeparator: boolean; + function NeedBottomSeparator: boolean; + function GetFirstChildSameContainer: TIDEMenuItem; + function GetLastChildSameContainer: TIDEMenuItem; + procedure BeginUpdate; deprecated; + procedure EndUpdate; deprecated; + procedure NotifySubSectionOnShow(Sender: TObject; WithChildren: Boolean = true); virtual; procedure RemoveAllHandlersOfObject(AnObject: TObject); @@ -173,18 +171,15 @@ type MenuItemDebugReport: boolean); override; procedure ConsistencyCheck; override; public - property ChildsAsSubMenu: boolean read FChildrenAsSubMenu - write SetChildrenAsSubMenu default true; deprecated;// use ChildrenAsSubMenu instead property ChildrenAsSubMenu: boolean read FChildrenAsSubMenu write SetChildrenAsSubMenu default true; + property ChildsAsSubMenu: boolean read FChildrenAsSubMenu + write SetChildrenAsSubMenu default true; deprecated;// use ChildrenAsSubMenu instead property SubMenuImages: TCustomImageList read FSubMenuImages write SetSubMenuImages; property Items[Index: Integer]: TIDEMenuItem read GetItems; default; property TopSeparator: TMenuItem read FTopSeparator; property BottomSeparator: TMenuItem read FBottomSeparator; - property NeedTopSeparator: boolean read FNeedTopSeparator; - property NeedBottomSeparator: boolean read FNeedBottomSeparator; - property VisibleCount: integer read FVisibleCount; // without grandchildren property States: TIDEMenuSectionStates read FStates; end; TIDEMenuSectionClass = class of TIDEMenuSection; @@ -211,6 +206,8 @@ type procedure SetShowAlwaysCheckable(const AValue: boolean); virtual; procedure SetMenuItem(const AValue: TMenuItem); override; public + constructor Create(const TheName: string); override; + procedure ConsistencyCheck; override; property AutoCheck: boolean read FAutoCheck write SetAutoCheck default False; property Default: Boolean read FDefault write SetDefault default False; property GroupIndex: Byte read FGroupIndex write SetGroupIndex default 0; @@ -553,19 +550,18 @@ end; procedure TIDEMenuItem.MenuItemDestroy(Sender: TObject); begin FMenuItem:=nil; - FAutoFreeMenuItem:=false; end; function TIDEMenuItem.RealVisible: boolean; var - xSection: TIDEMenuSection; + aSection: TIDEMenuSection; begin Result := Visible; - xSection := Section; - while (xSection<>nil) and Result do + aSection := Section; + while (aSection<>nil) and Result do begin - Result := xSection.Visible; - xSection := xSection.Section; + Result := aSection.Visible; + aSection := aSection.Section; end; end; @@ -574,6 +570,13 @@ begin if MenuItem<>nil then MenuItem.Bitmap:=Bitmap; end; +procedure TIDEMenuItem.RealizeVisible; +begin + if MenuItem=nil then exit; + MenuItem.Visible:=VisibleActive + or (Section=nil); // keep the root menuitem always visible +end; + procedure TIDEMenuItem.SetEnabled(const AValue: Boolean); begin if Enabled=AValue then exit; @@ -660,11 +663,11 @@ begin end; procedure TIDEMenuItem.SetMenuItem(const AValue: TMenuItem); +// only set the properties, do not update container begin if FMenuItem = AValue then exit; if FMenuItem <> nil then ClearMenuItems; FMenuItem := AValue; - AutoFreeMenuItem := False; if MenuItem <> nil then begin MenuItem.AddHandlerOnDestroy(@MenuItemDestroy); @@ -672,24 +675,23 @@ begin MenuItem.Bitmap := FBitmap; MenuItem.Hint := Hint; MenuItem.ImageIndex := ImageIndex; - MenuItem.Visible := Visible; MenuItem.Enabled := Enabled; MenuItem.OnClick := @MenuItemClick; MenuItem.ImageIndex := ImageIndex; - end else if Section<>nil then - Section.Invalidate(SectionIndex,SectionIndex); + RealizeVisible; + end; end; procedure TIDEMenuItem.SetSection(const AValue: TIDEMenuSection); -var - OldSection: TIDEMenuSection; begin if FSection=AValue then exit; - OldSection:=FSection; - ClearMenuItems; - if OldSection<>nil then - OldSection.Remove(Self); - FSection:=nil; + if Section<>nil then + Section.Remove(Self) + else begin + ClearMenuItems; + FSection:=nil; + FSectionIndex:=-1; + end; if FSection<>nil then FSection.AddLast(Self); end; @@ -702,9 +704,14 @@ begin OldVisibleActive:=VisibleActive; FVisible:=AValue; if MenuItem<>nil then - MenuItem.Visible:=Visible; - if (VisibleActive<>OldVisibleActive) and (Section<>nil) then - Section.ItemVisibleActiveChanged(Self); + RealizeVisible; + if (VisibleActive<>OldVisibleActive) and (Section<>nil) + and (VisibleCommandCount>0) then begin + if Visible then + Section.UpdateVisibleCommandCount(VisibleCommandCount) + else + Section.UpdateVisibleCommandCount(-VisibleCommandCount); + end; end; procedure TIDEMenuItem.ShortCutsUpdated(const aShortCut, @@ -723,39 +730,31 @@ begin if FMenuItem <> nil then begin FMenuItem.OnClick := nil; FMenuItem.RemoveHandlerOnDestroy(@MenuItemDestroy); + if (Section<>nil) or (FMenuItem.Parent<>nil) then + FMenuItem.Free; + FMenuItem:=nil; end; - if AutoFreeMenuItem then begin - FAutoFreeMenuItem:=false; - FMenuItem.Free; - end; - FMenuItem:=nil; - if Section<>nil then - Section.Invalidate(SectionIndex,SectionIndex); end; constructor TIDEMenuItem.Create(const TheName: string); begin inherited Create(TheName); FVisible:=true; - FSize:=1; FMenuItemClass:=TMenuItem; FSectionIndex:=-1; {$IFDEF VerboseMenuIntf} - //debugln('TIDEMenuItem.Create ',dbgsName(Self),' Name="',Name,'"'); + debugln('TIDEMenuItem.Create ',dbgsName(Self),' Name="',Name,'"'); {$ENDIF} end; destructor TIDEMenuItem.Destroy; begin - if Section<>nil then - Section.Remove(Self); + {$IFDEF VerboseMenuIntf} + debugln('TIDEMenuItem.Destroy ',dbgsName(Self),' Name="',Name,'"'); + {$ENDIF} + Section:=nil; + ClearMenuItems; FreeAndNil(FBitmap); - if FMenuItem<>nil then begin - if FAutoFreeMenuItem then - FreeAndNil(FMenuItem) - else - FMenuItem.RemoveAllHandlersOfObject(Self); - end; inherited Destroy; end; @@ -793,7 +792,6 @@ begin //debugln('TIDEMenuItem.CreateMenuItem ',dbgsName(Self),' Name="',Name,'"'); {$ENDIF} MenuItem:=MenuItemClass.Create(nil); - AutoFreeMenuItem:=true; end; function TIDEMenuItem.GetPath: string; @@ -815,8 +813,7 @@ begin end; function TIDEMenuItem.VisibleActive: boolean; -// true if Visible=true and not hidden -// false if menu item is hidden (e.g. due to no context, see TIDEMenuSection) +// true if has visible content begin Result:=Visible; end; @@ -842,9 +839,68 @@ begin Result:=nil; end; -function TIDEMenuItem.Size: integer; +function TIDEMenuItem.GetNextSameContainer: TIDEMenuItem; +// find the next visible TIDEMenuItem in the container (i.e. same MenuItem.Parent) +// The result can be: +// - a TIDEMenuCommand +// - a TIDEMenuSection with ChildrenAsSubMenu=true +// - a TIDEMenuSection with TopSeparator<>nil +var + i: Integer; + Sibling: TIDEMenuItem; + SiblingSection: TIDEMenuSection; begin - Result:=FSize; + Result:=nil; + if Section=nil then exit; + if Section.ChildrenAsSubMenu then + exit; // Self is the last item -> there is no next + for i:=SectionIndex+1 to Section.Count-1 do begin + Sibling:=Section[i]; + if not Sibling.VisibleActive then continue; + if Sibling is TIDEMenuSection then begin + SiblingSection:=TIDEMenuSection(Sibling); + if SiblingSection.ChildrenAsSubMenu + or (SiblingSection.TopSeparator<>nil) then + exit(SiblingSection); + Result:=SiblingSection.GetFirstChildSameContainer; + end else begin + exit(Sibling as TIDEMenuCommand); + end; + end; + // search behind parent Section + Result:=Section.GetNextSameContainer; +end; + +function TIDEMenuItem.GetPrevSameContainer: TIDEMenuItem; +// find the previous visible TIDEMenuItem in the container (i.e. same MenuItem.Parent) +// The result can be: +// - a TIDEMenuCommand +// - a TIDEMenuSection with ChildrenAsSubMenu=true +// - a TIDEMenuSection with BottomSeparator<>nil +var + i: Integer; + Sibling: TIDEMenuItem; + SiblingSection: TIDEMenuSection; +begin + Result:=nil; + if Section=nil then exit; + if Section.ChildrenAsSubMenu then + exit; // Self is the first item -> there is no previous + for i:=SectionIndex-1 downto 0 do begin + Sibling:=Section[i]; + if not Sibling.VisibleActive then continue; + if Sibling is TIDEMenuSection then begin + SiblingSection:=TIDEMenuSection(Sibling); + if SiblingSection.ChildrenAsSubMenu + or (SiblingSection.BottomSeparator<>nil) then + exit(SiblingSection); + Result:=SiblingSection.GetLastChildSameContainer; + end else begin + exit(Sibling as TIDEMenuCommand); + end; + end; + // search in front of parent Section + Result:=Section.GetNextSameContainer; end; function TIDEMenuItem.HasAsParent(Item: TIDEMenuItem): boolean; @@ -870,23 +926,25 @@ end; procedure TIDEMenuItem.ConsistencyCheck; - procedure RaiseError; + procedure RaiseError(const Msg: string = ''); var s: String; begin s:='TIDEMenuItem.ConsistencyCheck Name="'+Name+'" Caption="'+DbgStr(Caption)+'"'; + if Msg<>'' then + s+='. '+Msg; debugln(s); RaiseGDBException(s); end; begin if MenuItem<>nil then begin - debugln(['TIDEMenuItem.ConsistencyCheck: Bitmap=', FBitmap, - ', ImageIndex=', ImageIndex, ', ImageList=', GetImageList]); + //debugln(['TIDEMenuItem.ConsistencyCheck: Bitmap=', FBitmap, + // ', ImageIndex=', ImageIndex, ', ImageList=', GetImageList]); if MenuItem.Enabled<>Enabled then - RaiseError; - if MenuItem.Visible<>Visible then - RaiseError; + RaiseError('MenuItem.Enabled='+dbgs(MenuItem.Enabled)+' Enabled='+dbgs(Enabled)); + if MenuItem.Visible<>(VisibleActive or (Section=nil)) then + RaiseError('MenuItem.Visible='+dbgs(MenuItem.Visible)+' VisibleActive='+dbgs(VisibleActive)+' Visible='+dbgs(Visible)); if MenuItem.Caption<>Caption then RaiseError; if MenuItem.ImageIndex<>ImageIndex then @@ -900,6 +958,8 @@ begin end else begin if SectionIndex<0 then RaiseError; + if Section[SectionIndex]<>Self then + RaiseError; end; end; @@ -917,10 +977,24 @@ procedure TIDEMenuSection.ClearMenuItems; var i: Integer; begin - FreeSeparators; - for i:=0 to Count-1 do Items[i].ClearMenuItems; + if FItems<>nil then + for i:=Count-1 downto 0 do + Items[i].ClearMenuItems; + FreeTopSeparator; + FreeBottomSeparator; inherited ClearMenuItems; - Invalidate(0,Count-1); +end; + +procedure TIDEMenuSection.FreeTopSeparator; +begin + if TopSeparator=nil then exit; + FreeAndNil(FTopSeparator); +end; + +procedure TIDEMenuSection.FreeBottomSeparator; +begin + if BottomSeparator=nil then exit; + FreeAndNil(FBottomSeparator); end; procedure TIDEMenuSection.UpdateAllChildrenIndex(StartIndex: Integer); @@ -931,285 +1005,150 @@ begin Items[i].FSectionIndex:=i; end; -procedure TIDEMenuSection.UpdateMenuStructure; -// updates all FNeedBottomSeparator and FNeedTopSeparator -var - ContainerMenuItem: TMenuItem; - ContainerMenuIndex: integer; - Item: TIDEMenuItem; - CurSection: TIDEMenuSection; - xRealVisible: Boolean; - - procedure UpdateNeedTopSeparator; - // a separator at top is needed, if - // - this section is embedded (not ChildrenAsSubMenu) - // - and this section is visible - // - and this section has visible children - // - and there is a visible menu item in front - var - i: Integer; - NewNeedTopSeparator: Boolean; - begin - NewNeedTopSeparator:=false; - if (not ChildrenAsSubMenu) and (Section<>nil) and VisibleActive and xRealVisible then begin - // check for any visible item in front - i:=SectionIndex-1; - while i>=0 do begin - if Section[i].VisibleActive then begin - // there is a visible menu item in front - // => the Top separator is needed - //debugln('TIDEMenuSection.UpdateNeedTopSeparator Name="',Name,'" ItemInFront="',Section[i].Name,'" '); - NewNeedTopSeparator:=true; - break; - end; - dec(i); - end; - end; - - if NewNeedTopSeparator<>FNeedTopSeparator then begin - FNeedTopSeparator:=NewNeedTopSeparator; - if FNeedTopSeparator then - UpdateSize(1) - else - UpdateSize(-1); - end; - - if ContainerMenuItem=nil then exit; - if FNeedTopSeparator<>(TopSeparator<>nil) then begin - // FNeedTopSeparator has changed - if TopSeparator<>nil then begin - // TopSeparator is not needed anymore - FreeAndNil(FTopSeparator); - {$IFDEF VerboseMenuIntf} - debugln('TIDEMenuSection.UpdateMenuStructure FREE TopSeparator Name="',Name,'"'); - {$ENDIF} - end else begin - // TopSeparator is needed - FTopSeparator:=TMenuItem.Create(nil); - FTopSeparator.AddHandlerOnDestroy(@SeparatorDestroy); - FTopSeparator.Caption:='-'; - {$IFDEF VerboseMenuIntf} - debugln('TIDEMenuSection.UpdateNeedTopSeparator CREATE TopSeparator Name="',Name,'" ContainerMenuIndex=',dbgs(ContainerMenuIndex),' ContainerMenuItem.Count=',dbgs(ContainerMenuItem.Count)); - {$ENDIF} - if ContainerMenuIndex>ContainerMenuItem.Count then - begin - debugln('TIDEMenuSection.UpdateNeedTopSeparator CREATE TopSeparator Name="',Name,'" ContainerMenuIndex=',dbgs(ContainerMenuIndex),' ContainerMenuItem.Count=',dbgs(ContainerMenuItem.Count)); - GetRoot.WriteDebugReport(' Top ',true); - debugln('TIDEMenuSection.UpdateNeedTopSeparator CREATE TopSeparator Name="',Name,'" ContainerMenuIndex ** FORCED VALUE ** FROM ContainerMenuItem.Count=',dbgs(ContainerMenuItem.Count)); - RaiseGDBException('TIDEMenuSection.UpdateNeedTopSeparator inconsistency'); - end; - ContainerMenuItem.Insert(ContainerMenuIndex,FTopSeparator); - end; - end; - end; - - procedure UpdateNeedBottomSeparator; - // a separator at bottom is needed, if - // - this section is imbedded (not ChildrenAsSubMenu) - // - and this section is visible - // - and this section has visible children - // - and there is a visible menu item behind - // - and the visible menu item is not an embedded section (that creates its own separator) - var - ItemBehind: TIDEMenuItem; - i: Integer; - NewNeedBottomSeparator: Boolean; - begin - NewNeedBottomSeparator:=false; - //debugln('TIDEMenuSection.UpdateNeedBottomSeparator Name="',Name,'" ChildrenAsSubMenu=',dbgs(ChildrenAsSubMenu),' Section=',dbgs(Section<>nil),' VisibleActive=',dbgs(VisibleActive)); - if (not ChildrenAsSubMenu) and (Section<>nil) and VisibleActive and xRealVisible then begin - // check for any visible item behind - i:=SectionIndex+1; - while iFInvalidChildEndIndex) then exit; - if FUpdateLock>0 then begin + if NeedTopSeparator then begin + if (TopSeparator<>nil) + and (aMenuIndexnil then + FreeTopSeparator; + FTopSeparator:=MenuItemClass.Create(nil); + TopSeparator.AddHandlerOnDestroy(@OnSeparatorDestroy); + ParentMenuItem.Insert(aMenuIndex,TopSeparator); + end; + inc(aMenuIndex); + end else begin + if TopSeparator=nil then exit; + FreeTopSeparator; + end; +end; + +procedure TIDEMenuSection.UpdateBottomSeparator(ParentMenuItem: TMenuItem; + var aMenuIndex: integer); +begin + if NeedBottomSeparator then begin + if (BottomSeparator<>nil) + and (aMenuIndexnil then + FreeBottomSeparator; + FBottomSeparator:=MenuItemClass.Create(nil); + BottomSeparator.AddHandlerOnDestroy(@OnSeparatorDestroy); + ParentMenuItem.Insert(aMenuIndex,BottomSeparator); + end; + inc(aMenuIndex); + end else begin + if BottomSeparator=nil then exit; + FreeBottomSeparator; + end; +end; + +procedure TIDEMenuSection.UpdateContainer; +var + ParentMenuItem: TMenuItem; + aMenuIndex: integer; + + procedure UpdateSection(aSection: TIDEMenuSection); + var + i: Integer; + Item: TIDEMenuItem; + SubSection: TIDEMenuSection; + aVisible: Boolean; + begin + aVisible:=aSection.RealVisible; + for i:=0 to aSection.Count-1 do begin + Item:=aSection[i]; + if (Item is TIDEMenuSection) + and (not TIDEMenuSection(Item).ChildrenAsSubMenu) then begin + SubSection:=TIDEMenuSection(Item); + SubSection.UpdateTopSeparator(ParentMenuItem,aMenuIndex); + UpdateSection(SubSection); + SubSection.UpdateBottomSeparator(ParentMenuItem,aMenuIndex); + end else begin + // append MenuItem + if (Item.MenuItem<>nil) + and (aMenuIndex ok + inc(aMenuIndex); + end else begin + // structure has changed + if Item.MenuItem<>nil then + Item.ClearMenuItems; + if (Item.MenuItem=nil) and aVisible and Item.VisibleActive then + Item.CreateMenuItem; + if Item.MenuItem<>nil then begin + if Item.MenuItem.Parent=nil then + ParentMenuItem.Insert(aMenuIndex,Item.MenuItem); + inc(aMenuIndex); + end; + end; + end; + end; + end; + +begin + if not ChildrenAsSubMenu then begin + if Section<>nil then Section.UpdateContainer; exit; end; - if FInvalidChildStartIndex<0 then FInvalidChildStartIndex:=0; - xRealVisible := RealVisible; + if MenuItem=nil then exit; + if imssClearing in FStates then exit; + ParentMenuItem:=MenuItem; + aMenuIndex:=0; + UpdateSection(Self); +end; - if (Section<>nil) and (not Section.ChildrenAsSubMenu) - and (Section.FInvalidChildStartIndex<=SectionIndex) then begin - // the sections in front need update too - // => start the update in front - {$IFDEF VerboseMenuIntf} - debugln('TIDEMenuSection.UpdateMenuStructure Front Section="',Section.Name,'" Name="',Name,'" Section.Invalid=',dbgs(Section.FInvalidChildStartIndex),'..',dbgs(Section.FInvalidChildEndIndex),' Count=',dbgs(Count),' SectionIndex=',dbgs(SectionIndex)); - {$ENDIF} - Section.UpdateMenuStructure; - end; - // the sections in front are now uptodate, lets continue updating the current section - if FInvalidChildStartIndexaSection.VisibleActive) or (aSection=Self) then begin + {$IFDEF VerboseMenuIntf} + debugln(['TIDEMenuSection.UpdateVisibleCommandCount "',Name,'" Section="',aSection.Name,'" WasVis=',WasVisibleActive,' NowVis=',aSection.VisibleActive,' MI.Vis=',(aSection.MenuItem<>nil) and aSection.MenuItem.Visible]); + {$ENDIF} + if aSection.MenuItem<>nil then + aSection.RealizeVisible; + aSection.UpdateContainer; + end; + if not aSection.Visible then break; + aSection:=aSection.Section; end; end; @@ -1233,56 +1172,10 @@ begin end; end; -procedure TIDEMenuSection.ItemVisibleActiveChanged(AnItem: TIDEMenuItem); -var - OldVisibleActive: Boolean; - NowVisibleActive: Boolean; - FromIndex: LongInt; - ToIndex: LongInt; -begin - if imssClearing in FStates then - exit; - NowVisibleActive:=(AnItem.Section<>nil) and AnItem.VisibleActive; - if NowVisibleActive=AnItem.FLastVisibleActive then - RaiseGDBException(''); - AnItem.FLastVisibleActive:=NowVisibleActive; - - FromIndex:=AnItem.SectionIndex; - ToIndex:=AnItem.SectionIndex; - if (FromIndex>0) and NowVisibleActive then dec(FromIndex); - if (ToIndex0 then + RaiseGDBException(''); Exclude(FStates,imssClearing); - if (Section<>nil) and (OldVisibleActive<>VisibleActive) then - Section.ItemVisibleActiveChanged(Self); - EndUpdate; end; function TIDEMenuSection.Count: Integer; @@ -1342,14 +1227,23 @@ begin end; procedure TIDEMenuSection.Insert(Index: Integer; AnItem: TIDEMenuItem); +var + AddedVisibleCommands: Integer; begin + AnItem.Section:=nil; AnItem.Name:=CreateUniqueName(AnItem.Name); FItems.Insert(Index,AnItem); UpdateAllChildrenIndex(Index); - UpdateSize(AnItem.Size); AnItem.FSection:=Self; - if AnItem.VisibleActive then - ItemVisibleActiveChanged(AnItem); + + AddedVisibleCommands:=0; + if AnItem.Visible then + AddedVisibleCommands:=AnItem.VisibleCommandCount; + // update this and parents TMenuItems + UpdateVisibleCommandCount(AddedVisibleCommands); + // connect child TMenuItems + UpdateSubMenus; + {$IFDEF VerboseMenuIntf} ConsistencyCheck; {$ENDIF} @@ -1357,62 +1251,47 @@ end; procedure TIDEMenuSection.Remove(AnItem: TIDEMenuItem); var - OldVisibleActive: Boolean; + RemovedVisibleCommands: Integer; begin - OldVisibleActive:=False; + // consistency checks + if AnItem=nil then + RaiseGDBException(''); + if AnItem.Section<>Self then + RaiseGDBException(''); + if not (imssClearing in FStates) then begin - OldVisibleActive:=AnItem.VisibleActive; + // remove from FItems FItems.Delete(AnItem.SectionIndex); UpdateAllChildrenIndex(AnItem.SectionIndex); end; - UpdateSize(-AnItem.Size); + + RemovedVisibleCommands:=0; + if AnItem.Visible then + RemovedVisibleCommands:=AnItem.VisibleCommandCount; + AnItem.FSection:=nil; + AnItem.FSectionIndex:=-1; + + // free TMenuItems + if not (imssClearing in FStates) then + AnItem.ClearMenuItems; + + // update this and parents TMenuItems + UpdateVisibleCommandCount(-RemovedVisibleCommands); + if not (imssClearing in FStates) then begin - if OldVisibleActive then - ItemVisibleActiveChanged(AnItem); - // set the Index as last - AnItem.FSectionIndex:=0; + {$IFDEF VerboseMenuIntf} + ConsistencyCheck; + {$ENDIF} end; end; procedure TIDEMenuSection.CreateMenuItem; begin if ChildrenAsSubMenu then - inherited CreateMenuItem; -end; - -function TIDEMenuSection.GetContainerIndex(BehindSeparator: boolean): Integer; -var - SiblingIndex: Integer; -begin - Result:=0; - if (Section=nil) then exit; - - // get the start of the parent Section - if not Section.ChildrenAsSubMenu then - inc(Result,Section.GetContainerIndex(true)); - // add all siblings in front - SiblingIndex:=0; - while (Section[SiblingIndex]<>Self) do begin - if Section[SiblingIndex].Visible then - inc(Result,Section[SiblingIndex].Size); - inc(SiblingIndex); - end; - // add separator - if BehindSeparator and NeedTopSeparator then - inc(Result); -end; - -function TIDEMenuSection.GetChildContainerIndex(Index: integer): Integer; -var - i: Integer; -begin - if ChildrenAsSubMenu then - Result:=0 + inherited CreateMenuItem else - Result:=GetContainerIndex(true); - for i:=0 to Index-1 do - inc(Result,Items[i].Size); + ; // this section has no menuitem for its own end; function TIDEMenuSection.IndexOf(AnItem: TIDEMenuItem): Integer; @@ -1449,29 +1328,117 @@ end; function TIDEMenuSection.VisibleActive: boolean; begin - Result:=Visible and (VisibleCount>0); + Result:=Visible and (VisibleCommandCount>0); end; -function TIDEMenuSection.Size: integer; +function TIDEMenuSection.NeedTopSeparator: boolean; +var + i: Integer; + Sibling: TIDEMenuItem; begin - if ChildrenAsSubMenu then - Result:=1 - else - Result:=inherited Size; + Result:=false; + if MenuItem=nil then exit; + if ChildrenAsSubMenu then exit; + if not VisibleActive then exit; + // this is a logical section with visible MenuItems + if Section<>nil then begin + // search for a MenuItem in front + for i:=SectionIndex-1 downto 0 do begin + Sibling:=Section[i]; + if Sibling.VisibleActive then + exit(true); // there is a visible sibling above -> yes, need TopSeparator + end; + end; +end; + +function TIDEMenuSection.NeedBottomSeparator: boolean; +var + i: Integer; + Sibling: TIDEMenuItem; +begin + Result:=false; + if MenuItem=nil then exit; + if ChildrenAsSubMenu then exit; + if not VisibleActive then exit; + // this is a logical section with visible MenuItems + if Section<>nil then begin + for i:=SectionIndex+1 to Section.Count-1 do begin + Sibling:=Section[i]; + if Sibling.VisibleActive then begin + // there is a visible sibling below + if Sibling is TIDEMenuSection then begin + if not TIDEMenuSection(Sibling).ChildrenAsSubMenu then + exit(false); // the below sibling is a logical section with a TopSeparator -> no need for BottomSeparator + end; + // -> yes, need BottomSeparator + exit(true); + end; + end; + end; +end; + +function TIDEMenuSection.GetFirstChildSameContainer: TIDEMenuItem; +// find the first visible TIDEMenuItem in the same container (i.e. same MenuItem.Parent) +// The result can be: +// - a TIDEMenuCommand +// - a TIDEMenuSection with ChildrenAsSubMenu=true +// - a TIDEMenuSection with TopSeparator<>nil +var + i: Integer; + Item: TIDEMenuItem; + ChildSection: TIDEMenuSection; +begin + Result:=nil; + if ChildrenAsSubMenu then exit; + if not VisibleActive then exit; + for i:=0 to Count-1 do begin + Item:=Items[i]; + if not Item.VisibleActive then continue; + if Item is TIDEMenuCommand then + exit(Item); + ChildSection:=Item as TIDEMenuSection; + if ChildSection.ChildrenAsSubMenu + or (ChildSection.TopSeparator<>nil) then + exit(ChildSection); + Result:=ChildSection.GetFirstChildSameContainer; + end; +end; + +function TIDEMenuSection.GetLastChildSameContainer: TIDEMenuItem; +// find the last visible TIDEMenuItem in the same container (i.e. same MenuItem.Parent) +// The result can be: +// - a TIDEMenuCommand +// - a TIDEMenuSection with ChildrenAsSubMenu=true +// - a TIDEMenuSection with BottomSeparator<>nil +var + i: Integer; + Item: TIDEMenuItem; + ChildSection: TIDEMenuSection; +begin + Result:=nil; + if ChildrenAsSubMenu then exit; + if not VisibleActive then exit; + for i:=Count-1 downto 0 do begin + Item:=Items[i]; + if not Item.VisibleActive then continue; + if Item is TIDEMenuCommand then + exit(Item); + ChildSection:=Item as TIDEMenuSection; + if ChildSection.ChildrenAsSubMenu + or (ChildSection.BottomSeparator<>nil) then + exit(ChildSection); + Result:=ChildSection.GetLastChildSameContainer; + end; end; procedure TIDEMenuSection.BeginUpdate; begin - inc(FUpdateLock); + end; procedure TIDEMenuSection.EndUpdate; begin - if FUpdateLock<=0 then - RaiseGDBException('TIDEMenuSection.EndUpdate'); - dec(FUpdateLock); - if FUpdateLock=0 then - UpdateMenuStructure; + end; procedure TIDEMenuSection.RemoveAllHandlersOfObject(AnObject: TObject); @@ -1500,11 +1467,11 @@ var i: Integer; begin debugln([Prefix,'SectionIndex=',SectionIndex,' Name="',DbgStr(Name),'"', - ' VisibleActive=',VisibleActive, + ' Visible=',Visible, + ' VisCmdCnt=',VisibleCommandCount, + ' VisActive=',VisibleActive, ' ChildrenAsSubMenu=',ChildrenAsSubMenu, - ' ContainerIndex=',GetContainerIndex(false), - ' NeedSep:Top=',NeedTopSeparator,',Bottom=',NeedBottomSeparator, - ' Size=',dbgs(Size)]); + '']); for i:=0 to Count-1 do if Items[i]<>nil then Items[i].WriteDebugReport(Prefix+' ',false); if MenuItemDebugReport and (MenuItem<>nil) then @@ -1512,23 +1479,126 @@ begin end; procedure TIDEMenuSection.ConsistencyCheck; + + procedure RaiseError(const Msg: string = ''); + var + s: String; + begin + s:='TIDEMenuSection.ConsistencyCheck Name="'+Name+'"'; + if Msg<>'' then + s+='. '+Msg; + debugln(s); + RaiseGDBException(s); + end; + + procedure CheckMenuItemIndex(aMenuItem: TMenuItem; var Index: integer); + begin + if aMenuItem.Parent<>MenuItem then + RaiseError(''); + if Index>=MenuItem.Count then + RaiseError(''); + if MenuItem[Index]<>aMenuItem then + RaiseError(''); + inc(Index); + end; + + procedure CheckContainerMenuItems(aSection: TIDEMenuSection; var Index: integer); + var + i: Integer; + Item: TIDEMenuItem; + SubSection: TIDEMenuSection; + aVisible: Boolean; + begin + aVisible:=aSection.RealVisible; + for i:=0 to aSection.Count-1 do begin + Item:=aSection[i]; + if (Item is TIDEMenuSection) then begin + SubSection:=TIDEMenuSection(Item); + if SubSection.NeedTopSeparator then begin + if SubSection.TopSeparator=nil then + RaiseError('missing TopSeparator'); + CheckMenuItemIndex(SubSection.TopSeparator,Index); + end else begin + if SubSection.TopSeparator<>nil then + RaiseError('dangling TopSeparator'); + end; + if SubSection.ChildrenAsSubMenu then begin + if aVisible and SubSection.VisibleActive then begin + if SubSection.MenuItem=nil then + RaiseError('missing SubMenu'); + CheckMenuItemIndex(SubSection.MenuItem,Index); + end else begin + // a hidden item can have a MenuItem + if (SubSection.MenuItem<>nil) and (SubSection.MenuItem.Parent<>nil) then + CheckMenuItemIndex(SubSection.MenuItem,Index); + end; + end else + CheckContainerMenuItems(SubSection,Index); + if SubSection.NeedBottomSeparator then begin + if SubSection.BottomSeparator=nil then + RaiseError('missing BottomSeparator'); + CheckMenuItemIndex(SubSection.BottomSeparator,Index); + end else begin + if SubSection.BottomSeparator<>nil then + RaiseError('dangling BottomSeparator'); + end; + end else begin + // TIDEMenuCommand + if aVisible and Item.VisibleActive then begin + if Item.MenuItem=nil then + RaiseError('missing MenuItem'); + CheckMenuItemIndex(Item.MenuItem,Index); + end else begin + // a hidden item can have a MenuItem + if (Item.MenuItem<>nil) and (Item.MenuItem.Parent<>nil) then + CheckMenuItemIndex(Item.MenuItem,Index); + end; + end; + end; + end; + var i: Integer; Item: TIDEMenuItem; - RealVisibleCount: Integer; + RealVisibleCommandCount: Integer; begin inherited ConsistencyCheck; - RealVisibleCount:=0; + RealVisibleCommandCount:=0; for i:=0 to Count-1 do begin Item:=Items[i]; - Item.ConsistencyCheck; if Item.SectionIndex<>i then - RaiseGDBException(''); - if Item.VisibleActive then - inc(RealVisibleCount); + RaiseError(''); + Item.ConsistencyCheck; + if Item.Visible then begin + if Item is TIDEMenuCommand then + inc(RealVisibleCommandCount) + else if Item is TIDEMenuSection then + inc(RealVisibleCommandCount,TIDEMenuSection(Item).VisibleCommandCount); + end; + end; + if RealVisibleCommandCount<>VisibleCommandCount then + RaiseError('VisibleCommandCount='+dbgs(VisibleCommandCount)+' Real='+dbgs(RealVisibleCommandCount)); + + if NeedTopSeparator then begin + if TopSeparator=nil then + RaiseError(''); + end else begin + if TopSeparator<>nil then + RaiseError(''); + end; + + if NeedBottomSeparator then begin + if BottomSeparator=nil then + RaiseError(''); + end else begin + if BottomSeparator<>nil then + RaiseError(''); + end; + + if ChildrenAsSubMenu and (MenuItem<>nil) then begin + i:=0; + CheckContainerMenuItems(Self,i); end; - if RealVisibleCount<>VisibleCount then - RaiseGDBException(''); end; function TIDEMenuSection.GetItems(Index: Integer): TIDEMenuItem; @@ -1536,28 +1606,14 @@ begin Result:=TIDEMenuItem(FItems[Index]); end; -procedure TIDEMenuSection.SeparatorDestroy(Sender: TObject); +procedure TIDEMenuSection.OnSeparatorDestroy(Sender: TObject); begin if Sender=FTopSeparator then - FTopSeparator:=nil; - if Sender=FBottomSeparator then + FTopSeparator:=nil + else if Sender=FBottomSeparator then FBottomSeparator:=nil; end; -procedure TIDEMenuSection.FreeSeparators; -begin - if FNeedTopSeparator then begin - UpdateSize(-1); - FNeedTopSeparator:=false; - end; - FreeAndNil(FTopSeparator); - if FNeedBottomSeparator then begin - UpdateSize(-1); - FNeedBottomSeparator:=false; - end; - FreeAndNil(FBottomSeparator); -end; - procedure TIDEMenuSection.AddHandler(HandlerType: TIDEMenuSectionHandlerType; const AMethod: TMethod; AsLast: boolean); begin @@ -1582,11 +1638,13 @@ procedure TIDEMenuSection.SetMenuItem(const AValue: TMenuItem); begin if MenuItem=AValue then exit; inherited SetMenuItem(AValue); - Invalidate(0,Count-1); {$IFDEF VerboseMenuIntf} debugln('TIDEMenuSection.SetMenuItem Name="',Name,'"'); {$ENDIF} - UpdateMenuStructure; + if (MenuItem<>nil) and VisibleActive then begin + UpdateContainer; + UpdateSubMenus; + end; end; procedure TIDEMenuSection.SetChildrenAsSubMenu(const AValue: boolean); @@ -1594,16 +1652,24 @@ begin if FChildrenAsSubMenu=AValue then exit; FChildrenAsSubMenu:=AValue; ClearMenuItems; - if Section<>nil then begin - Section.Invalidate(SectionIndex,SectionIndex); - {$IFDEF VerboseMenuIntf} - debugln('TIDEMenuSection.SetChildrenAsSubMenu Name="',Name,'"'); - {$ENDIF} - if AValue then - Section.UpdateSize(1) - else - Section.UpdateSize(-1); - Section.UpdateMenuStructure; + {$IFDEF VerboseMenuIntf} + debugln(['TIDEMenuSection.SetChildrenAsSubMenu Name="',Name,'" ChildrenAsSubMenu=',ChildrenAsSubMenu]); + {$ENDIF} + if Section<>nil then + Section.UpdateContainer; + if ChildrenAsSubMenu then + UpdateContainer; + UpdateSubMenus; +end; + +procedure TIDEMenuSection.SetVisible(const AValue: Boolean); +begin + if AValue=Visible then exit; + inherited SetVisible(AValue); + if VisibleActive then begin + if ChildrenAsSubMenu then + UpdateContainer; + UpdateSubMenus; end; end; @@ -1687,6 +1753,55 @@ begin end; end; +constructor TIDEMenuCommand.Create(const TheName: string); +begin + inherited Create(TheName); + FVisibleCommandCount:=1; +end; + +procedure TIDEMenuCommand.ConsistencyCheck; + + procedure RaiseError; + var + s: String; + begin + s:='TIDEMenuItem.ConsistencyCheck Name="'+Name+'" Caption="'+DbgStr(Caption)+'"'; + debugln(s); + RaiseGDBException(s); + end; + +begin + inherited ConsistencyCheck; + if MenuItem<>nil then begin + if MenuItem.AutoCheck<>AutoCheck then + RaiseError; + if MenuItem.Checked<>Checked then + RaiseError; + if MenuItem.Default<>Default then + RaiseError; + if MenuItem.RadioItem<>RadioItem then + RaiseError; + if MenuItem.RightJustify<>RightJustify then + RaiseError; + if MenuItem.ShowAlwaysCheckable<>ShowAlwaysCheckable then + RaiseError; + if MenuItem.GroupIndex<>GroupIndex then + RaiseError; + if Command<>nil then begin + if MenuItem.ShortCut<>KeyToShortCut(Command.ShortcutA.Key1,Command.ShortcutA.Shift1) then + RaiseError; + if MenuItem.ShortCutKey2<>KeyToShortCut(Command.ShortcutA.Key2,Command.ShortcutA.Shift2) then + RaiseError; + end + else begin + if MenuItem.ShortCut<>0 then + RaiseError; + if MenuItem.ShortCutKey2<>0 then + RaiseError; + end; + end; +end; + { TIDEMenuRoots } function TIDEMenuRoots.GetItems(Index: integer): TIDEMenuSection; diff --git a/ide/etmessageframe.pas b/ide/etmessageframe.pas index c4fdfba227..a83b35a858 100644 --- a/ide/etmessageframe.pas +++ b/ide/etmessageframe.pas @@ -2753,7 +2753,7 @@ var VisibleCnt: Integer; begin MessagesMenuRoot.MenuItem:=MsgCtrlPopupMenu.Items; - MessagesMenuRoot.BeginUpdate; + //MessagesMenuRoot.BeginUpdate; try HasText:=false; HasFilename:=false; @@ -2891,7 +2891,7 @@ begin UpdateQuickFixes(Line); finally - MessagesMenuRoot.EndUpdate; + //MessagesMenuRoot.EndUpdate; end; end; diff --git a/ide/main.pp b/ide/main.pp index dce22dccab..5435a136e0 100644 --- a/ide/main.pp +++ b/ide/main.pp @@ -7876,7 +7876,7 @@ var begin ToolCount:=ExternalUserTools.Count; Section:=itmCustomTools; - Section.BeginUpdate; + //Section.BeginUpdate; try // add enough menuitems while Section.Count-1 0 then begin @@ -1847,25 +1847,25 @@ begin j := PtrUInt(EdList.Objects[i]); EditorCur := SourceEditorManager.SourceEditors[j]; if (EditorCur.GetProjectFile <> nil) and (EditorCur.GetProjectFile.IsPartOfProject) then begin - M := itmTabListProject; - CurMenuItem := GetMenuItem(ItemCountProject, M); + aSection := itmTabListProject; + CurMenuItem := GetMenuItem(ItemCountProject, aSection); inc(ItemCountProject); end else begin SourceEditorManager.OnPackageForSourceEditor(P, EditorCur); if P <> nil then begin s := Format(lisTabsFor, [p.Name]); if itmTabListPackage.FindByName(S) is TIDEMenuSection then - M := TIDEMenuSection(itmTabListPackage.FindByName(S)) + aSection := TIDEMenuSection(itmTabListPackage.FindByName(S)) else - M := RegisterIDESubMenu(itmTabListPackage, S, S); - CurMenuItem := GetMenuItem(M.Count, M); + aSection := RegisterIDESubMenu(itmTabListPackage, S, S); + CurMenuItem := GetMenuItem(aSection.Count, aSection); end else begin - M := itmTabListOther; - CurMenuItem := GetMenuItem(ItemCountOther, M); + aSection := itmTabListOther; + CurMenuItem := GetMenuItem(ItemCountOther, aSection); inc(ItemCountOther); end; end; - M.Visible := True; + aSection.Visible := True; if EditorCur.SharedEditorCount > 1 then CurMenuItem.Caption := EditorCur.PageName + ' ('+TForm(EditorCur.Owner).Caption+')' //CurMenuItem.Caption := EditorCur.PageName @@ -1873,9 +1873,9 @@ begin else CurMenuItem.Caption := EditorCur.PageName; if CurMenuItem.MenuItem <> nil then - CurMenuItem.MenuItem.Checked := SourceEditorManager.ActiveEditor = EditorCur; - if (SourceEditorManager.ActiveEditor = EditorCur) and (M.MenuItem <> nil) then - M.MenuItem.Checked := true; + CurMenuItem.Checked := SourceEditorManager.ActiveEditor = EditorCur; + if (SourceEditorManager.ActiveEditor = EditorCur) and (aSection.MenuItem <> nil) then + aSection.Checked := true; CurMenuItem.OnClick := @mnuWindowSourceItemClick; CurMenuItem.Tag := j; end; @@ -1884,8 +1884,8 @@ begin ClearMenuItem(ItemCountOther, itmTabListOther); for i := 0 to itmTabListPackage.Count - 1 do begin if itmTabListPackage.Items[i] is TIDEMenuSection then begin - M := itmTabListPackage.Items[i] as TIDEMenuSection; - M.Caption := M.Caption + Format(' (%d)', [M.Count]); + aSection := itmTabListPackage.Items[i] as TIDEMenuSection; + aSection.Caption := aSection.Caption + Format(' (%d)', [aSection.Count]); end; end; itmTabListProject.Caption := dlgEnvProject + Format(' (%d)', [itmTabListProject.Count]); diff --git a/ide/sourceeditor.pp b/ide/sourceeditor.pp index f681f2b0bc..659bfb6930 100644 --- a/ide/sourceeditor.pp +++ b/ide/sourceeditor.pp @@ -6523,7 +6523,7 @@ var begin PopM:=TPopupMenu(Sender); SourceTabMenuRoot.MenuItem:=PopM.Items; - SourceTabMenuRoot.BeginUpdate; + //SourceTabMenuRoot.BeginUpdate; try // Get the tab that was clicked if PopM.PopupComponent is TPageControl then begin @@ -6625,7 +6625,7 @@ begin end; finally - SourceTabMenuRoot.EndUpdate; + //SourceTabMenuRoot.EndUpdate; end; end; @@ -6661,7 +6661,7 @@ begin IDECommandList.ExecuteUpdateEvents; SourceEditorMenuRoot.MenuItem:=SrcPopupMenu.Items; - SourceEditorMenuRoot.BeginUpdate; + //SourceEditorMenuRoot.BeginUpdate; try RemoveUserDefinedMenuItems; RemoveContextMenuItems; @@ -6746,7 +6746,7 @@ begin Manager.OnPopupMenu(@AddContextPopupMenuItem); SourceEditorMenuRoot.NotifySubSectionOnShow(Self); finally - SourceEditorMenuRoot.EndUpdate; + //SourceEditorMenuRoot.EndUpdate; end; end; diff --git a/packager/packageeditor.pas b/packager/packageeditor.pas index 840657c766..e0a6315174 100644 --- a/packager/packageeditor.pas +++ b/packager/packageeditor.pas @@ -250,7 +250,7 @@ type procedure DisableI18NForLFMCheckBoxChange(Sender: TObject); procedure EditVirtualUnitMenuItemClick(Sender: TObject); procedure ExpandDirectoryMenuItemClick(Sender: TObject); - procedure FilterEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure FilterEditKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState); procedure FindInFilesMenuItemClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); @@ -798,7 +798,7 @@ begin //debugln(['TPackageEditorForm.FilesPopupMenuPopup START ',ItemsPopupMenu.Items.Count]); PackageEditorMenuFilesRoot.MenuItem:=ItemsPopupMenu.Items; //debugln(['TPackageEditorForm.FilesPopupMenuPopup START after connect ',ItemsPopupMenu.Items.Count]); - PackageEditorMenuRoot.BeginUpdate; + //PackageEditorMenuRoot.BeginUpdate; try CollectSelected; Writable := not LazPackage.ReadOnly; @@ -862,7 +862,7 @@ begin end; finally - PackageEditorMenuRoot.EndUpdate; + //PackageEditorMenuRoot.EndUpdate; end; FSingleSelectedFile := Nil; FSingleSelectedDep := Nil; @@ -936,7 +936,7 @@ var begin PackageEditorMenuRoot.MenuItem:=MorePopupMenu.Items; - PackageEditorMenuRoot.BeginUpdate; + //PackageEditorMenuRoot.BeginUpdate; try Writable:=(not LazPackage.ReadOnly); @@ -964,7 +964,7 @@ begin // under section PkgEditMenuSectionMisc SetItem(PkgEditMenuViewPackageSource,@ViewPkgSourceClick); finally - PackageEditorMenuRoot.EndUpdate; + //PackageEditorMenuRoot.EndUpdate; end; end; diff --git a/test/ideintf/testmenuintf.lfm b/test/ideintf/testmenuintf.lfm index 01e5767078..dbae60e509 100644 --- a/test/ideintf/testmenuintf.lfm +++ b/test/ideintf/testmenuintf.lfm @@ -1,9 +1,9 @@ -object TestMenuIntfForm: TTestMenuIntfForm +object TestMenuIntfDlg: TTestMenuIntfDlg Left = 287 Height = 237 Top = 215 Width = 541 - Caption = 'TestMenuIntfForm' + Caption = 'TestMenuIntfDlg' Menu = TestMainMenuIntf1 LCLVersion = '1.7' object TestMainMenuIntf1: TMainMenu diff --git a/test/ideintf/testmenuintf.pas b/test/ideintf/testmenuintf.pas index 6245cd77f3..395777ed99 100644 --- a/test/ideintf/testmenuintf.pas +++ b/test/ideintf/testmenuintf.pas @@ -1,4 +1,4 @@ -unit testmenuintf; +unit TestMenuIntf; {$mode objfpc}{$H+} @@ -10,9 +10,9 @@ uses type - { TTestMenuIntfForm } + { TTestMenuIntfDlg } - TTestMenuIntfForm = class(TForm) + TTestMenuIntfDlg = class(TForm) TestMainMenuIntf1: TMainMenu; TestPopupMenuIntf1: TPopupMenu; private @@ -22,13 +22,31 @@ type { TMenuIntfTest } TTestMenuIntf = class(TTestCase) + private + FDialog: TTestMenuIntfDlg; + FMainMenuRoot: TIDEMenuSection; + FPopupMenuRoot: TIDEMenuSection; + protected + procedure SetUp; override; + procedure TearDown; override; public + function CreateCommand(aParent: TIDEMenuSection; aName: string): TIDEMenuCommand; + function CreateSection(aParent: TIDEMenuSection; aName: string): TIDEMenuSection; + property Dialog: TTestMenuIntfDlg read FDialog; + property MainMenuRoot: TIDEMenuSection read FMainMenuRoot; + property PopupMenuRoot: TIDEMenuSection read FPopupMenuRoot; published - procedure TestMainMenu; + procedure TestEmptyMainMenu; + procedure TestSinglePopupMenu; + procedure TestPopupMenuList; + procedure TestPopupMenuLogicalSection; + procedure TestPopupMenuSubMenu; + procedure TestPopupMenuVisible; + procedure TestPopupMenuClearHiddenSection; end; var - TestMenuIntfForm: TTestMenuIntfForm; + TestMenuIntfDlg: TTestMenuIntfDlg; implementation @@ -37,9 +55,220 @@ implementation { TMenuIntfTest } -procedure TTestMenuIntf.TestMainMenu; +procedure TTestMenuIntf.SetUp; begin + inherited SetUp; + IDEMenuRoots:=TIDEMenuRoots.Create; + FMainMenuRoot:=RegisterIDEMenuRoot('MainMenuRoot'); + FPopupMenuRoot:=RegisterIDEMenuRoot('PopupMenuRoot'); + FDialog:=TTestMenuIntfDlg.Create(nil); +end; +procedure TTestMenuIntf.TearDown; +begin + FreeAndNil(FDialog); + FMainMenuRoot:=nil; + FPopupMenuRoot:=nil; + FreeAndNil(IDEMenuRoots); + inherited TearDown; +end; + +function TTestMenuIntf.CreateCommand(aParent: TIDEMenuSection; aName: string): TIDEMenuCommand; +begin + Result:=RegisterIDEMenuCommand(aParent,aName,aName); +end; + +function TTestMenuIntf.CreateSection(aParent: TIDEMenuSection; aName: string + ): TIDEMenuSection; +begin + Result:=RegisterIDEMenuSection(aParent,aName); +end; + +procedure TTestMenuIntf.TestEmptyMainMenu; +begin + FMainMenuRoot.MenuItem:=Dialog.TestMainMenuIntf1.Items; + MainMenuRoot.ConsistencyCheck; +end; + +procedure TTestMenuIntf.TestSinglePopupMenu; +begin + FPopupMenuRoot.MenuItem:=Dialog.TestPopupMenuIntf1.Items; + PopupMenuRoot.ConsistencyCheck; + RegisterIDEMenuCommand(PopupMenuRoot,'Help','Help'); + PopupMenuRoot.ConsistencyCheck; +end; + +procedure TTestMenuIntf.TestPopupMenuList; +var + i: Integer; +begin + FPopupMenuRoot.MenuItem:=Dialog.TestPopupMenuIntf1.Items; + PopupMenuRoot.ConsistencyCheck; + for i:=1 to 3 do begin + RegisterIDEMenuCommand(PopupMenuRoot,'Item'+IntToStr(i),'Item'+IntToStr(i)); + PopupMenuRoot.ConsistencyCheck; + end; +end; + +procedure TTestMenuIntf.TestPopupMenuLogicalSection; +var + Section1: TIDEMenuSection; +begin + FPopupMenuRoot.MenuItem:=Dialog.TestPopupMenuIntf1.Items; + PopupMenuRoot.ConsistencyCheck; + Section1:=RegisterIDEMenuSection(PopupMenuRoot,'Section1'); + PopupMenuRoot.ConsistencyCheck; + Section1.ChildrenAsSubMenu:=false; + PopupMenuRoot.ConsistencyCheck; + RegisterIDEMenuCommand(Section1,'Item1','Item1'); + PopupMenuRoot.ConsistencyCheck; +end; + +procedure TTestMenuIntf.TestPopupMenuSubMenu; +var + Section1: TIDEMenuSection; +begin + FPopupMenuRoot.MenuItem:=Dialog.TestPopupMenuIntf1.Items; + PopupMenuRoot.ConsistencyCheck; + Section1:=RegisterIDEMenuSection(PopupMenuRoot,'Section1'); + PopupMenuRoot.ConsistencyCheck; + RegisterIDEMenuCommand(Section1,'Item1','Item1'); + PopupMenuRoot.ConsistencyCheck; +end; + +procedure TTestMenuIntf.TestPopupMenuVisible; +var + LogSection1, SubMenu2, LogSection2: TIDEMenuSection; + Item1, Item2, Item3, Item4: TIDEMenuCommand; +begin + FPopupMenuRoot.MenuItem:=Dialog.TestPopupMenuIntf1.Items; + PopupMenuRoot.ConsistencyCheck; + + LogSection1:=RegisterIDEMenuSection(PopupMenuRoot,'LogSection1'); + PopupMenuRoot.ConsistencyCheck; + Item1:=RegisterIDEMenuCommand(LogSection1,'Item1','Item1'); + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection1.VisibleActive',true,LogSection1.VisibleActive); + AssertEquals('LogSection1.VisibleCommandCount',1,LogSection1.VisibleCommandCount); + + SubMenu2:=RegisterIDEMenuSection(PopupMenuRoot,'SubMenu2'); + PopupMenuRoot.ConsistencyCheck; + Item2:=RegisterIDEMenuCommand(SubMenu2,'Item2','Item2'); + PopupMenuRoot.ConsistencyCheck; + AssertEquals('SubMenu2.VisibleActive',true,SubMenu2.VisibleActive); + AssertEquals('SubMenu2.VisibleCommandCount',1,SubMenu2.VisibleCommandCount); + + LogSection2:=RegisterIDEMenuSection(PopupMenuRoot,'LogSection2'); + PopupMenuRoot.ConsistencyCheck; + Item3:=RegisterIDEMenuCommand(LogSection2,'Item3','Item3'); + Item4:=RegisterIDEMenuCommand(LogSection2,'Item4','Item4'); + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection2.VisibleActive',true,LogSection2.VisibleActive); + AssertEquals('LogSection2.VisibleCommandCount',2,LogSection2.VisibleCommandCount); + + // hide Item1 -> auto hides LogSection1 + Item1.Visible:=false; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection1.VisibleActive',false,LogSection1.VisibleActive); + AssertEquals('LogSection1.VisibleCommandCount',0,LogSection1.VisibleCommandCount); + + // show Item1 -> auto shows LogSection1 + Item1.Visible:=true; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection1.VisibleActive',true,LogSection1.VisibleActive); + AssertEquals('LogSection1.VisibleCommandCount',1,LogSection1.VisibleCommandCount); + + // hide Item2 -> auto hides SubMenu2 + Item2.Visible:=false; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('SubMenu2.VisibleActive',false,SubMenu2.VisibleActive); + AssertEquals('SubMenu2.VisibleCommandCount',0,SubMenu2.VisibleCommandCount); + + // show Item2 -> auto shows SubMenu2 + Item2.Visible:=true; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('SubMenu2.VisibleActive',true,SubMenu2.VisibleActive); + AssertEquals('SubMenu2.VisibleCommandCount',1,SubMenu2.VisibleCommandCount); + + // hide Item3, Item4 still visible + Item3.Visible:=false; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection2.VisibleActive',true,LogSection2.VisibleActive); + AssertEquals('LogSection2.VisibleCommandCount',1,LogSection2.VisibleCommandCount); + + // hide Item4 -> auto hide LogSection2 + Item4.Visible:=false; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection2.VisibleActive',false,LogSection2.VisibleActive); + AssertEquals('LogSection2.VisibleCommandCount',0,LogSection2.VisibleCommandCount); + + // show Item3 -> auto shows LogSection2 + Item3.Visible:=true; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection2.VisibleActive',true,LogSection2.VisibleActive); + AssertEquals('LogSection2.VisibleCommandCount',1,LogSection2.VisibleCommandCount); +end; + +procedure TTestMenuIntf.TestPopupMenuClearHiddenSection; +var + LogSection1, SubSection2: TIDEMenuSection; + Item2: TIDEMenuCommand; +begin + FPopupMenuRoot.MenuItem:=Dialog.TestPopupMenuIntf1.Items; + PopupMenuRoot.ConsistencyCheck; + + LogSection1:=RegisterIDEMenuSection(PopupMenuRoot,'LogSection1'); + PopupMenuRoot.ConsistencyCheck; + RegisterIDEMenuCommand(LogSection1,'Item1','Item1'); + PopupMenuRoot.ConsistencyCheck; + + SubSection2:=RegisterIDEMenuSection(LogSection1,'SubSection2'); + PopupMenuRoot.ConsistencyCheck; + Item2:=RegisterIDEMenuCommand(SubSection2,'Item2','Item2'); + PopupMenuRoot.ConsistencyCheck; + + writeln('TTestMenuIntf.TestPopupMenuClearHiddenSection START'); + AssertEquals('LogSection1.VisibleActive',true,LogSection1.VisibleActive); + AssertEquals('LogSection1.VisibleCommandCount',2,LogSection1.VisibleCommandCount); + AssertEquals('SubSection2.VisibleActive',true,SubSection2.VisibleActive); + AssertEquals('SubSection2.VisibleCommandCount',1,SubSection2.VisibleCommandCount); + + // hide SubSection2 -> LogSection1 looses one command + writeln('TTestMenuIntf.TestPopupMenuClearHiddenSection hide SubSection2 -> LogSection1 looses one command'); + SubSection2.Visible:=false; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection1.VisibleActive',true,LogSection1.VisibleActive); + AssertEquals('LogSection1.VisibleCommandCount',1,LogSection1.VisibleCommandCount); + AssertEquals('SubSection2.VisibleActive',false,SubSection2.VisibleActive); + AssertEquals('SubSection2.VisibleCommandCount',1,SubSection2.VisibleCommandCount); + + // hide Item2 -> no effect on LogSection1 + writeln('TTestMenuIntf.TestPopupMenuClearHiddenSection hide Item2 -> no effect on LogSection1'); + Item2.Visible:=false; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection1.VisibleActive',true,LogSection1.VisibleActive); + AssertEquals('LogSection1.VisibleCommandCount',1,LogSection1.VisibleCommandCount); + AssertEquals('SubSection2.VisibleActive',false,SubSection2.VisibleActive); + AssertEquals('SubSection2.VisibleCommandCount',0,SubSection2.VisibleCommandCount); + + // show Item2 -> no effect on LogSection1 + writeln('TTestMenuIntf.TestPopupMenuClearHiddenSection show Item2 -> no effect on LogSection1'); + Item2.Visible:=true; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection1.VisibleActive',true,LogSection1.VisibleActive); + AssertEquals('LogSection1.VisibleCommandCount',1,LogSection1.VisibleCommandCount); + AssertEquals('SubSection2.VisibleActive',false,SubSection2.VisibleActive); + AssertEquals('SubSection2.VisibleCommandCount',1,SubSection2.VisibleCommandCount); + + // clear SubSection2 -> no effect on LogSection1 + writeln('TTestMenuIntf.TestPopupMenuClearHiddenSection clear SubSection2 -> no effect on LogSection1'); + SubSection2.Clear; + Item2:=nil; + PopupMenuRoot.ConsistencyCheck; + AssertEquals('LogSection1.VisibleActive',true,LogSection1.VisibleActive); + AssertEquals('LogSection1.VisibleCommandCount',1,LogSection1.VisibleCommandCount); + AssertEquals('SubSection2.VisibleActive',false,SubSection2.VisibleActive); + AssertEquals('SubSection2.VisibleCommandCount',0,SubSection2.VisibleCommandCount); end; initialization diff --git a/test/runtestsgui.lpi b/test/runtestsgui.lpi index ef755c22e1..499846801e 100644 --- a/test/runtestsgui.lpi +++ b/test/runtestsgui.lpi @@ -107,8 +107,10 @@ - + + + diff --git a/test/runtestsgui.lpr b/test/runtestsgui.lpr index ffc51b4dc3..346c0b9feb 100644 --- a/test/runtestsgui.lpr +++ b/test/runtestsgui.lpr @@ -29,7 +29,7 @@ begin Application.Title:='Run Lazarus tests'; Application.Initialize; Application.CreateForm(TGuiTestRunner, TestRunner); - Application.CreateForm(TTestMenuIntfForm, TestMenuIntfForm); + Application.CreateForm(TTestMenuIntfDlg, TestMenuIntfDlg); Application.Run; end.