
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7211 8e941d3f-bd1b-0410-a28a-d453659cc2b4
352 lines
9.3 KiB
ObjectPascal
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.
|
|
|