{
 *****************************************************************************
  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.
 *****************************************************************************

  Authors: Maciej Izak
           Michael W. Vogel

  DaThoX 2004-2015
  FreeSparta.com

  Known Issues:
    - when form / frame is moved out of screen, there the form has no designer
      grid (dots)
    - ObjectInspector eats focus of AnchorDesigner (bug or feature?)
    - designer: mouse wheel to scroll content doesn't work - csDesigning is set and
      form doesn't get a LM_MOUSEWHEEL message
  TODO:

}

unit DockedMainIDE;

{$mode delphi}{$H+}
{ $define DEBUGDOCKEDFORMEDITOR}

interface

uses
  // RTL
  Classes, SysUtils, Contnrs,
  // LCL
  LCLIntf, Controls, Forms,
  // IdeIntf
  SrcEditorIntf, LazIDEIntf, FormEditingIntf, PropEdits, LazLoggerBase,
  // DockedFormEditor
  DockedResizer, DockedSourcePageControl, DockedTools,
  DockedOptionsIDE, DockedDesignForm,
  DockedSourceWindow;

type

  { TDockedTabMaster }

  TDockedTabMaster = class(TIDETabMaster)
  private
    // enable autosizing for docked form editor forms, see issue #32207 - disabled
    // in SourceFileManager per PreventAutoSize
    FAutoSizeControlList: TObjectList;
    FActivateEditorList: TObjectList;
  protected
    function GetTabDisplayState: TTabDisplayState; override;
    function GetTabDisplayStateEditor(ASourceEditor: TSourceEditorInterface): TTabDisplayState; override;
  public
    constructor Create;
    destructor Destroy; override;
    procedure BeginUpdate; override;
    procedure EndUpdate; override;
    procedure ActivateEditorPostponed(ASourceEditor: TSourceEditorInterface);
    function  AutoSizeInShowDesigner(AControl: TControl): Boolean; override;
    procedure EnableAutoSizing(AControl: TControl);
    function  GetDesigner(ASourceEditor: TSourceEditorInterface; ATabDisplayState: TTabDisplayState): TIDesigner; override;
    procedure ToggleFormUnit; override;
    procedure JumpToCompilerMessage(ASourceEditor: TSourceEditorInterface); override;
    procedure ShowCode(ASourceEditor: TSourceEditorInterface); override;
    procedure ShowDesigner(ASourceEditor: TSourceEditorInterface; AIndex: Integer = 0); override;
    procedure ShowForm(AForm: TCustomForm); override;
    procedure OptionsModified;
  end;

  { TDockedMainIDE }

  TDockedMainIDE = class(TObject)
  public
    class function GetCurrentPageControl: TSourcePageControl;

    class procedure Screen_FormAdded(Sender: TObject; AForm: TCustomForm);
    class procedure Screen_FormDel(Sender: TObject; AForm: TCustomForm);

    class procedure WindowCreate(Sender: TObject);
    class procedure WindowDestroy(Sender: TObject);
    class procedure WindowHide(Sender: TObject);
    class procedure WindowShow(Sender: TObject);

    class procedure EditorActivated(Sender: TObject);
    class procedure EditorActivatedPostponed(Sender: TObject);
    class procedure EditorCreate(Sender: TObject);
    class procedure EditorDestroyed(Sender: TObject);

    class procedure TabChange(Sender: TObject);

    class procedure GlobalSNOnChangeBounds(Sender: TObject);
    class procedure OnShowDesignerForm(Sender: TObject; {%H-}AEditor: TSourceEditorInterface;
                      AComponentPaletteClassSelected: Boolean);
    class procedure OnShowSrcEditor(Sender: TObject);

    class procedure OnDesignShowMethod(const Name: String);
    class procedure OnDesignRefreshPropertyValues;
    class procedure OnDesignModified(Sender: TObject);
    class procedure OnDesignPersistentAdded({%H-}APersistent: TPersistent; {%H-}Select: Boolean);
    class procedure OnDesignPersistentDeleted({%H-}APersistent: TPersistent);
    class procedure OnDesignMouseDown(Sender: TObject; {%H-}Button: TMouseButton;
                      {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
    class procedure OnDesignSetSelection(const ASelection: TPersistentSelectionList);
  end;

var
  DockedTabMaster: TDockedTabMaster absolute IDETabMaster;

implementation

{ TDockedTabMaster }

function TDockedTabMaster.GetTabDisplayState: TTabDisplayState;
begin
  Result := GetTabDisplayStateEditor(SourceEditorManagerIntf.ActiveEditor);
end;

function TDockedTabMaster.GetTabDisplayStateEditor(ASourceEditor: TSourceEditorInterface): TTabDisplayState;
var
  LPageCtrl: TSourcePageControl;
begin
  if ASourceEditor = nil then Exit(tdsNone);
  LPageCtrl := SourceWindows.FindPageControl(ASourceEditor);
  if LPageCtrl = nil then Exit(tdsNone);
  Result := LPageCtrl.ActiveTabDisplayState;
end;

constructor TDockedTabMaster.Create;
begin
  FActivateEditorList := TObjectList.Create(False);
  FAutoSizeControlList := TObjectList.Create(False);
end;

destructor TDockedTabMaster.Destroy;
begin
  FAutoSizeControlList.Free;
  FActivateEditorList.Free;
  inherited Destroy;
end;

procedure TDockedTabMaster.BeginUpdate;
begin
  SourceEditorManagerIntf.UnRegisterChangeEvent(semEditorActivate, TDockedMainIDE.EditorActivated);
  SourceEditorManagerIntf.RegisterChangeEvent(semEditorActivate, TDockedMainIDE.EditorActivatedPostponed);
end;

procedure TDockedTabMaster.EndUpdate;
var
  ASourceEditor: TSourceEditorInterface;
begin
  SourceEditorManagerIntf.UnRegisterChangeEvent(semEditorActivate, TDockedMainIDE.EditorActivatedPostponed);
  SourceEditorManagerIntf.RegisterChangeEvent(semEditorActivate, TDockedMainIDE.EditorActivated);
  for ASourceEditor in FActivateEditorList do
    TDockedMainIDE.EditorActivated(ASourceEditor);
  FActivateEditorList.Clear;
end;

procedure TDockedTabMaster.ActivateEditorPostponed(ASourceEditor: TSourceEditorInterface);
begin
  FActivateEditorList.Add(ASourceEditor);
end;

function TDockedTabMaster.AutoSizeInShowDesigner(AControl: TControl): Boolean;
begin
  // enable autosizing for docked form editor forms, see issue #32207 - disabled
  // in SourceFileManager per PreventAutoSize
  FAutoSizeControlList.Add(AControl);
  Result := True;
end;

procedure TDockedTabMaster.EnableAutoSizing(AControl: TControl);
var
  AIndex: Integer;
  AutoSizeControl: TControl;
begin
  // enable autosizing for docked form editor forms, see issue #32207 - disabled
  // in SourceFileManager per PreventAutoSize
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedTabMaster.EnableAutoSizing: ', DbgSName(AControl)); {$ENDIF}
  if not Assigned(AControl) then Exit;
  AutoSizeControl := nil;

  if AControl is TNonFormProxyDesignerForm then
  begin
    if (TNonFormProxyDesignerForm(AControl).LookupRoot is TControl) then
      AutoSizeControl := TControl(TNonFormProxyDesignerForm(AControl).LookupRoot);
  end else
    AutoSizeControl := AControl;

  AIndex := FAutoSizeControlList.IndexOf(AutoSizeControl);
  if (AIndex >= 0) then
  begin
    FAutoSizeControlList.Delete(AIndex);
    AutoSizeControl.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster Delayed'){$ENDIF};
  end;
end;

function TDockedTabMaster.GetDesigner(ASourceEditor: TSourceEditorInterface; ATabDisplayState: TTabDisplayState): TIDesigner;
var
  LPageCtrl: TSourcePageControl;
begin
  if ASourceEditor = nil then Exit(nil);
  LPageCtrl := SourceWindows.FindPageControl(ASourceEditor);
  if LPageCtrl = nil then Exit(nil);
  if LPageCtrl.DesignForm = nil then Exit(nil);
  case ATabDisplayState of
    tdsDesign:
      Result := LPageCtrl.DesignForm.Designer;
    tdsOther:
      Result := LPageCtrl.DesignForm.AnchorDesigner;
    else
      Result := nil;
  end;
end;

procedure TDockedTabMaster.ToggleFormUnit;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedTabMaster.ToggleFormUnit'); {$ENDIF}
  case TabDisplayState of
    tdsCode:
      ShowDesigner(SourceEditorManagerIntf.ActiveEditor);
    else
      ShowCode(SourceEditorManagerIntf.ActiveEditor);
  end;
end;

procedure TDockedTabMaster.JumpToCompilerMessage(
  ASourceEditor: TSourceEditorInterface);
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedTabMaster.JumpToCompilerMessage'); {$ENDIF}
  SourceEditorManagerIntf.ActiveEditor := ASourceEditor;
  ShowCode(ASourceEditor);
end;

procedure TDockedTabMaster.ShowCode(ASourceEditor: TSourceEditorInterface);
var
  LPageCtrl: TSourcePageControl;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedTabMaster.ShowCode'); {$ENDIF}
  if ASourceEditor = nil then Exit;
  SourceEditorManagerIntf.ActiveEditor := ASourceEditor;
  LPageCtrl := SourceWindows.FindPageControl(ASourceEditor);
  LPageCtrl.ShowCode;
  ASourceEditor.EditorControl.SetFocus;
end;

procedure TDockedTabMaster.ShowDesigner(ASourceEditor: TSourceEditorInterface; AIndex: Integer);
var
  LPageCtrl: TSourcePageControl;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedTabMaster.ShowDesigner'); {$ENDIF}
  if ASourceEditor = nil then Exit;
  SourceEditorManagerIntf.ActiveEditor := ASourceEditor;
  LPageCtrl := SourceWindows.FindPageControl(ASourceEditor);
  LPageCtrl.ShowDesigner(AIndex);
end;

procedure TDockedTabMaster.ShowForm(AForm: TCustomForm);
var
  LEditor: TSourceEditorInterface;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedTabMaster.ShowForm'); {$ENDIF}
  LEditor := FindSourceEditorForDesigner(AForm.Designer);
  SourceEditorManagerIntf.ActiveEditor := LEditor;
  ShowDesigner(LEditor);
end;

procedure TDockedTabMaster.OptionsModified;
var
  LPageCtrl: TSourcePageControl;
begin
  LPageCtrl := SourceWindows.LastActivePageControl;
  DesignForms.RemoveAllAnchorDesigner;
  SourceWindows.ShowCodeTabSkipCurrent(nil, nil);
  SourceWindows.RefreshAllPageControls;
  TDockedMainIDE.EditorActivated(SourceWindows.LastActiveSourceEditor);
  LPageCtrl.OnChange(LPageCtrl);
end;

{ TDockedMainIDE }

class function TDockedMainIDE.GetCurrentPageControl: TSourcePageControl;
var
  LForm: TCustomForm;
  LDesignForm: TDesignForm;
  LSourceWindowIntf: TSourceEditorWindowInterface;
begin
  Result := nil;
  if (FormEditingHook = nil) or (GlobalDesignHook = nil) then Exit;
  LForm := FormEditingHook.GetDesignerForm(GlobalDesignHook.LookupRoot);
  LDesignForm := DesignForms.Find(LForm);
  if LDesignForm = nil then Exit;
  LSourceWindowIntf := LDesignForm.LastActiveSourceWindow;
  if not Assigned(LSourceWindowIntf) then Exit;
  Result := SourceWindows.FindPageControl(LSourceWindowIntf.ActiveEditor);
end;

class procedure TDockedMainIDE.Screen_FormAdded(Sender: TObject; AForm: TCustomForm);
var
  LSourceEditor: TSourceEditorInterface;
  LDesignForm: TDesignForm;
  LPageCtrl: TSourcePageControl;
begin
  if IsFormDesign(AForm) then
  begin
    {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.Screen_FormAdded ', DbgSName(AForm)); {$ENDIF}

    // AForm like TForm1 etc...
    if not (csDesignInstance in AForm.ComponentState) and not (AForm is TNonFormProxyDesignerForm) then
      Exit;

    LDesignForm := TDesignForm.Create(AForm);
    LDesignForm.Hiding := True;
    DesignForms.Add(LDesignForm);
    LSourceEditor := FindSourceEditorForDesigner(AForm.Designer);
    if LSourceEditor <> nil then
    begin
      LPageCtrl := SourceWindows.FindPageControl(LSourceEditor);
      if LPageCtrl <> nil then
      begin
        LPageCtrl.DesignForm := LDesignForm;
        LPageCtrl.CreateTabSheetDesigner;
        if LDesignForm.IsAnchorDesign then
          LPageCtrl.CreateTabSheetAnchors;
      end;
    end;
    SetTimer(AForm.Handle, WM_SETNOFRAME, 10, nil);
  end else begin
    if AForm is TSourceEditorWindowInterface then
      AForm.AddHandlerOnChangeBounds(GlobalSNOnChangeBounds);
  end;
end;

class procedure TDockedMainIDE.Screen_FormDel(Sender: TObject; AForm: TCustomForm);
var
  LSourceWindow: TSourceWindow;
  LPageCtrl: TSourcePageControl;
begin
  if IsFormDesign(AForm) then
  begin
    {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.Screen_FormDel ', DbgSName(AForm)); {$ENDIF}
    AForm.Parent := nil;

    for LSourceWindow in SourceWindows do
    begin
      if LSourceWindow.ActiveDesignForm <> nil then
        if LSourceWindow.ActiveDesignForm.Form = AForm then
          // don't set ActiveDesignForm := nil! we can't call OnChange tab, because tab don't exist anymore
          LSourceWindow.RemoveActiveDesignForm;

      for LPageCtrl in LSourceWindow.PageControlList do
        if LPageCtrl.DesignForm <> nil then
          if LPageCtrl.DesignForm.Form = AForm then
          begin
            LPageCtrl.DesignForm := nil;
            LPageCtrl.PageIndex := 0;
          end;
    end;
    DesignForms.Remove(AForm);
  end
  else
    if AForm is TSourceEditorWindowInterface then
      AForm.RemoveHandlerOnChangeBounds(GlobalSNOnChangeBounds);
end;

class procedure TDockedMainIDE.WindowCreate(Sender: TObject);
var
  LSourceWindowIntf: TSourceEditorWindowInterface;
begin
  if not (Sender is TSourceEditorWindowInterface) then Exit;
  if Sender.ClassNameIs('TSourceNotebook') then
  begin
    {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.WindowCreate SourceEditor created'); {$ENDIF}
    LSourceWindowIntf := Sender as TSourceEditorWindowInterface;
    SourceWindows.Add(TSourceWindow.Create(LSourceWindowIntf));
  end;
end;

class procedure TDockedMainIDE.WindowDestroy(Sender: TObject);
var
  LDesignForm: TDesignForm;
begin
  if not (Sender is TSourceEditorWindowInterface) then Exit;
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.WindowDestroy'); {$ENDIF}
  for LDesignForm in DesignForms do
    if LDesignForm.LastActiveSourceWindow = Sender then
      LDesignForm.LastActiveSourceWindow := nil;
  SourceWindows.Remove(Sender as TSourceEditorWindowInterface);
end;

class procedure TDockedMainIDE.WindowHide(Sender: TObject);
var
  LSourceWindow: TSourceWindow;
  LDesignForm: TDesignForm;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.WindowHide'); {$ENDIF}
  LSourceWindow := SourceWindows.SourceWindow[Sender as TSourceEditorWindowInterface];
  if not Assigned(LSourceWindow) or (LSourceWindow.ActiveDesignForm = nil) then
    Exit;
  LDesignForm := LSourceWindow.ActiveDesignForm;
  LDesignForm.HideWindow;
end;

class procedure TDockedMainIDE.WindowShow(Sender: TObject);
var
  LSourceWindow: TSourceWindow;
  LDesignForm: TDesignForm;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.WindowShow'); {$ENDIF}
  LSourceWindow := SourceWindows.SourceWindow[Sender as TSourceEditorWindowInterface];
  if not Assigned(LSourceWindow) or (LSourceWindow.ActiveDesignForm = nil) then
    Exit;
  LDesignForm := LSourceWindow.ActiveDesignForm;
  LDesignForm.ShowWindow;
end;

class procedure TDockedMainIDE.EditorActivated(Sender: TObject);
var
  LDesigner: TIDesigner;
  LSourceEditor: TSourceEditorInterface;
var
  LPageCtrl: TSourcePageControl;
  LSourceWindowIntf: TSourceEditorWindowInterface;
  LDesignForm: TDesignForm;
begin
  if Sender is TSourceEditorInterface then
  begin
    LSourceEditor := TSourceEditorInterface(Sender);
    {$IFDEF DEBUGDOCKEDFORMEDITOR}
      DebugLn('TDockedMainIDE.EditorActivated [' + LSourceEditor.PageCaption +
              '] SourceWindow[' + SourceWindowCaption(LSourceEditor) + ']');
    {$ENDIF}
    // if we create directly new project then Activate is called without EditorCreate...
    if not (LSourceEditor.EditorControl.Parent.Parent is TSourcePageControl) then
    begin
      // possible is situation when we moved tab into other window
      // then was not called event EditorDestroy - that generates problems with switching tabs
      // or when we moving tab to first window ( then is raising : duplicates not allowed in dictionary).
      if SourceWindows.LastSourceEditorNotFound then
        EditorDestroyed(nil);
      EditorCreate(Sender);
    end;
    LDesigner := LSourceEditor.GetDesigner(True);
    // should be performed during EditorCreate (parent of parent is SourcePageControl)
    LPageCtrl := TSourcePageControl(LSourceEditor.EditorControl.Parent.Parent);
    if LPageCtrl = nil then Exit;

    LDesignForm := SourceWindows.FindDesignForm(LPageCtrl);
    if LDesigner = nil then
      LPageCtrl.RemoveDesignPages
    else begin
      if not Assigned(LPageCtrl.Resizer) then
        LPageCtrl.CreateResizer;
      LPageCtrl.CreateTabSheetDesigner;
      if LDesignForm.IsAnchorDesign then
        LPageCtrl.CreateTabSheetAnchors;
    end;

    LSourceWindowIntf := TSourceEditorWindowInterface(LPageCtrl.Owner);
    SourceWindows.LastActiveSourceWindow := LSourceWindowIntf;
    SourceWindows.LastActiveSourceEditor := LSourceEditor;

    // when we switch tab, design form should be hidden
    if (LDesigner = nil) or (LDesignForm = nil) then
      SourceWindows.SourceWindow[LSourceWindowIntf].ActiveDesignForm := nil
    else begin
      // during form  loading for example from package, ActiveDesignForm assignment,
      // blocks the message queue responsible for hiding form
      // We can't check it because there are some forms where designing is not handled yet.
      // (for that kind of forms is returned empty designform data)
      // maybe we can fix this in future
      if not LDesignForm.Hiding then
        // Prevent unexpected events (when is deactivated some control outside designed form)
        if (LDesignForm.LastActiveSourceWindow = LSourceWindowIntf)
        // important!!! for many error - switching between editors...
        and LPageCtrl.DesignerPageActive then
          SourceWindows.SourceWindow[LSourceWindowIntf].ActiveDesignForm := LDesignForm
        else
          SourceWindows.SourceWindow[LSourceWindowIntf].ActiveDesignForm := nil;
    end;

    if LPageCtrl.DesignerPageActive then
    begin
      if not LDesignForm.Hiding then
      begin
        SourceWindows.ShowCodeTabSkipCurrent(LPageCtrl, LDesignForm);
        LPageCtrl.AdjustPage;
        // don't focus designer here, focus can be on ObjectInspector, then
        // <Del> in OI Events deletes component instead event handler
      end;
    end else begin
      if LDesignForm <> nil then
        LDesignForm.HideWindow;
      LPageCtrl.InitPage;
    end;
  end else begin
    {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.EditorActivated [' + DbgSName(Sender) + '] - not a Source Editor!');{$ENDIF}
  end;
end;

class procedure TDockedMainIDE.EditorActivatedPostponed(Sender: TObject);
begin
  if Sender is TSourceEditorInterface then
    DockedTabMaster.ActivateEditorPostponed(TSourceEditorInterface(Sender));
end;

class procedure TDockedMainIDE.EditorCreate(Sender: TObject);
var
  LSourceEditor: TSourceEditorInterface;
  LSourceWindowIntf: TSourceEditorWindowInterface;
  LPageCtrl: TSourcePageControl;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.EditorCreate'); {$ENDIF}
  LSourceEditor := Sender as TSourceEditorInterface;
  if LSourceEditor.EditorControl.Parent.Parent is TSourcePageControl then Exit;
  LPageCtrl := TSourcePageControl.Create(LSourceEditor);
  LPageCtrl.OnChange := TabChange;
  LSourceWindowIntf := SourceWindowGet(LSourceEditor);
  SourceWindows.SourceWindow[LSourceWindowIntf].AddPageCtrl(LPageCtrl);
end;

class procedure TDockedMainIDE.EditorDestroyed(Sender: TObject);
var
  LSourceEditor: TSourceEditorInterface;
  LPageCtrl: TSourcePageControl;
  LSourceWindowIntf: TSourceEditorWindowInterface;
  LDesignForm: TDesignForm;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.EditorDestroyed'); {$ENDIF}
  // sender is here as special parameter, because is possible situation where is moved editor
  // to another window and was not triggered EditorDestroy - for more info goto editoractivate
  if Sender = nil then
    LSourceEditor := SourceWindows.LastActiveSourceEditor
  else
    LSourceEditor := TSourceEditorInterface(Sender);

  // parent don't exist anymore and we must search in each window...
  if Sender = nil then // but not for Sender = nil :P
    LPageCtrl := SourceWindows.LastActivePageControl
  else
    LPageCtrl := SourceWindows.FindPageControl(LSourceEditor);

  if LPageCtrl = nil then Exit;

  LDesignForm := DesignForms.Find(LSourceEditor.GetDesigner(False));

  // goto first comment (forced destroy)
  if Sender = nil then
    LSourceWindowIntf := SourceWindows.LastActiveSourceWindow
  else
    LSourceWindowIntf := TSourceEditorWindowInterface(LPageCtrl.Owner);

  if LDesignForm <> nil then
  begin
    SourceWindows.SourceWindow[LSourceWindowIntf].ActiveDesignForm := nil;
    LDesignForm.LastActiveSourceWindow := nil;
  end;

  SourceWindows.SourceWindow[LSourceWindowIntf].RemovePageCtrl(LSourceEditor);
  LPageCtrl.Free;
end;

class procedure TDockedMainIDE.TabChange(Sender: TObject);
var
  LSourceWindowIntf: TSourceEditorWindowInterface;
  LSourceWindow: TSourceWindow;
  LDesigner: TIDesigner;
  LDesignForm: TDesignForm;
  LPageCtrl: TSourcePageControl;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.TabChange'); {$ENDIF}
  // activate proper source editor window when user is clicking on page.
  // (at clicking time can be active other source window)
  LSourceWindowIntf := TComponent(Sender).Owner as TSourceEditorWindowInterface;
  if LSourceWindowIntf <> SourceEditorManagerIntf.ActiveSourceWindow then
    SourceEditorManagerIntf.ActiveSourceWindow := LSourceWindowIntf;

  LPageCtrl := TSourcePageControl(Sender);
  if LSourceWindowIntf.ActiveEditor = nil then Exit;
  if LPageCtrl = nil then Exit;

  LDesigner := LSourceWindowIntf.ActiveEditor.GetDesigner(True);
  LDesignForm := DesignForms.Find(LDesigner);
  if LDesignForm = nil then Exit;
  LSourceWindow := SourceWindows.SourceWindow[LSourceWindowIntf];
  if LSourceWindow = nil then Exit;

  if not LPageCtrl.DesignerPageActive then
  begin
    LSourceWindow.ActiveDesignForm := nil;
    LPageCtrl.InitPage;
    LSourceWindowIntf.ActiveEditor.EditorControl.SetFocus;
  end else begin
    LSourceWindow.ActiveDesignForm := LDesignForm;
    // enable autosizing after creating a new form
    DockedTabMaster.EnableAutoSizing(LDesignForm.Form);
    SourceWindows.ShowCodeTabSkipCurrent(LPageCtrl, LDesignForm);
    LPageCtrl.DesignerSetFocus;
  end;
end;

class procedure TDockedMainIDE.GlobalSNOnChangeBounds(Sender: TObject);
var
  LSourceWindowIntf: TSourceEditorWindowInterface;
  LSourceWindow: TSourceWindow;
  LDesignForm: TDesignForm;
  LPageCtrl: TSourcePageControl;
  LResizer: TResizer;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.GlobalSNOnChangeBounds'); {$ENDIF}
  // Check parent. Maybe is different? If yes then window changed state (docked/undocked) and we need to perform few actions
  LSourceWindowIntf := Sender as TSourceEditorWindowInterface;

  // dock/undock event :)
  LSourceWindow := SourceWindows.SourceWindow[LSourceWindowIntf];
  if LSourceWindow = nil then Exit;
  if LSourceWindow.LastTopParent <> LSourceWindowIntf.GetTopParent then
  begin
    LSourceWindow.LastTopParent := LSourceWindowIntf.GetTopParent;
    // refresh for popupparent
    LDesignForm := LSourceWindow.ActiveDesignForm;
    LSourceWindow.ActiveDesignForm := nil;
    LSourceWindow.ActiveDesignForm := LDesignForm;
    if LDesignForm <> nil then
    begin
      LPageCtrl := SourceWindows.FindPageControl(LSourceWindowIntf.ActiveEditor);
      if not Assigned(LPageCtrl) then Exit;
      LResizer := LPageCtrl.Resizer;
      if not Assigned(LResizer) then Exit;
      LDesignForm.Form.Parent := LResizer.FormContainer;
      SetTimer(LDesignForm.Form.Handle, WM_BOUNDTODESIGNTABSHEET, 10, nil);
    end;
  end;
end;

class procedure TDockedMainIDE.OnShowDesignerForm(Sender: TObject; AEditor: TSourceEditorInterface;
  AComponentPaletteClassSelected: Boolean);
var
  LDesignForm: TDesignForm;
  LPageCtrl: TSourcePageControl;
  LSourceWindow: TSourceWindow;
  LSourceEditorInterface: TSourceEditorInterface;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.OnShowDesignerForm'); {$ENDIF}
  LDesignForm := DesignForms.Find(TCustomForm(Sender).Designer);
  if (LDesignForm = nil) or LDesignForm.Hiding then Exit;
  LPageCtrl := SourceWindows.FindPageControl(SourceEditorManagerIntf.ActiveEditor);
  if LPageCtrl = nil then Exit;

  if AComponentPaletteClassSelected then
  begin
    // if form is already opened do nothing, if not then show form
    for LSourceWindow in SourceWindows do
    begin
      LSourceEditorInterface := LSourceWindow.SourceWindowIntf.ActiveEditor;
      if (LSourceEditorInterface = nil) or (LSourceEditorInterface.GetDesigner(True) <> LDesignForm.Designer) then
        Continue;
      LPageCtrl := SourceWindows.FindPageControl(LSourceEditorInterface);
      if LPageCtrl.FormPageActive then
        Exit;
    end;
  end;
  DockedTabMaster.ShowDesigner(SourceEditorManagerIntf.ActiveEditor);
end;

class procedure TDockedMainIDE.OnShowSrcEditor(Sender: TObject);
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.OnShowSrcEditor'); {$ENDIF}
  DockedTabMaster.ShowCode(Sender as TSourceEditorInterface);
end;

class procedure TDockedMainIDE.OnDesignShowMethod(const Name: String);
var
  LDesignForm: TDesignForm;
  LSecondEditor: TSourceEditorInterface = nil;
  LSourceWindowIntf: TSourceEditorWindowInterface;
  i: Integer;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.OnDesignShowMethod'); {$ENDIF}
  if FormEditingHook = nil then Exit;
  LDesignForm := DesignForms.Find(FormEditingHook.GetCurrentDesigner);
  if LDesignForm = nil then Exit;
  for i := 0 to SourceEditorManagerIntf.SourceWindowCount - 1 do
  begin
    LSourceWindowIntf := SourceEditorManagerIntf.SourceWindows[i];
    if LDesignForm.LastActiveSourceWindow = LSourceWindowIntf then
      Continue;
    if LSourceWindowIntf.ActiveEditor <> nil then
      if LSourceWindowIntf.ActiveEditor.GetDesigner(True) = LDesignForm.Designer then
      begin
        LSecondEditor := LSourceWindowIntf.ActiveEditor;
        Break;
      end;
  end;

  if Assigned(LSecondEditor) then
  begin
    DockedTabMaster.ShowCode(LSecondEditor);
    LazarusIDE.DoShowMethod(LSecondEditor, Name);
  end else
    if Assigned(LDesignForm.LastActiveSourceWindow) then
      DockedTabMaster.ShowCode(LDesignForm.LastActiveSourceWindow.ActiveEditor);
end;

class procedure TDockedMainIDE.OnDesignRefreshPropertyValues;
var
  LPageCtrl: TSourcePageControl;

  function RootIsSelected: Boolean;
  var
    LSelection: TPersistentSelectionList;
    i: Integer;
  begin
    Result := False;
    LSelection := TPersistentSelectionList.Create;
    try
      GlobalDesignHook.GetSelection(LSelection);
      for i := 0 to LSelection.Count - 1 do
        if LSelection.Items[i] = GlobalDesignHook.LookupRoot then
        begin
          Result := True;
          Break;
        end;
    finally
      LSelection.Free;
    end;
  end;

begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.OnDesignRefreshPropertyValues'); {$ENDIF}
  if not (GlobalDesignHook.LookupRoot is TCustomFrame)
  and not (GlobalDesignHook.LookupRoot is TCustomForm) then Exit;

  LPageCtrl := GetCurrentPageControl;
  if not Assigned(LPageCtrl) then Exit;
  if not ((LPageCtrl.PageIndex = 2) or RootIsSelected) then Exit;
  LPageCtrl.AdjustPage;
end;

class procedure TDockedMainIDE.OnDesignModified(Sender: TObject);
var
  LPageCtrl: TSourcePageControl;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.OnDesignModified'); {$ENDIF}
  LPageCtrl := GetCurrentPageControl;
  if not Assigned(LPageCtrl) then Exit;
  if not Assigned(LPageCtrl.Resizer) then Exit;
  LPageCtrl.Resizer.ResizeControl.OnModified;
end;

class procedure TDockedMainIDE.OnDesignPersistentAdded(APersistent: TPersistent; Select: Boolean);
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.OnDesignPersistentAdded'); {$ENDIF}
  OnDesignModified(nil);
end;

class procedure TDockedMainIDE.OnDesignPersistentDeleted(APersistent: TPersistent);
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.OnDesignPersistentDeleted'); {$ENDIF}
  OnDesignModified(nil);
end;

class procedure TDockedMainIDE.OnDesignMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  LPageCtrl: TSourcePageControl;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} writeln('TDockedMainIDE.OnDesignMouseDown'); {$ENDIF}
  LPageCtrl := GetCurrentPageControl;
  if not Assigned(LPageCtrl) then Exit;
  LPageCtrl.DesignerSetFocus;
end;

class procedure TDockedMainIDE.OnDesignSetSelection(
  const ASelection: TPersistentSelectionList);
var
  LDesignForm: TDesignForm;
  LSelectedControl: TControl;
begin
  {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TDockedMainIDE.OnDesignSetSelection'); {$ENDIF}
  LDesignForm := DesignForms.Find(FormEditingHook.GetCurrentDesigner);
  if LDesignForm = nil then Exit;
  if (ASelection.Count = 1) and (ASelection[0] is TControl) then
    LSelectedControl := TControl(ASelection[0]);
  if LSelectedControl = LDesignForm.SelectedControl then Exit;
  LDesignForm.SelectedControl := LSelectedControl;
  OnDesignModified(nil);
end;

end.

