{ *************************************************************************** * * * This source is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This code is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * A copy of the GNU General Public License is available on the World * * Wide Web at . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. * * * *************************************************************************** Abstract: A drop-down list with all component tab names. Allows selection of any tab, including the ones not visible in tab control. This list is opened from a button at the right edge of component palette. This file is originally part of CodeTyphon Studio (http://www.pilotlogic.com/). They improved Lazarus GPL code with this feature, then it was copied and backported to Lazarus. Later it was modified with a different button etc. } unit CompPagesPopup; {$mode objfpc}{$H+} interface uses Classes, SysUtils, math, // LCL LCLIntf, LCLType, LMessages, Forms, Controls, ComCtrls, ExtCtrls, Graphics, Dialogs, Buttons, // LazUtils LazLoggerBase, LazUTF8, // IdeIntf IDEImagesIntf, MenuIntf, // IDE LazarusIDEStrConsts, ComponentPalette_Options, MainBase, MainBar; type { TDlgCompPagesPopup } TDlgCompPagesPopup = class(TForm) cBtnClose: TSpeedButton; Panel1: TPanel; Panel2: TPanel; TreeView1: TTreeView; procedure cBtnCloseClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDeactivate(Sender: TObject); procedure FormShow(Sender: TObject); procedure TreeView1Click(Sender: TObject); private fViewAllNode, fOptionsNode: TTreeNode; fGroups: TStringListUTF8Fast; // Objects have group TreeNodes fLastCloseUp: QWord; fLastCanShowCheck: Boolean; procedure AppDeactivated(Sender: TObject); procedure FindGroups; procedure BuildTreeItem(aPageCapt: string); procedure BuildList; protected procedure WMActivate(var Message : TLMActivate); message LM_ACTIVATE; procedure DoCreate; override; procedure DoClose(var CloseAction: TCloseAction); override; public PositionForControl: TControl; destructor Destroy; override; procedure FixBounds; procedure CanShowCheck; property LastCanShowCheck: Boolean read fLastCanShowCheck; end; var DlgCompPagesPopup: TDlgCompPagesPopup; implementation {$R *.lfm} function FirstWord(aStr: string): string; var spPos: integer; begin spPos := Pos(' ', aStr); if spPos > 0 then Result := Copy(aStr, 1, spPos-1) else Result := ''; end; { TDlgCompPagesPopup } procedure TDlgCompPagesPopup.FormShow(Sender: TObject); begin BuildList; Application.AddOnDeactivateHandler(@AppDeactivated); end; procedure TDlgCompPagesPopup.FormDeactivate(Sender: TObject); begin Close; end; procedure TDlgCompPagesPopup.cBtnCloseClick(Sender: TObject); begin Close; end; procedure TDlgCompPagesPopup.FormCreate(Sender: TObject); begin IDEImages.AssignImage(cBtnClose, 'menu_close'); TreeView1.Images := IDEImages.Images_16; {TIDEImages.AddImageToImageList(ImageList1, 'item_package'); TIDEImages.AddImageToImageList(ImageList1, 'pkg_open');} end; procedure TDlgCompPagesPopup.DoClose(var CloseAction: TCloseAction); begin inherited DoClose(CloseAction); Application.RemoveOnDeactivateHandler(@AppDeactivated); if CloseAction = caHide then fLastCloseUp := GetTickCount64; end; destructor TDlgCompPagesPopup.Destroy; begin Application.RemoveOnDeactivateHandler(@AppDeactivated); inherited Destroy; end; procedure TDlgCompPagesPopup.DoCreate; begin inherited DoCreate; fLastCanShowCheck := True; end; procedure TDlgCompPagesPopup.FixBounds; var Mon: TRect; zPos: TPoint; margin, y: Integer; begin zPos:=point(PositionForControl.Width div 2,PositionForControl.Height); zPos:=PositionForControl.ClientToScreen(zPos); Mon := Screen.MonitorFromPoint(zPos).WorkareaRect; Self.Height := TreeView1.Items.GetLastSubNode.Top + TreeView1.Items.GetLastSubNode.Height + TreeView1.Top + 20; Self.Left := zPos.x - Self.Width div 2; Self.Top:= zPos.y; if (self.Left) < Mon.Left then Self.Left := Mon.Left; if (self.Left+self.Width) > Mon.Left + Mon.Width then Self.Left := Max(Mon.Left, Mon.Left + Mon.Width - Self.Width); margin := Min(Scale96ToScreen(150), PositionForControl.Top + PositionForControl.Height); if margin > Mon.Height div 4 then margin := 0; if (self.Height + margin) > Mon.Height then self.Height := Mon.Height - margin; if (self.Top+self.Height) > Mon.Top + Mon.Height then begin y := zPos.y - PositionForControl.Height - Self.Height; if y > Mon.Top then begin Self.Top := y // show above button end else begin // overlap button, try to go right or left of it Self.Top := Mon.Top + Mon.Height - Self.Height; if zPos.x + PositionForControl.Width div 2 <= Mon.Left + Mon.Width - Self.Width then Self.Left := zPos.x + PositionForControl.Width div 2 else; Self.Left := Max(Mon.Left, zPos.x - PositionForControl.Width div 2 - Self.Width); end; end; end; procedure TDlgCompPagesPopup.TreeView1Click(Sender: TObject); var i: integer; SelNode: TTreeNode; e: TIDEMenuItem; begin SelNode:=TreeView1.Selected; if (SelNode=nil) or (SelNode.ImageIndex=1) then exit; if (SelNode.Data <> nil) then begin e := TIDEMenuItem(SelNode.Data); Close; e.DoOnClick; exit; end; if SelNode=fViewAllNode then MainIDE.DoShowComponentList else if SelNode=fOptionsNode then MainIDE.DoOpenIDEOptions(TCompPaletteOptionsFrame, '', [], []) else with MainIDEBar do if Assigned(ComponentPageControl) and (ComponentPageControl.PageCount>0) then for i:=0 to ComponentPageControl.PageCount-1 do if SameText(SelNode.Text, ComponentPageControl.Page[i].Caption) then begin ComponentPageControl.PageIndex:=i; ComponentPageControl.OnChange(Self); Break; end; Close; end; procedure TDlgCompPagesPopup.WMActivate(var Message: TLMActivate); begin {$IFDEF LCLWin32} //activate the mainform to simulate a true popup-window (works only on Windows) if Assigned(PopupParent) and PopupParent.HandleAllocated then SendMessage(PopupParent.Handle, LM_NCACTIVATE, Ord(Message.Active <> WA_INACTIVE), 0); {$ENDIF} inherited WMActivate(Message); end; procedure TDlgCompPagesPopup.FindGroups; // Find groups. Page names with many words are grouped by the first word. var i, grpIndex: integer; Word1: string; begin for i:=0 to MainIDEBar.ComponentPageControl.PageCount-1 do begin Word1 := FirstWord(MainIDEBar.ComponentPageControl.Page[i].Caption); if (Word1 <> '') and (Word1 <> 'Data') then // "Data" is an exception begin grpIndex := fGroups.IndexOf(Word1); if grpIndex > -1 then // Found, mark as group. TreeNode will be created later. fGroups.Objects[grpIndex] := nil else // Will be a group only if other members are found. fGroups.AddObject(Word1, Self); // <>nil means a single item now. end; end; // Delete single items (marked with "1") from groups list. for i := fGroups.Count-1 downto 0 do if Assigned(fGroups.Objects[i]) then fGroups.Delete(i); end; procedure TDlgCompPagesPopup.AppDeactivated(Sender: TObject); begin Close; end; procedure TDlgCompPagesPopup.BuildTreeItem(aPageCapt: string); // Create items in tree, grouping as needed. var grInd: integer; Word1: string; GroupNode, ItemNode: TTreeNode; begin GroupNode := Nil; Word1 := FirstWord(aPageCapt); if Word1 <> '' then begin grInd := fGroups.IndexOf(Word1); if grInd > -1 then // Group found begin if Assigned(fGroups.Objects[grInd]) then GroupNode := TTreeNode(fGroups.Objects[grInd]) else begin GroupNode := TreeView1.Items.AddChild(nil, Word1+' pages'); fGroups.Objects[grInd] := GroupNode; end; end; end; ItemNode:=TreeView1.Items.AddChild(GroupNode, aPageCapt); ItemNode.ImageIndex:=IDEImages.GetImageIndex('item_package'); ItemNode.SelectedIndex:=0; end; procedure TDlgCompPagesPopup.CanShowCheck; begin fLastCanShowCheck := not Visible and (GetTickCount64 > fLastCloseUp + 100); end; procedure TDlgCompPagesPopup.BuildList; var i: integer; e: TIDEMenuItem; n: TTreeNode; begin TreeView1.BeginUpdate; TreeView1.Items.Clear; fViewAllNode:=nil; fOptionsNode:=nil; if MainIDEBar.ComponentPageControl=nil then begin TreeView1.Items.AddChild(nil,'Sorry, No Pages'); Exit; end; fGroups := TStringListUTF8Fast.Create; try FindGroups; for i:=0 to MainIDEBar.ComponentPageControl.PageCount-1 do BuildTreeItem(MainIDEBar.ComponentPageControl.Page[i].Caption); finally fGroups.Free; end; // add 'View all' fViewAllNode:=TreeView1.Items.AddChild(nil, lisCompPalComponentList); fViewAllNode.ImageIndex:=IDEImages.GetImageIndex('menu_view_components'); fViewAllNode.SelectedIndex:=fViewAllNode.ImageIndex; for i := 0 to ComponentPalettePageDropDownExtraEntries.Count - 1 do begin e := ComponentPalettePageDropDownExtraEntries.Items[i]; n := TreeView1.Items.AddChildObject(nil, e.Caption, Pointer(e)); n.ImageIndex := e.ImageIndex; end; // add 'Options' fOptionsNode:=TreeView1.Items.AddChild(nil, lisMenuGeneralOptions); fOptionsNode.ImageIndex:=IDEImages.LoadImage('menu_environment_options'); fOptionsNode.SelectedIndex:=fOptionsNode.ImageIndex; TreeView1.EndUpdate; TreeView1.FullExpand; Panel2.Caption:=Format(lisTotalPages, [IntToStr(MainIDEBar.ComponentPageControl.PageCount)]); FixBounds; end; end.