lazarus/components/dockedformeditor/source/dockedsourcewindow.pas
2021-05-11 12:19:23 +00:00

478 lines
15 KiB
ObjectPascal

{
*****************************************************************************
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Authors: Maciej Izak
Michael W. Vogel
This is the access to one SourceEditor window. There can be more then just one
(that list is SourceWindows).
Each SourceEditor window can hold a lot of units, forms etc. available per
SourceEditorWindowInterface in PageControlList.
}
unit DockedSourceWindow;
{$mode objfpc}{$H+}
{$modeswitch advancedrecords}
{$modeswitch typehelpers}
{ $define DEBUGDOCKEDFORMEDITOR}
interface
uses
// RTL
Classes, SysUtils, fgl,
// LCL
Forms, Controls, LCLProc,
// IDEIntf
SrcEditorIntf, LazIDEIntf, FormEditingIntf, ExtendedNotebook,
// DockedFormEditor
DockedDesignForm, DockedSourcePageControl,
DockedOptionsIDE, DockedTools;
type
{ TSourceWindow }
TSourceWindow = class
private
FActiveDesignForm: TDesignForm;
FDefaultNotebookPageChanged: TNotifyEvent;
FLastActiveSourceEditor: TSourceEditorInterface;
FLastTopParent: TControl;
FNotebook: TExtendedNotebook;
FPageControlList: TSourcePageControls;
FSourceWindowIntf: TSourceEditorWindowInterface;
function GetActiveEditor: TSourceEditorInterface;
procedure HookIntoOnPageChanged;
procedure NoteBookPageChanged(Sender: TObject);
procedure SetActiveDesignForm(const AValue: TDesignForm);
procedure UpdateEditorPageCaption(Sender: TObject);
public
constructor Create(ASourceWindowIntf: TSourceEditorWindowInterface);
destructor Destroy; override;
procedure AddPageCtrl(APageControl: TSourcePageControl);
procedure AdjustPageControl;
function FindPageControl(ASourceEditor: TSourceEditorInterface): TSourcePageControl;
procedure RemoveActiveDesignForm;
procedure RemovePageCtrl(ASourceEditor: TSourceEditorInterface);
public
property ActiveDesignForm: TDesignForm read FActiveDesignForm write SetActiveDesignForm;
property ActiveEditor: TSourceEditorInterface read GetActiveEditor;
property LastActiveSourceEditor: TSourceEditorInterface read FLastActiveSourceEditor write FLastActiveSourceEditor;
property LastTopParent: TControl read FLastTopParent write FLastTopParent;
property PageControlList: TSourcePageControls read FPageControlList;
property SourceWindowIntf: TSourceEditorWindowInterface read FSourceWindowIntf;
end;
{ TSourceWindows }
TSourceWindows = class(specialize TFPGList<TSourceWindow>)
private
FLastActiveSourceWindow: TSourceEditorWindowInterface;
function GetLastActivePageControl: TSourcePageControl;
function GetLastActiveSourceEditor: TSourceEditorInterface;
function GetSourceWindowIntf(ASrcEditor: TSourceWindow): TSourceEditorWindowInterface;
function GetSourceWindow(ASourceWindowIntf: TSourceEditorWindowInterface): TSourceWindow;
procedure SetLastActiveSourceEditor(AValue: TSourceEditorInterface);
public
constructor CreateNew;
destructor Destroy; override;
function Contains(ASourceWindowIntf: TSourceEditorWindowInterface): Boolean;
function Contains(ASrcEditor: TSourceWindow): Boolean;
procedure DeleteItem(Index: Integer);
function FindDesignForm(APageCtrl: TSourcePageControl): TDesignForm;
function FindPageControl(ASrcEditor: TSourceEditorInterface): TSourcePageControl;
function IndexOf(ASourceWindowIntf: TSourceEditorWindowInterface): Integer; overload;
function LastSourceEditorNotFound: Boolean;
procedure RefreshActivePageControls;
procedure RefreshAllPageControls;
procedure Remove(ASourceWindowIntf: TSourceEditorWindowInterface); overload;
procedure ShowCodeTabSkipCurrent(CurrentPageCtrl: TSourcePageControl; ADesignForm: TDesignForm);
public
property LastActiveSourceWindow: TSourceEditorWindowInterface read FLastActiveSourceWindow write FLastActiveSourceWindow;
property LastActiveSourceEditor: TSourceEditorInterface read GetLastActiveSourceEditor write SetLastActiveSourceEditor;
property LastActivePageControl: TSourcePageControl read GetLastActivePageControl;
property SourceWindowIntf[ASrcEditor: TSourceWindow]: TSourceEditorWindowInterface read GetSourceWindowIntf;
property SourceWindow[ASourceWindowIntf: TSourceEditorWindowInterface]: TSourceWindow read GetSourceWindow;
end;
var
SourceWindows: TSourceWindows;
implementation
{ TSourceWindow }
procedure TSourceWindow.HookIntoOnPageChanged;
var
i: Integer;
begin
for i := 0 to FSourceWindowIntf.ControlCount - 1 do
if FSourceWindowIntf.Controls[i] is TExtendedNotebook then
begin
FNotebook := TExtendedNotebook(FSourceWindowIntf.Controls[i]);
Break;
end;
if not Assigned(FNotebook) then Exit;
FDefaultNotebookPageChanged := FNotebook.OnChange;
FNotebook.OnChange := @NoteBookPageChanged;
end;
procedure TSourceWindow.NoteBookPageChanged(Sender: TObject);
var
LPageCtrl: TSourcePageControl;
begin
{$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TSourceWindow.NoteBookPageChanged SourceWindow[' + FSourceWindowIntf.Caption + ']'); {$ENDIF}
FDefaultNotebookPageChanged(Sender);
LPageCtrl := FindPageControl(ActiveEditor);
if not Assigned(LPageCtrl) then Exit;
if LPageCtrl.DesignerPageActive then
begin
LPageCtrl.AdjustPage;
{$IF DEFINED(LCLGtk2)}
LPageCtrl.DesignerSetFocusAsync;
{$ELSE}
LPageCtrl.DesignerSetFocus;
{$ENDIF}
end;
end;
function TSourceWindow.GetActiveEditor: TSourceEditorInterface;
begin
Result := FSourceWindowIntf.ActiveEditor;
end;
procedure TSourceWindow.SetActiveDesignForm(const AValue: TDesignForm);
var
LPageCtrl: TSourcePageControl;
begin
if FActiveDesignForm = AValue then Exit;
if FActiveDesignForm <> nil then
// don't hide now if soon form will be hidden (for example on the IDE start)
if not FActiveDesignForm.Hiding then
FActiveDesignForm.HideWindow;
FActiveDesignForm := AValue;
LPageCtrl := FindPageControl(ActiveEditor);
// important when we want back to tab where was oppened form
if (AValue <> nil) then
LazarusIDE.DoShowDesignerFormOfSrc(ActiveEditor);
if LPageCtrl = nil then Exit;
LPageCtrl.DesignForm := AValue;
end;
procedure TSourceWindow.UpdateEditorPageCaption(Sender: TObject);
var
LSourceEditor: TSourceEditorInterface;
LSourceWindowIntf: TSourceEditorWindowInterface;
begin
// if a unit is cloned to undocked empty source editor window, the SourcePageControl
// is not created, the only workaround I found is, to activate the new created
// source editor in this window
if not (Sender is TSourceEditorInterface) then Exit;
if SourceEditorManagerIntf.ActiveSourceWindow = nil then Exit;
LSourceEditor := TSourceEditorInterface(Sender);
{$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TSourceWindow.UpdateEditorPageCaption [' + SourceWindowCaption(LSourceEditor) + ']'); {$ENDIF}
LSourceWindowIntf := SourceWindowGet(LSourceEditor);
if not Assigned(LSourceWindowIntf)
or (SourceEditorManagerIntf.ActiveSourceWindow = LSourceWindowIntf)
or (SourceWindows.LastActiveSourceWindow = LSourceWindowIntf)
then
Exit;
LSourceWindowIntf.ActiveEditor := LSourceEditor;
end;
constructor TSourceWindow.Create(ASourceWindowIntf: TSourceEditorWindowInterface);
begin
FLastActiveSourceEditor := nil;
FSourceWindowIntf := ASourceWindowIntf;
FPageControlList := TSourcePageControls.Create;
FNotebook := nil;
HookIntoOnPageChanged;
FSourceWindowIntf.AddUpdateEditorPageCaptionHandler(@UpdateEditorPageCaption);
end;
destructor TSourceWindow.Destroy;
begin
if Assigned(FNotebook) then
FNotebook.OnChange := FDefaultNotebookPageChanged;
FPageControlList.Free;
inherited Destroy;
end;
procedure TSourceWindow.AddPageCtrl(APageControl: TSourcePageControl);
begin
FPageControlList.Add(APageControl);
end;
procedure TSourceWindow.AdjustPageControl;
var
LPageCtrl: TSourcePageControl;
begin
LPageCtrl := FindPageControl(ActiveEditor);
if LPageCtrl <> nil then
LPageCtrl.AdjustPage;
end;
function TSourceWindow.FindPageControl(ASourceEditor: TSourceEditorInterface): TSourcePageControl;
var
LParent: TWinControl;
begin
if ASourceEditor = nil then
Exit(nil);
LParent := ASourceEditor.EditorControl.Parent;
while LParent <> nil do
begin
if LParent is TSourcePageControl then
Exit(TSourcePageControl(LParent));
LParent := LParent.Parent;
end;
Result := nil;
end;
procedure TSourceWindow.RemoveActiveDesignForm;
begin
FActiveDesignForm := nil;
end;
procedure TSourceWindow.RemovePageCtrl(ASourceEditor: TSourceEditorInterface);
begin
FPageControlList.Remove(ASourceEditor);
if LastActiveSourceEditor = ASourceEditor then
LastActiveSourceEditor := nil;
end;
{ TSourceWindows }
function TSourceWindows.GetSourceWindowIntf(ASrcEditor: TSourceWindow): TSourceEditorWindowInterface;
var
LIndex: Integer;
begin
LIndex := IndexOf(ASrcEditor);
if LIndex >= 0 then
Result := Items[LIndex].SourceWindowIntf
else
Result := nil;
end;
function TSourceWindows.GetLastActivePageControl: TSourcePageControl;
begin
Result := FindPageControl(LastActiveSourceEditor);
end;
function TSourceWindows.GetLastActiveSourceEditor: TSourceEditorInterface;
var
LSourceWindow: TSourceWindow;
begin
Result := nil;
if not Assigned(LastActiveSourceWindow) then Exit;
LSourceWindow := SourceWindow[LastActiveSourceWindow];
if not Assigned(LSourceWindow) then Exit;
Result := LSourceWindow.LastActiveSourceEditor;
end;
function TSourceWindows.GetSourceWindow(ASourceWindowIntf: TSourceEditorWindowInterface): TSourceWindow;
var
LIndex: Integer;
begin
LIndex := IndexOf(ASourceWindowIntf);
if LIndex >= 0 then
Result := Items[LIndex]
else
Result := nil;
end;
procedure TSourceWindows.SetLastActiveSourceEditor(AValue: TSourceEditorInterface);
var
LSourceWindow: TSourceWindow;
begin
if not Assigned(LastActiveSourceWindow) then Exit;
LSourceWindow := SourceWindow[LastActiveSourceWindow];
if not Assigned(LSourceWindow) then Exit;
LSourceWindow.LastActiveSourceEditor := AValue;
end;
constructor TSourceWindows.CreateNew;
begin
inherited Create;
FLastActiveSourceWindow := nil;
end;
destructor TSourceWindows.Destroy;
begin
while Count > 0 do
DeleteItem(0);
inherited Destroy;
end;
function TSourceWindows.Contains(ASourceWindowIntf: TSourceEditorWindowInterface): Boolean;
begin
Result := IndexOf(ASourceWindowIntf) >= 0;
end;
function TSourceWindows.Contains(ASrcEditor: TSourceWindow): Boolean;
begin
Result := IndexOf(ASrcEditor) >= 0;
end;
procedure TSourceWindows.DeleteItem(Index: Integer);
var
LSourceWindow: TSourceWindow;
begin
LSourceWindow := Items[Index];
LSourceWindow.Free;
Delete(Index);
end;
function TSourceWindows.FindDesignForm(APageCtrl: TSourcePageControl): TDesignForm;
var
LSourceWindow: TSourceWindow;
LSourceEditorInterface: TSourceEditorInterface;
begin
Result := nil;
if APageCtrl = nil then Exit;
for LSourceWindow in Self do
begin
if APageCtrl.Owner = LSourceWindow.SourceWindowIntf then
begin
LSourceEditorInterface := LSourceWindow.ActiveEditor;
if LSourceEditorInterface = nil then Exit;
Result := DesignForms.Find(LSourceEditorInterface.GetDesigner(True));
Exit;
end;
end;
end;
function TSourceWindows.FindPageControl(ASrcEditor: TSourceEditorInterface): TSourcePageControl;
var
LSourceWindow: TSourceWindow;
begin
Result := nil;
for LSourceWindow in Self do
if LSourceWindow.PageControlList.Contains(ASrcEditor) then
Exit(LSourceWindow.PageControlList.PageControl[ASrcEditor]);
end;
function TSourceWindows.IndexOf(ASourceWindowIntf: TSourceEditorWindowInterface): Integer;
var
i: Integer;
begin
Result := -1;
for i := 0 to Count - 1 do
if Items[i].SourceWindowIntf = ASourceWindowIntf then
Exit(i);
end;
function TSourceWindows.LastSourceEditorNotFound: Boolean;
var
i: Integer;
LPageCtrl: TSourcePageControl;
LSourceWindow: TSourceWindow;
begin
if (LastActiveSourceWindow = nil) or (LastActiveSourceEditor = nil) then
Exit(False);
LSourceWindow := SourceWindow[LastActiveSourceWindow];
for LPageCtrl in LSourceWindow.PageControlList do
begin
Result := True;
for i := 0 to LastActiveSourceWindow.Count - 1 do
if LPageCtrl.SourceEditor = LastActiveSourceWindow.Items[i] then
begin
Result := False;
Break;
end;
if Result then
begin
// after moving code editor into other window, sometimes IDE switch to other tab
// this line prevent this.
LSourceWindow.LastActiveSourceEditor := LPageCtrl.SourceEditor;
Exit;
end;
end;
Result := False;
end;
procedure TSourceWindows.RefreshActivePageControls;
var
LSourceWindow: TSourceWindow;
LPageCtrl: TSourcePageControl;
begin
for LSourceWindow in Self do
begin
LPageCtrl := LSourceWindow.FindPageControl(LSourceWindow.ActiveEditor);
// for example LPageCtrl is nil when we clone source to new window
if (LPageCtrl = nil) or (csDestroying in LSourceWindow.SourceWindowIntf.ComponentState) then
Continue;
if (LSourceWindow.ActiveEditor = nil)
or (LSourceWindow.ActiveEditor.GetDesigner(True) <> nil)
then
LPageCtrl.RemoveDesignPages
else
if Assigned(LPageCtrl.DesignForm) then
begin
LPageCtrl.CreateTabSheetDesigner;
if not (LPageCtrl.DesignForm.Form is TNonControlProxyDesignerForm) then
LPageCtrl.CreateTabSheetAnchors;
end;
end;
end;
procedure TSourceWindows.RefreshAllPageControls;
var
LSourceWindow: TSourceWindow;
LPageCtrl: TSourcePageControl;
begin
for LSourceWindow in SourceWindows do
for LPageCtrl in LSourceWindow.PageControlList do
begin
LPageCtrl.TabPosition := DockedOptions.TabPosition;
LPageCtrl.RefreshResizer;
if not DockedOptions.AnchorTabVisible then
LPageCtrl.RemoveTabSheetAnchors;
end;
end;
procedure TSourceWindows.Remove(ASourceWindowIntf: TSourceEditorWindowInterface);
var
LIndex: Integer;
begin
LIndex := IndexOf(ASourceWindowIntf);
if LIndex < 0 then Exit;
DeleteItem(LIndex);
if LastActiveSourceWindow = ASourceWindowIntf then
LastActiveSourceWindow := nil;
end;
procedure TSourceWindows.ShowCodeTabSkipCurrent(CurrentPageCtrl: TSourcePageControl; ADesignForm: TDesignForm);
var
LSourceWindow: TSourceWindow;
LPageCtrl: TSourcePageControl;
begin
for LSourceWindow in Self do
for LPageCtrl in LSourceWindow.PageControlList do
if LPageCtrl = CurrentPageCtrl then
begin
LPageCtrl.DesignForm := ADesignForm;
LPageCtrl.InitPage;
end else
if LPageCtrl.DesignForm = ADesignForm then
LPageCtrl.ShowCode;
end;
initialization
SourceWindows := TSourceWindows.CreateNew;
finalization
SourceWindows.Free;
end.