{ ***************************************************************************** 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 SourceEditorWindows). Each SourceEditor window can hold a lot of units, forms etc. available per SourceEditorWindowInterface in PageControlList. } unit DockedSourceEditorWindow; {$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 DockedSourceEditorPageControls, DockedDesignForm, DockedModulePageControl, DockedOptionsIDE, DockedTools; type { TSourceEditorWindow } TSourceEditorWindow = class private FActiveDesignForm: TDesignForm; FLastActiveSourceEditor: TSourceEditorInterface; FLastTopParent: TControl; FNotebookPageChanged: TNotifyEvent; FPageControlList: TSourceEditorPageControls; FSourceEditorNotebook: TExtendedNotebook; FSourceEditorWindowInterface: TSourceEditorWindowInterface; function GetActiveEditor: TSourceEditorInterface; procedure HookIntoOnPageChanged; procedure SetActiveDesignForm(const AValue: TDesignForm); procedure SourceEditorPageChanged(Sender: TObject); procedure UpdateEditorPageCaption(Sender: TObject); public constructor Create(ASourceEditorWindowInterface: TSourceEditorWindowInterface); destructor Destroy; override; procedure AddPageCtrl(ASourceEditor: TSourceEditorInterface; APageControl: TModulePageControl); procedure AdjustPageControl; function FindModulePageControl(ASourceEditor: TSourceEditorInterface): TModulePageControl; 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: TSourceEditorPageControls read FPageControlList; property SourceEditorWindowInterface: TSourceEditorWindowInterface read FSourceEditorWindowInterface; end; { TSourceEditorWindows } TSourceEditorWindows = class(specialize TFPGList) private FLastActiveSourceEditorWindow: TSourceEditorWindowInterface; function GetLastActiveModulePageControl: TModulePageControl; function GetLastActiveSourceEditor: TSourceEditorInterface; function GetWindowInterface(ASrcEditor: TSourceEditorWindow): TSourceEditorWindowInterface; function GetSourceEditorWindow(AWindowInterface: TSourceEditorWindowInterface): TSourceEditorWindow; procedure SetLastActiveSourceEditor(AValue: TSourceEditorInterface); public constructor CreateNew; destructor Destroy; override; function Contains(AWindowInterface: TSourceEditorWindowInterface): Boolean; function Contains(ASrcEditor: TSourceEditorWindow): Boolean; procedure DeleteItem(Index: Integer); function FindDesignForm(AModulePageCtrl: TModulePageControl): TDesignForm; function FindModulePageControl(ASrcEditor: TSourceEditorInterface): TModulePageControl; function IndexOf(AWindowInterface: TSourceEditorWindowInterface): Integer; overload; function LastSourceEditorNotFound: Boolean; procedure RefreshActivePageControls; procedure RefreshAllPageControls; procedure Remove(AWindowInterface: TSourceEditorWindowInterface); overload; procedure ShowCodeTabSkipCurrent(CurrentPageCtrl: TModulePageControl; ADesignForm: TDesignForm); public property LastActiveSourceEditorWindow: TSourceEditorWindowInterface read FLastActiveSourceEditorWindow write FLastActiveSourceEditorWindow; property LastActiveSourceEditor: TSourceEditorInterface read GetLastActiveSourceEditor write SetLastActiveSourceEditor; property LastActiveModulePageControl: TModulePageControl read GetLastActiveModulePageControl; property WindowInterface[ASrcEditor: TSourceEditorWindow]: TSourceEditorWindowInterface read GetWindowInterface; property SourceEditorWindow[AWindowInterface: TSourceEditorWindowInterface]: TSourceEditorWindow read GetSourceEditorWindow; end; var SourceEditorWindows: TSourceEditorWindows; implementation { TSourceEditorWindow } procedure TSourceEditorWindow.HookIntoOnPageChanged; var i: Integer; begin for i := 0 to FSourceEditorWindowInterface.ControlCount - 1 do if FSourceEditorWindowInterface.Controls[i] is TExtendedNotebook then begin FSourceEditorNotebook := TExtendedNotebook(FSourceEditorWindowInterface.Controls[i]); Break; end; if not Assigned(FSourceEditorNotebook) then Exit; FNotebookPageChanged := FSourceEditorNotebook.OnChange; FSourceEditorNotebook.OnChange := @SourceEditorPageChanged; end; function TSourceEditorWindow.GetActiveEditor: TSourceEditorInterface; begin Result := FSourceEditorWindowInterface.ActiveEditor; end; procedure TSourceEditorWindow.SetActiveDesignForm(const AValue: TDesignForm); var LPageCtrl: TModulePageControl; 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 := FindModulePageControl(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 TSourceEditorWindow.SourceEditorPageChanged(Sender: TObject); var LPageCtrl: TModulePageControl; begin {$IFDEF DEBUGDOCKEDFORMEDITOR} DebugLn('TSourceEditorWindow.SourceEditorPageChanged SourceEditorWindow[' + FSourceEditorWindowInterface.Caption + ']'); {$ENDIF} FNotebookPageChanged(Sender); LPageCtrl := FindModulePageControl(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; procedure TSourceEditorWindow.UpdateEditorPageCaption(Sender: TObject); var LSourceEditor: TSourceEditorInterface; LSourceEditorWindow: TSourceEditorWindowInterface; begin // if a unit is cloned to undocked empty source editor window, the ModulePageControl // 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('TSourceEditorWindow.UpdateEditorPageCaption [' + SourceEditorWindowCaption(LSourceEditor) + ']'); {$ENDIF} LSourceEditorWindow := SourceEditorWindow(LSourceEditor); if not Assigned(LSourceEditorWindow) or (SourceEditorManagerIntf.ActiveSourceWindow = LSourceEditorWindow) or (SourceEditorWindows.LastActiveSourceEditorWindow = LSourceEditorWindow) then Exit; LSourceEditorWindow.ActiveEditor := LSourceEditor; end; constructor TSourceEditorWindow.Create(ASourceEditorWindowInterface: TSourceEditorWindowInterface); begin FLastActiveSourceEditor := nil; FSourceEditorWindowInterface := ASourceEditorWindowInterface; FPageControlList := TSourceEditorPageControls.Create; FSourceEditorNotebook := nil; HookIntoOnPageChanged; FSourceEditorWindowInterface.AddUpdateEditorPageCaptionHandler(@UpdateEditorPageCaption); end; destructor TSourceEditorWindow.Destroy; begin if Assigned(FSourceEditorNotebook) then FSourceEditorNotebook.OnChange := FNotebookPageChanged; FPageControlList.Free; inherited Destroy; end; procedure TSourceEditorWindow.AddPageCtrl(ASourceEditor: TSourceEditorInterface; APageControl: TModulePageControl); begin FPageControlList.Add(ASourceEditor, APageControl); end; procedure TSourceEditorWindow.AdjustPageControl; var LPageCtrl: TModulePageControl; begin LPageCtrl := FindModulePageControl(ActiveEditor); if LPageCtrl <> nil then LPageCtrl.AdjustPage; end; function TSourceEditorWindow.FindModulePageControl(ASourceEditor: TSourceEditorInterface): TModulePageControl; var LParent: TWinControl; begin if ASourceEditor = nil then Exit(nil); LParent := ASourceEditor.EditorControl.Parent; while LParent <> nil do begin if LParent is TModulePageControl then Exit(TModulePageControl(LParent)); LParent := LParent.Parent; end; Result := nil; end; procedure TSourceEditorWindow.RemoveActiveDesignForm; begin FActiveDesignForm := nil; end; procedure TSourceEditorWindow.RemovePageCtrl(ASourceEditor: TSourceEditorInterface); begin FPageControlList.Remove(ASourceEditor); if LastActiveSourceEditor = ASourceEditor then LastActiveSourceEditor := nil; end; { TSourceEditorWindows } function TSourceEditorWindows.GetWindowInterface(ASrcEditor: TSourceEditorWindow): TSourceEditorWindowInterface; var LIndex: Integer; begin LIndex := IndexOf(ASrcEditor); if LIndex >= 0 then Result := Items[LIndex].SourceEditorWindowInterface else Result := nil; end; function TSourceEditorWindows.GetLastActiveModulePageControl: TModulePageControl; begin Result := FindModulePageControl(LastActiveSourceEditor); end; function TSourceEditorWindows.GetLastActiveSourceEditor: TSourceEditorInterface; var LSourceEditorWindow: TSourceEditorWindow; begin Result := nil; if not Assigned(LastActiveSourceEditorWindow) then Exit; LSourceEditorWindow := SourceEditorWindow[LastActiveSourceEditorWindow]; if not Assigned(LSourceEditorWindow) then Exit; Result := LSourceEditorWindow.LastActiveSourceEditor; end; function TSourceEditorWindows.GetSourceEditorWindow(AWindowInterface: TSourceEditorWindowInterface): TSourceEditorWindow; var LIndex: Integer; begin LIndex := IndexOf(AWindowInterface); if LIndex >= 0 then Result := Items[LIndex] else Result := nil; end; procedure TSourceEditorWindows.SetLastActiveSourceEditor(AValue: TSourceEditorInterface); var LSourceEditorWindow: TSourceEditorWindow; begin if not Assigned(LastActiveSourceEditorWindow) then Exit; LSourceEditorWindow := SourceEditorWindow[LastActiveSourceEditorWindow]; if not Assigned(LSourceEditorWindow) then Exit; LSourceEditorWindow.LastActiveSourceEditor := AValue; end; constructor TSourceEditorWindows.CreateNew; begin inherited Create; FLastActiveSourceEditorWindow := nil; end; destructor TSourceEditorWindows.Destroy; begin while Count > 0 do DeleteItem(0); inherited Destroy; end; function TSourceEditorWindows.Contains(AWindowInterface: TSourceEditorWindowInterface): Boolean; begin Result := IndexOf(AWindowInterface) >= 0; end; function TSourceEditorWindows.Contains(ASrcEditor: TSourceEditorWindow): Boolean; begin Result := IndexOf(ASrcEditor) >= 0; end; procedure TSourceEditorWindows.DeleteItem(Index: Integer); var LSourceEditorWindow: TSourceEditorWindow; begin LSourceEditorWindow := Items[Index]; LSourceEditorWindow.Free; Delete(Index); end; function TSourceEditorWindows.FindDesignForm(AModulePageCtrl: TModulePageControl): TDesignForm; var LSourceEditorWindow: TSourceEditorWindow; LSourceEditorInterface: TSourceEditorInterface; begin Result := nil; if AModulePageCtrl = nil then Exit; for LSourceEditorWindow in Self do begin if AModulePageCtrl.Owner = LSourceEditorWindow.SourceEditorWindowInterface then begin LSourceEditorInterface := LSourceEditorWindow.ActiveEditor; if LSourceEditorInterface = nil then Exit; Result := DesignForms.Find(LSourceEditorInterface.GetDesigner(True)); Exit; end; end; end; function TSourceEditorWindows.FindModulePageControl(ASrcEditor: TSourceEditorInterface): TModulePageControl; var LSourceEditorWindow: TSourceEditorWindow; begin Result := nil; for LSourceEditorWindow in Self do if LSourceEditorWindow.PageControlList.Contains(ASrcEditor) then Exit(LSourceEditorWindow.PageControlList.PageControl[ASrcEditor]); end; function TSourceEditorWindows.IndexOf(AWindowInterface: TSourceEditorWindowInterface): Integer; var i: Integer; begin Result := -1; for i := 0 to Count - 1 do if Items[i].SourceEditorWindowInterface = AWindowInterface then Exit(i); end; function TSourceEditorWindows.LastSourceEditorNotFound: Boolean; var i: Integer; LSourceEditorPageControl: TSourceEditorPageControl; LSourceEditorWindow: TSourceEditorWindow; begin if (LastActiveSourceEditorWindow = nil) or (LastActiveSourceEditor = nil) then Exit(False); LSourceEditorWindow := SourceEditorWindow[LastActiveSourceEditorWindow]; for LSourceEditorPageControl in LSourceEditorWindow.PageControlList do begin Result := True; for i := 0 to LastActiveSourceEditorWindow.Count - 1 do if LSourceEditorPageControl.SourceEditor = LastActiveSourceEditorWindow.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. LSourceEditorWindow.LastActiveSourceEditor := LSourceEditorPageControl.SourceEditor; Exit; end; end; Result := False; end; procedure TSourceEditorWindows.RefreshActivePageControls; var LWindow: TSourceEditorWindow; LPageCtrl: TModulePageControl; begin for LWindow in Self do begin LPageCtrl := LWindow.FindModulePageControl(LWindow.ActiveEditor); // for example LPageCtrl is nil when we clone module to new window if (LPageCtrl = nil) or (csDestroying in LWindow.SourceEditorWindowInterface.ComponentState) then Continue; if (LWindow.ActiveEditor = nil) or (LWindow.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 TSourceEditorWindows.RefreshAllPageControls; var LWindow: TSourceEditorWindow; LSourceEditorPageControl: TSourceEditorPageControl; begin for LWindow in SourceEditorWindows do for LSourceEditorPageControl in LWindow.PageControlList do begin LSourceEditorPageControl.PageControl.TabPosition := DockedOptions.TabPosition; LSourceEditorPageControl.PageControl.RefreshResizer; end; end; procedure TSourceEditorWindows.Remove(AWindowInterface: TSourceEditorWindowInterface); var LIndex: Integer; begin LIndex := IndexOf(AWindowInterface); if LIndex < 0 then Exit; DeleteItem(LIndex); if LastActiveSourceEditorWindow = AWindowInterface then LastActiveSourceEditorWindow := nil; end; procedure TSourceEditorWindows.ShowCodeTabSkipCurrent(CurrentPageCtrl: TModulePageControl; ADesignForm: TDesignForm); var LSourceEditorWindow: TSourceEditorWindow; LSourceEditorPageControl: TSourceEditorPageControl; begin for LSourceEditorWindow in Self do for LSourceEditorPageControl in LSourceEditorWindow.PageControlList do if LSourceEditorPageControl.PageControl = CurrentPageCtrl then begin LSourceEditorPageControl.PageControl.DesignForm := ADesignForm; LSourceEditorPageControl.PageControl.InitPage; end else if LSourceEditorPageControl.PageControl.DesignForm = ADesignForm then LSourceEditorPageControl.PageControl.ShowCode; end; initialization SourceEditorWindows := TSourceEditorWindows.CreateNew; finalization SourceEditorWindows.Free; end.