{ ***************************************************************************** See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** Authors: Maciej Izak Michael W. Vogel } unit DockedDesignForm; {$mode objfpc}{$H+} {$modeswitch advancedrecords} {$modeswitch typehelpers} interface uses // RTL Classes, SysUtils, fgl, // LCL LMessages, Forms, LCLType, LCLIntf, Controls, // IDEIntf SrcEditorIntf, // DockedFormEditor DockedFormAccesses, DockedBasicAnchorDesigner; const WM_SETNOFRAME = WM_USER; WM_BOUNDTODESIGNTABSHEET = WM_USER + 1; type { TDesignForm } TDesignForm = class(TDesignFormIDE) private FHiding: Boolean; FOnAdjustPageNeeded: TNotifyEvent; FWndMethod: TWndMethod; procedure FixF12_ActiveEditor; procedure FormChangeBounds(Sender: TObject); procedure WndMethod(var Msg: TLMessage); public constructor Create(AForm: TCustomForm); override; destructor Destroy; override; public property Hiding: Boolean read FHiding write FHiding; property OnAdjustPageNeeded: TNotifyEvent read FOnAdjustPageNeeded write FOnAdjustPageNeeded; end; { TDesignForms } TDesignForms = class(specialize TFPGList) public destructor Destroy; override; procedure DeleteDesignForm(AIndex: Integer); function Find(AForm: TCustomForm): TDesignForm; overload; function Find(ADesigner: TIDesigner): TDesignForm; overload; function IndexOf(AForm: TCustomForm): Integer; overload; procedure Remove(AForm: TCustomForm); overload; procedure RemoveAllAnchorDesigner; end; var DesignForms: TDesignForms; implementation { TDesignForm } procedure TDesignForm.FixF12_ActiveEditor; var i: Integer; begin // Without this, button F12 don't work. (after creating new for editor is inactive) // Just do it for new created forms or the last loaded form becomes the active // source editor after reopening a project. if Designer <> SourceEditorManagerIntf.ActiveEditor.GetDesigner(True) then Exit; SourceEditorManagerIntf.ActiveEditor := nil; for i := 0 to SourceEditorManagerIntf.UniqueSourceEditorCount - 1 do if Designer = SourceEditorManagerIntf.UniqueSourceEditors[i].GetDesigner(True) then begin SourceEditorManagerIntf.ActiveEditor := SourceEditorManagerIntf.UniqueSourceEditors[i]; Break; end; end; procedure TDesignForm.FormChangeBounds(Sender: TObject); begin if not Update then SetTimer(Form.Handle, WM_BOUNDTODESIGNTABSHEET, 10, nil); end; procedure TDesignForm.WndMethod(var Msg: TLMessage); var Timer: TLMTimer; begin if Msg.msg = LM_TIMER then begin Timer := TLMTimer(Msg); case Timer.TimerID of WM_SETNOFRAME: begin KillTimer(Form.Handle, WM_SETNOFRAME); LCLIntf.ShowWindow(Form.Handle, SW_HIDE); FHiding := False; FixF12_ActiveEditor; Exit; end; WM_BOUNDTODESIGNTABSHEET: begin KillTimer(Form.Handle, WM_BOUNDTODESIGNTABSHEET); if Assigned(FOnAdjustPageNeeded) then FOnAdjustPageNeeded(Self); Exit; end; end; end; case Msg.msg of {$IFDEF LCLWin32} // we need to correct ActiveEditor to right form // this code works correctly on Windows platform // (is necessery for selecting controls after form resizing). // in Linux platforms below code brings problems with QT (inactive form) LM_LBUTTONDOWN, LM_RBUTTONDOWN, LM_MBUTTONDOWN, LM_XBUTTONDOWN: if LastActiveSourceWindow <> nil then begin SourceEditorManagerIntf.ActiveSourceWindow := LastActiveSourceWindow; SourceEditorManagerIntf.ActiveEditor := LastActiveSourceWindow.ActiveEditor; end; {$ENDIF} // Prevent usage of parent hint (SourceEditorWindow), see issue #39217 CM_PARENTSHOWHINTCHANGED: begin Msg.Result := 0; Exit; end; end; FWndMethod(Msg); end; constructor TDesignForm.Create(AForm: TCustomForm); begin inherited Create(AForm); AForm.AddHandlerOnChangeBounds(@FormChangeBounds); FWndMethod := Form.WindowProc; Form.WindowProc := @WndMethod; end; destructor TDesignForm.Destroy; begin Form.WindowProc := FWndMethod; // ! important risky point :P Form.RemoveHandlerOnChangeBounds(@FormChangeBounds); inherited Destroy; end; { TDesignForms } destructor TDesignForms.Destroy; begin while Count > 0 do DeleteDesignForm(0); inherited Destroy; end; procedure TDesignForms.DeleteDesignForm(AIndex: Integer); var LDesignForm: TDesignForm; begin LDesignForm := Self[AIndex]; LDesignForm.Free; Delete(AIndex); end; function TDesignForms.Find(AForm: TCustomForm): TDesignForm; var LDesignForm: TDesignForm; begin // sometimes at some level of initialization form can not contain TIDesigner // (during ide run and when is oppened default project with some TForm1) Result := nil; if AForm = nil then Exit; for LDesignForm in Self do if LDesignForm.Form = AForm then Exit(LDesignForm); end; function TDesignForms.Find(ADesigner: TIDesigner): TDesignForm; var LDesignForm: TDesignForm; begin Result := nil; if ADesigner = nil then Exit; for LDesignForm in Self do if LDesignForm.Designer = ADesigner then Exit(LDesignForm); end; function TDesignForms.IndexOf(AForm: TCustomForm): Integer; var i: Integer; begin Result := -1; for i := 0 to Count - 1 do if Items[i].Form = AForm then Exit(i); end; procedure TDesignForms.Remove(AForm: TCustomForm); var LIndex: Integer; begin LIndex := IndexOf(AForm); if LIndex < 0 then Exit; Items[LIndex].Free; Delete(LIndex); end; procedure TDesignForms.RemoveAllAnchorDesigner; var LDesignForm: TDesignForm; LAnchorDesigner: TBasicAnchorDesigner; begin for LDesignForm in Self do begin LAnchorDesigner := LDesignForm.AnchorDesigner; if not Assigned(LAnchorDesigner) then Continue; LAnchorDesigner.Free; LDesignForm.AnchorDesigner := nil; end; end; initialization DesignForms := TDesignForms.Create; finalization FreeAndNil(DesignForms); end.