lazarus-ccr/components/manualdock/mandocking.pas
2019-12-18 20:42:52 +00:00

352 lines
9.3 KiB
ObjectPascal

unit mandocking;
{$mode objfpc}{$H+}
interface
uses
Types,
Forms,
SysUtils,
Controls,
ExtCtrls,
ProjectIntf,
LazIDEIntf,
MenuIntf,
IDEMsgIntf,
SrcEditorIntf,
XMLConf;
procedure Register;
resourcestring
mnuDockMsgWindow = 'Dock "Messages" window';
implementation
type
TDockState = record
Docked : Boolean;
FloatRect : TRect;
FloatBrd : TFormBorderStyle;
DockSize : TSize;
EverDocked : Boolean; // if "docking" has even been since start of IDE.
end;
{ TManualDocker }
TManualDocker = class(TObject)
private
FCurrentSrcWin : TWinControl;
protected
procedure AdjustControlsOrder;
function DoChangeDocking(DockingEnabled: Boolean): Boolean;
procedure LoadState(cfg: TXMLConfig; var Astate: TDockState; const StateName: string);
procedure SaveState(cfg: TXMLConfig; const Astate: TDockState; const StateName: string);
procedure LoadStates;
procedure SaveStates;
procedure AllocControls(AParent: TWinControl);
procedure DeallocControls;
procedure ReallocControls;
procedure UpdateDockState(var astate: TDockState; wnd: TWinControl);
procedure DoPanelResize;
procedure PanelResize(Sender: TObject);
procedure SourceWindowCreated(Sender: TObject);
procedure SourceWindowDestroyed(Sender: TObject);
public
ConfigPath : AnsiString;
split : TSplitter;
panel : TPanel;
MsgWnd : TDockState;
constructor Create;
destructor Destroy; override;
procedure OnCmdClick(Sender: TObject);
function OnProjectOpen(Sender: TObject; AProject: TLazProject): TModalResult;
end;
var
cmd : TIDEMenuCommand = nil;
docker : TManualDocker = nil;
const
DockCfgRoot = 'ManualDockConfig';
DockCfgXML = 'manualdockconfig.xml';
MsgDockedName = 'Messages';
{ TManualDocker }
function SafeRect(const c: TREct; MinWidth, MinHeight: Integer): TRect;
begin
Result := c;
if Result.Top < 0 then Result.Top := 0;
if Result.Left < 0 then Result.Left := 0;
if c.Right - c.Left < MinWidth then Result.Right := Result.Left + MinWidth;
if c.Bottom - c.Top < MinHeight then Result.Bottom := Result.Top + MinHeight;
end;
function Max(a, b: Integer): Integer;
begin
if a > b then Result := a
else Result := b;
end;
procedure TManualDocker.AdjustControlsOrder;
var
bar : TControl;
i:Integer;
begin
if not Assigned(FCurrentSrcWin) then Exit;
//HACK: the actual "bottom" controls are unknown!
bar:=nil;
for i:=0 to FCurrentSrcWin.ControlCount-1 do
if ((FCurrentSrcWin.Controls[i].Name='StatusBar') or (FCurrentSrcWin.Controls[i].ClassName='TStatusBar')) then begin
bar:=FCurrentSrcWin.Controls[i];
Break;
end;
split.Top:=bar.Top;
panel.Top:=split.Height+split.Top;
if Assigned(bar) then bar.Top:=panel.Top+panel.Height;
end;
function TManualDocker.DoChangeDocking(DockingEnabled:Boolean):Boolean;
begin
if DockingEnabled then begin
Result:=False;
if not (Assigned(SourceEditorManagerIntf) and Assigned(SourceEditorManagerIntf.ActiveSourceWindow))
or not Assigned(IDEMessagesWindow)
then Exit;
if not Assigned(panel) then
AllocControls(SourceEditorManagerIntf.ActiveSourceWindow);
if IDEMessagesWindow.Parent = nil then begin
MsgWnd.FloatRect := IDEMessagesWindow.BoundsRect;
MsgWnd.FloatBrd := IDEMessagesWindow.BorderStyle;
end;
panel.visible:=true;
split.visible:=true;
panel.Height:=MsgWnd.DockSize.cy;
AdjustControlsOrder;
IDEMessagesWindow.BorderStyle := bsNone;
IDEMessagesWindow.Parent := panel;
// LCL fails with "infinite resize loop", using manual size adjustement instead
panel.OnResize:=@PanelResize;
DoPanelResize;
IDEMessagesWindow.TabStop := False;
{ // this code has been used to keep the cursor back to the source code
// whenever IDE is focused back, instead of focusing a compiler message
for i := 0 to IDEMessagesWindow.ControlCount - 1 do
if IDEMessagesWindow.Controls[i] is TWinControl then
TWinControl(IDEMessagesWindow.Controls[i]).TabStop := False;
}
Result:=True;
MsgWnd.EverDocked:=True;
end else if MsgWnd.EverDocked then begin
if Assigned(panel) then begin
panel.visible := False;
UpdateDockState(MsgWnd, panel);
end;
if Assigned(split) then split.visible := False;
IDEMessagesWindow.Parent := nil;
IDEMessagesWindow.BoundsRect := SafeRect(MsgWnd.FloatRect,
Max(30, IDEMessagesWindow.ClientWidth), Max(30, IDEMessagesWindow.ClientHeight));
IDEMessagesWindow.BorderStyle := MsgWnd.FloatBrd;
IDEMessagesWindow.TabStop := true;
IDEMessagesWindow.Show;
Result:=True;
end else
Result:=true;
end;
constructor TManualDocker.Create;
var
pths : array [0..1] of String;
i : Integer;
begin
if SourceEditorManagerIntf <> nil then begin
SourceEditorManagerIntf.RegisterChangeEvent(semWindowCreate, @SourceWindowCreated);
SourceEditorManagerIntf.RegisterChangeEvent(semWindowDestroy, @SourceWindowDestroyed);
end;
pths[0]:= LazarusIDE.GetPrimaryConfigPath;
pths[1]:= LazarusIDE.GetSecondaryConfigPath;
for i := 0 to length(pths)-1 do begin
try
ConfigPath := IncludeTrailingPathDelimiter(pths[i])+DockCfgXML;
LoadStates;
Break;
except
end;
end;
MsgWnd.FloatBrd := bsToolWindow;
end;
destructor TManualDocker.Destroy;
begin
if Assigned(panel) then UpdateDockState(MsgWnd, panel);
SaveStates;
DeallocControls;
inherited Destroy;
end;
procedure TManualDocker.OnCmdClick(Sender: TObject);
var
NeedDocking: Boolean;
begin
NeedDocking:=not Cmd.Checked;
DoChangeDocking(NeedDocking);
MsgWnd.docked:=NeedDocking;
cmd.Checked:=NeedDocking;
end;
function TManualDocker.OnProjectOpen(Sender: TObject; AProject: TLazProject): TModalResult;
begin
DoChangeDocking(MsgWnd.Docked);
Result := mrOK;
end;
function CreateXMLConfig(const xmlfile: string) : TXMLConfig;
begin
Result := TXMLConfig.Create(nil);
Result.RootName := DockCfgRoot;
Result.Filename := xmlfile;
end;
procedure TManualDocker.AllocControls(AParent: TWinControl);
begin
FCurrentSrcWin := AParent;
panel := TPanel.Create(FCurrentSrcWin);
panel.BorderStyle := bsNone;
panel.Align:=alBottom;
FCurrentSrcWin.InsertControl(panel);
split := TSplitter.Create(AParent);
split.Align:=alBottom;
FCurrentSrcWin.InsertControl(split);
end;
procedure TManualDocker.DeallocControls;
begin
split:=nil;
panel:=nil;
end;
procedure TManualDocker.ReallocControls;
begin
end;
procedure TManualDocker.UpdateDockState(var astate: TDockState; wnd: TWinControl);
begin
astate.DockSize.cx := wnd.ClientWidth;
astate.DockSize.cy := wnd.ClientHeight;
end;
procedure TManualDocker.DoPanelResize;
begin
IDEMessagesWindow.BoundsRect:=panel.ClientRect;
end;
procedure TManualDocker.PanelResize(Sender: TObject);
begin
if not Assigned(panel) or not Assigned(IDEMessagesWindow) or not MsgWnd.Docked then Exit;
DoPanelResize;
end;
procedure TManualDocker.SourceWindowCreated(Sender: TObject);
begin
if Assigned(FCurrentSrcWin) or (SourceEditorManagerIntf.SourceWindowCount > 1) then
Exit;
if MsgWnd.Docked then DoChangeDocking(true);
end;
procedure TManualDocker.SourceWindowDestroyed(Sender: TObject);
begin
if FCurrentSrcWin <> Sender then Exit;
DoChangeDocking(False);
DeallocControls;
FCurrentSrcWin := nil;
// avoid re-docking to the window being destroyed
if MsgWnd.Docked and (SourceEditorManagerIntf.ActiveSourceWindow<>Sender) then
DoChangeDocking(True);
end;
procedure TManualDocker.LoadState(cfg: TXMLConfig; var Astate: TDockState;
const StateName: string);
var
nm : UnicodeString;
begin
nm := UTF8Decode(StateName);
AState.Docked := cfg.GetValue(nm+'/docked', False);
AState.FloatRect.Left := cfg.GetValue(nm+'/float/left', -1);
AState.FloatRect.Top := cfg.GetValue(nm+'/float/top', -1);
AState.FloatRect.Right := cfg.GetValue(nm+'/float/right', -1);
AState.FloatRect.Bottom := cfg.GetValue(nm+'/float/bottom', -1);
AState.DockSize.cx := cfg.GetValue(nm+'/docked/cx', 30);
AState.DockSize.cy := cfg.GetValue(nm+'/docked/cy', 50);
end;
procedure TManualDocker.SaveState(cfg: TXMLConfig; const Astate: TDockState; const StateName: string);
var
nm : UnicodeString;
begin
nm:=UTF8Decode(StateName);
cfg.SetValue(nm+'/docked', AState.Docked);
cfg.SetValue(nm+'/float/left', AState.FloatRect.Left);
cfg.SetValue(nm+'/float/top', AState.FloatRect.Top);
cfg.SetValue(nm+'/float/right', AState.FloatRect.Right);
cfg.SetValue(nm+'/float/bottom', AState.FloatRect.Bottom);
cfg.SetValue(nm+'/docked/cx', AState.DockSize.cx);
cfg.SetValue(nm+'/docked/cy', AState.DockSize.cy)
end;
procedure TManualDocker.LoadStates;
var
cfg : TXMLConfig;
begin
cfg := CreateXMLConfig(ConfigPath);
try
LoadState(cfg, MsgWnd, MsgDockedName)
finally
cfg.Free;
end;
end;
procedure TManualDocker.SaveStates;
var
cfg : TXMLConfig;
begin
cfg := CreateXMLConfig(ConfigPath);
try
try
SaveState(cfg, MsgWnd, MsgDockedName)
finally
cfg.Free;
end;
except
end;
end;
procedure Register;
begin
docker := TManualDocker.Create;
cmd := RegisterIDEMenuCommand(itmViewMainWindows, 'makeMessagesDocked', mnuDockMsgWindow, @docker.OnCmdClick, nil, nil, '');
cmd.Checked:=docker.MsgWnd.Docked;
LazarusIDE.AddHandlerOnProjectOpened(@docker.OnProjectOpen, False);
end;
initialization
finalization
docker.Free;
end.