lazarus/ide/comppagespopup.pas

349 lines
10 KiB
ObjectPascal

{
***************************************************************************
* *
* 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 <http://www.gnu.org/copyleft/gpl.html>. 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.