mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 03:48:07 +02:00
4420 lines
144 KiB
ObjectPascal
4420 lines
144 KiB
ObjectPascal
{ /***************************************************************************
|
|
designer.pp - Lazarus IDE unit
|
|
--------------------------------
|
|
|
|
Initial Revision : Sat May 10 23:15:32 CST 1999
|
|
|
|
|
|
***************************************************************************/
|
|
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
}
|
|
unit Designer;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
{off $DEFINE VerboseDesigner}
|
|
{off $DEFINE VerboseDesignerDraw}
|
|
{off $DEFINE VerboseDesignerSelect}
|
|
|
|
uses
|
|
// RTL + FCL
|
|
Types, Classes, Math, SysUtils, Variants, TypInfo,
|
|
// LCL
|
|
LCLType, LResources, LCLIntf, LMessages, InterfaceBase,
|
|
Forms, Controls, Graphics, Dialogs, ExtCtrls, Menus, ClipBrd,
|
|
// LazUtils
|
|
GraphType, GraphMath, LazFileUtils, LazFileCache, LazLoggerBase, LazUtilities,
|
|
// BuildIntf
|
|
ProjectIntf, ComponentReg,
|
|
// IDEIntf
|
|
IDEDialogs, PropEdits, PropEditUtils, ComponentEditors, MenuIntf,
|
|
IDEImagesIntf, FormEditingIntf, IDECommands, LazIDEIntf,
|
|
ObjectInspector, IdeIntfStrConsts,
|
|
// IDE
|
|
LazarusIDEStrConsts, EnvGuiOptions, EditorOptions, SourceEditor,
|
|
// Designer
|
|
AlignCompsDlg, SizeCompsDlg, ScaleCompsDlg, DesignerProcs, CustomFormEditor,
|
|
AskCompNameDlg, ControlSelection, ChangeClassDialog, ImgList;
|
|
|
|
type
|
|
TDesigner = class;
|
|
|
|
TOnGetSelectedComponentClass = procedure(Sender: TObject;
|
|
var RegisteredComponent: TRegisteredComponent) of object;
|
|
TOnSetDesigning = procedure(Sender: TObject; Component: TComponent;
|
|
Value: boolean) of object;
|
|
TOnPasteComponents = procedure(Sender: TObject; LookupRoot: TComponent;
|
|
TxtCompStream: TStream; Parent: TWinControl;
|
|
NewComponents: TFPList) of object;
|
|
TOnPastedComponents = procedure(Sender: TObject; LookupRoot: TComponent) of object;
|
|
TOnPersistentDeleted = procedure(Sender: TObject; APersistent: TPersistent)
|
|
of object;
|
|
TOnGetNonVisualCompIcon = procedure(Sender: TObject;
|
|
AComponent: TComponent; var ImageList: TCustomImageList; var ImageIndex: TImageIndex) of object;
|
|
TOnRenameComponent = procedure(Designer: TDesigner; AComponent: TComponent;
|
|
const NewName: string) of object;
|
|
TOnProcessCommand = procedure(Sender: TObject; Command: word;
|
|
var Handled: boolean) of object;
|
|
TOnComponentAdded = procedure(Sender: TObject; AComponent: TComponent;
|
|
ARegisteredComponent: TRegisteredComponent) of object;
|
|
TOnHasParentCandidates = function: Boolean of object;
|
|
|
|
TDesignerFlag = (
|
|
dfHasSized,
|
|
dfNeedPainting,
|
|
dfDuringPaintControl,
|
|
dfDestroyingForm,
|
|
dfShowEditorHints,
|
|
dfShowComponentCaptions,
|
|
dfShowNonVisualComponents
|
|
);
|
|
TDesignerFlags = set of TDesignerFlag;
|
|
|
|
TUndoItem = record
|
|
obj: string;
|
|
fieldName: string;
|
|
propInfo: PPropInfo;
|
|
oldVal, newVal: Variant;
|
|
compName, parentName: TComponentName;
|
|
opType: TUndoOpType;
|
|
isValid: Boolean;
|
|
GroupId: int64;
|
|
end;
|
|
|
|
{ TDesigner }
|
|
|
|
TDesigner = class(TComponentEditorDesigner)
|
|
private
|
|
FDesignerPopupMenu: TPopupMenu;
|
|
FDefaultFormBounds: TRect;
|
|
FLastFormBounds: TRect;
|
|
FFlags: TDesignerFlags;
|
|
FGridColor: TColor;
|
|
FMediator: TDesignerMediator;
|
|
FOnChangeParent: TNotifyEvent;
|
|
FOnPastedComponents: TOnPastedComponents;
|
|
FProcessingDesignerEvent: Integer;
|
|
FOnActivated: TNotifyEvent;
|
|
FOnCloseQuery: TNotifyEvent;
|
|
FOnShowObjectInspector: TNotifyEvent;
|
|
FOnShowAnchorEditor: TNotifyEvent;
|
|
FOnShowTabOrderEditor: TNotifyEvent;
|
|
FOnPersistentDeleted: TOnPersistentDeleted;
|
|
FOnGetNonVisualCompIcon: TOnGetNonVisualCompIcon;
|
|
FOnGetSelectedComponentClass: TOnGetSelectedComponentClass;
|
|
FOnModified: TNotifyEvent;
|
|
FOnPasteComponent: TOnPasteComponents;
|
|
FOnProcessCommand: TOnProcessCommand;
|
|
FOnPropertiesChanged: TNotifyEvent;
|
|
FOnRenameComponent: TOnRenameComponent;
|
|
FOnSaveAsXML: TNotifyEvent;
|
|
FOnSetDesigning: TOnSetDesigning;
|
|
FOnShowOptions: TNotifyEvent;
|
|
FOnComponentAdded: TOnComponentAdded;
|
|
FOnViewLFM: TNotifyEvent;
|
|
FOnForwardKeyToOI: TOnForwardKeyToOI;
|
|
FShiftState: TShiftState;
|
|
FTheFormEditor: TCustomFormEditor;
|
|
FPopupMenuComponentEditor: TBaseComponentEditor;
|
|
FUndoList: array of TUndoItem;
|
|
FUndoCurr: integer;
|
|
FUndoLock: integer;
|
|
FUndoGroupId: int64;
|
|
|
|
//hint stuff
|
|
FHintTimer: TTimer;
|
|
FHintWIndow: THintWindow;
|
|
|
|
// component drawing
|
|
FDDC: TDesignerDeviceContext;
|
|
FSurface: TBitmap;
|
|
|
|
procedure DrawNonVisualComponent(AComponent: TComponent);
|
|
function GetGridColor: TColor;
|
|
function GetGridSizeX: integer;
|
|
function GetGridSizeY: integer;
|
|
function GetIsControl: Boolean;
|
|
function GetShowBorderSpacing: boolean;
|
|
function GetShowComponentCaptions: boolean;
|
|
function GetShowEditorHints: boolean;
|
|
function GetShowGrid: boolean;
|
|
function GetSnapToGrid: boolean;
|
|
procedure HintTimer(Sender : TObject);
|
|
//procedure InvalidateWithParent(AComponent: TComponent);
|
|
procedure SetDefaultFormBounds(const AValue: TRect);
|
|
procedure SetGridColor(const AValue: TColor);
|
|
procedure SetGridSizeX(const AValue: integer);
|
|
procedure SetGridSizeY(const AValue: integer);
|
|
procedure SetMediator(const AValue: TDesignerMediator);
|
|
procedure SetPopupMenuComponentEditor(const AValue: TBaseComponentEditor);
|
|
procedure SetShowBorderSpacing(const AValue: boolean);
|
|
procedure SetShowComponentCaptions(const AValue: boolean);
|
|
procedure SetShowEditorHints(const AValue: boolean);
|
|
procedure SetShowGrid(const AValue: boolean);
|
|
procedure SetSnapToGrid(const AValue: boolean);
|
|
procedure DoOnForwardKeyToObjectInspector(Sender: TObject; Key: TUTF8Char);
|
|
protected
|
|
MouseDownComponent: TComponent;
|
|
MouseDownSender: TComponent;
|
|
MouseDownPos: TPoint;
|
|
MouseDownShift: TShiftState;
|
|
MouseUpPos: TPoint;
|
|
LastMouseMovePos: TPoint;
|
|
LastFormCursor: TCursor;
|
|
DeletingPersistent: TList;
|
|
LastPaintSender: TControl;
|
|
|
|
// event handlers for designed components
|
|
function PaintControl(Sender: TControl; TheMessage: TLMPaint): Boolean;
|
|
function SizeControl(Sender: TControl; TheMessage: TLMSize): Boolean;
|
|
function MoveControl(Sender: TControl; TheMessage: TLMMove): Boolean;
|
|
procedure MouseDownOnControl(Sender: TControl; var TheMessage: TLMMouse);
|
|
procedure MouseMoveOnControl(Sender: TControl; var TheMessage: TLMMouse);
|
|
procedure MouseUpOnControl(Sender: TControl; var TheMessage: TLMMouse);
|
|
procedure KeyDown(Sender: TControl; var TheMessage: TLMKEY);
|
|
procedure KeyUp(Sender: TControl; var TheMessage: TLMKEY);
|
|
function HandleSetCursor(var TheMessage: TLMessage): boolean;
|
|
procedure HandlePopupMenu(Sender: TControl; var Message: TLMContextMenu);
|
|
procedure GetMouseMsgShift(TheMessage: TLMMouse; out Shift: TShiftState;
|
|
out Button: TMouseButton);
|
|
function GetShowNonVisualComponents: boolean; override;
|
|
procedure SetShowNonVisualComponents(AValue: boolean); override;
|
|
|
|
// procedures for working with components and persistents
|
|
function GetDesignControl(AControl: TControl): TControl;
|
|
function DoDeleteSelectedPersistents: boolean;
|
|
procedure DoDeleteSelectedPersistentsAsync({%H-}Data: PtrInt);
|
|
procedure CutSelectionAsync({%H-}Data: PtrInt);
|
|
procedure DoSelectAll;
|
|
procedure DoDeletePersistent(APersistent: TPersistent; FreeIt: boolean);
|
|
function GetSelectedComponentClass: TRegisteredComponent;
|
|
procedure NudgePosition(DiffX, DiffY: Integer);
|
|
procedure NudgeSize(DiffX, DiffY: Integer);
|
|
procedure NudgeSelection(DiffX, DiffY: Integer); overload;
|
|
procedure NudgeSelection(SelectNext: Boolean); overload;
|
|
procedure SelectParentOfSelection;
|
|
function DoCopySelectionToClipboard: boolean;
|
|
function GetPasteParent: TWinControl;
|
|
procedure DoModified;
|
|
function DoPasteSelectionFromClipboard(PasteFlags: TComponentPasteSelectionFlags
|
|
): boolean;
|
|
function DoInsertFromStream(s: TStream; PasteParent: TWinControl;
|
|
PasteFlags: TComponentPasteSelectionFlags): Boolean;
|
|
|
|
function DoUndo: Boolean;
|
|
function DoRedo: Boolean;
|
|
procedure ExecuteUndoItem(IsActUndo: boolean);
|
|
procedure SetNextUndoGroupId; inline;
|
|
|
|
procedure DoShowAnchorEditor;
|
|
procedure DoShowTabOrderEditor;
|
|
procedure DoShowObjectInspector;
|
|
type
|
|
TChangeOrderAction = (
|
|
coaMoveToFront,
|
|
coaMoveToBack,
|
|
coaForwardOne,
|
|
coaBackOne
|
|
);
|
|
procedure DoChangeZOrder(TheAction: TChangeOrderAction);
|
|
|
|
procedure NotifyComponentAdded(AComponent: TComponent);
|
|
function ComponentClassAtPos(const AClass: TComponentClass;
|
|
const APos: TPoint; const UseRootAsDefault,
|
|
IgnoreHidden: boolean): TComponent;
|
|
procedure SetTempCursor(ARoot: TWinControl; ACursor: TCursor);
|
|
|
|
// popup menu
|
|
procedure BuildPopupMenu;
|
|
procedure DesignerPopupMenuPopup(Sender: TObject);
|
|
procedure ComponentEditorVerbMenuItemClick(Sender: TObject);
|
|
procedure AlignPopupMenuClick(Sender: TObject);
|
|
procedure MirrorHorizontalPopupMenuClick(Sender: TObject);
|
|
procedure MirrorVerticalPopupMenuClick(Sender: TObject);
|
|
procedure ScalePopupMenuClick(Sender: TObject);
|
|
procedure SizePopupMenuClick(Sender: TObject);
|
|
procedure ResetPopupMenuClick(Sender: TObject);
|
|
procedure AnchorEditorMenuClick(Sender: TObject);
|
|
procedure TabOrderMenuClick(Sender: TObject);
|
|
procedure OrderMoveToFrontMenuClick(Sender: TObject);
|
|
procedure OrderMoveToBackMenuClick(Sender: TObject);
|
|
procedure OrderForwardOneMenuClick(Sender: TObject);
|
|
procedure OrderBackOneMenuClick(Sender: TObject);
|
|
procedure CopyMenuClick(Sender: TObject);
|
|
procedure CutMenuClick(Sender: TObject);
|
|
procedure PasteMenuClick(Sender: TObject);
|
|
procedure DeleteSelectionMenuClick(Sender: TObject);
|
|
procedure SelectAllMenuClick(Sender: TObject);
|
|
procedure ChangeClassMenuClick(Sender: TObject);
|
|
procedure ChangeParentMenuClick(Sender: TObject);
|
|
procedure ShowNonVisualComponentsMenuClick(Sender: TObject);
|
|
procedure SnapToGridOptionMenuClick(Sender: TObject);
|
|
procedure ShowOptionsMenuItemClick(Sender: TObject);
|
|
procedure SnapToGuideLinesOptionMenuClick(Sender: TObject);
|
|
procedure ViewLFMMenuClick(Sender: TObject);
|
|
procedure SaveAsXMLMenuClick(Sender: TObject);
|
|
procedure CenterFormMenuClick(Sender: TObject);
|
|
|
|
// hook
|
|
function GetPropertyEditorHook: TPropertyEditorHook; override;
|
|
function DoFormActivated(Active: boolean): boolean;
|
|
function DoFormCloseQuery: boolean;
|
|
|
|
property PopupMenuComponentEditor: TBaseComponentEditor read FPopupMenuComponentEditor write SetPopupMenuComponentEditor;
|
|
public
|
|
Selection: TControlSelection;
|
|
DDC: TDesignerDeviceContext;
|
|
|
|
constructor Create(TheDesignerForm: TCustomForm; AControlSelection: TControlSelection);
|
|
procedure PrepareFreeDesigner(AFreeComponent: boolean); override;
|
|
procedure DisconnectComponent; override;
|
|
destructor Destroy; override;
|
|
|
|
procedure Modified; override;
|
|
procedure SelectOnlyThisComponent(AComponent: TComponent); override;
|
|
function CopySelection: boolean; override;
|
|
function CutSelection: boolean; override;
|
|
function CanCopy: Boolean; override;
|
|
function CanPaste: Boolean; override;
|
|
function PasteSelection(PasteFlags: TComponentPasteSelectionFlags): boolean; override;
|
|
function ClearSelection: boolean; override;
|
|
function DeleteSelection: boolean; override;
|
|
function CopySelectionToStream(AllComponentsStream: TStream): boolean; override;
|
|
function InsertFromStream(s: TStream; Parent: TWinControl;
|
|
PasteFlags: TComponentPasteSelectionFlags): Boolean; override;
|
|
function InvokeComponentEditor(AComponent: TComponent): boolean; override;
|
|
function ChangeClass: boolean; override;
|
|
|
|
procedure DoProcessCommand(Sender: TObject; var Command: word;
|
|
var Handled: boolean);
|
|
|
|
function CanUndo: Boolean; override;
|
|
function CanRedo: Boolean; override;
|
|
function Undo: Boolean; override;
|
|
function Redo: Boolean; override;
|
|
function AddUndoAction(const aPersistent: TPersistent; aOpType: TUndoOpType;
|
|
StartNewGroup: boolean; aFieldName: string; const aOldVal, aNewVal: Variant): boolean; override;
|
|
function IsUndoLocked: boolean; override;
|
|
procedure ClearUndoItem(AIndex: Integer);
|
|
procedure AddComponent(const NewRegisteredComponent: TRegisteredComponent;
|
|
const NewComponentClass: TComponentClass; const NewParent: TComponent;
|
|
const NewLeft, NewTop, NewWidth, NewHeight: Integer); override;
|
|
procedure AddComponentCheckParent(var NewParent: TComponent;
|
|
const OriginComponent: TComponent; const OriginWinControl: TWinControl;
|
|
const NewComponentClass: TComponentClass); override;
|
|
|
|
function NonVisualComponentLeftTop(AComponent: TComponent): TPoint;
|
|
function NonVisualComponentAtPos(X, Y: integer): TComponent;
|
|
procedure MoveNonVisualComponentIntoForm(AComponent: TComponent);
|
|
procedure MoveNonVisualComponentsIntoForm;
|
|
function WinControlAtPos(x,y: integer; UseRootAsDefault,
|
|
IgnoreHidden: boolean): TWinControl;
|
|
function ControlAtPos(x,y: integer; UseRootAsDefault,
|
|
IgnoreHidden: boolean): TControl;
|
|
function ComponentAtPos(x,y: integer; UseRootAsDefault,
|
|
IgnoreHidden: boolean): TComponent;
|
|
function GetDesignedComponent(AComponent: TComponent): TComponent;
|
|
function GetComponentEditorForSelection: TBaseComponentEditor;
|
|
function GetShiftState: TShiftState; override;
|
|
|
|
procedure AddComponentEditorMenuItems(AComponentEditor: TBaseComponentEditor;
|
|
ClearOldOnes: boolean);
|
|
|
|
function IsDesignMsg(Sender: TControl;
|
|
var TheMessage: TLMessage): Boolean; override;
|
|
procedure UTF8KeyPress(var UTF8Key: TUTF8Char); override;
|
|
function UniqueName(const BaseName: string): string; override;
|
|
Procedure RemovePersistentAndChildren(APersistent: TPersistent);
|
|
procedure Notification({%H-}AComponent: TComponent;
|
|
Operation: TOperation); override;
|
|
procedure ValidateRename(AComponent: TComponent;
|
|
const CurName, NewName: string); override;
|
|
function CreateUniqueComponentName(const AClassName: string): string; override;
|
|
|
|
procedure PaintGrid; override;
|
|
procedure PaintClientGrid(AWinControl: TWinControl;
|
|
aDDC: TDesignerDeviceContext);
|
|
procedure DrawNonVisualComponents(aDDC: TDesignerDeviceContext);
|
|
procedure DrawDesignerItems(OnlyIfNeeded: boolean); override;
|
|
procedure CheckFormBounds;
|
|
procedure DoPaintDesignerItems;
|
|
function ComponentIsIcon(AComponent: TComponent): boolean;
|
|
function GetParentFormRelativeClientOrigin(AComponent: TComponent): TPoint;
|
|
public
|
|
property Flags: TDesignerFlags read FFlags;
|
|
property GridSizeX: integer read GetGridSizeX write SetGridSizeX;
|
|
property GridSizeY: integer read GetGridSizeY write SetGridSizeY;
|
|
property GridColor: TColor read GetGridColor write SetGridColor;
|
|
property IsControl: Boolean read GetIsControl;
|
|
property Mediator: TDesignerMediator read FMediator write SetMediator;
|
|
property ProcessingDesignerEvent: Integer read FProcessingDesignerEvent;
|
|
property OnActivated: TNotifyEvent read FOnActivated write FOnActivated;
|
|
property OnCloseQuery: TNotifyEvent read FOnCloseQuery write FOnCloseQuery;
|
|
property OnPersistentDeleted: TOnPersistentDeleted
|
|
read FOnPersistentDeleted write FOnPersistentDeleted;
|
|
property OnGetNonVisualCompIcon: TOnGetNonVisualCompIcon
|
|
read FOnGetNonVisualCompIcon write FOnGetNonVisualCompIcon;
|
|
property OnGetSelectedComponentClass: TOnGetSelectedComponentClass
|
|
read FOnGetSelectedComponentClass
|
|
write FOnGetSelectedComponentClass;
|
|
property OnProcessCommand: TOnProcessCommand
|
|
read FOnProcessCommand write FOnProcessCommand;
|
|
property OnModified: TNotifyEvent read FOnModified write FOnModified;
|
|
property OnPasteComponents: TOnPasteComponents read FOnPasteComponent
|
|
write FOnPasteComponent;
|
|
property OnPastedComponents: TOnPastedComponents read FOnPastedComponents
|
|
write FOnPastedComponents;
|
|
property OnPropertiesChanged: TNotifyEvent
|
|
read FOnPropertiesChanged write FOnPropertiesChanged;
|
|
property OnRenameComponent: TOnRenameComponent
|
|
read FOnRenameComponent write FOnRenameComponent;
|
|
property OnSetDesigning: TOnSetDesigning read FOnSetDesigning write FOnSetDesigning;
|
|
property OnComponentAdded: TOnComponentAdded read FOnComponentAdded
|
|
write FOnComponentAdded;
|
|
property OnShowOptions: TNotifyEvent read FOnShowOptions write FOnShowOptions;
|
|
property OnViewLFM: TNotifyEvent read FOnViewLFM write FOnViewLFM;
|
|
property OnSaveAsXML: TNotifyEvent read FOnSaveAsXML write FOnSaveAsXML;
|
|
property OnShowObjectInspector: TNotifyEvent read FOnShowObjectInspector write FOnShowObjectInspector;
|
|
property OnShowAnchorEditor: TNotifyEvent read FOnShowAnchorEditor write FOnShowAnchorEditor;
|
|
property OnShowTabOrderEditor: TNotifyEvent read FOnShowTabOrderEditor write FOnShowTabOrderEditor;
|
|
property OnForwardKeyToObjectInspector: TOnForwardKeyToOI read FOnForwardKeyToOI
|
|
write FOnForwardKeyToOI;
|
|
property OnChangeParent: TNotifyEvent read FOnChangeParent write FOnChangeParent;
|
|
|
|
property ShowGrid: boolean read GetShowGrid write SetShowGrid;
|
|
property ShowBorderSpacing: boolean read GetShowBorderSpacing write SetShowBorderSpacing;
|
|
property ShowEditorHints: boolean read GetShowEditorHints write SetShowEditorHints;
|
|
property ShowComponentCaptions: boolean read GetShowComponentCaptions
|
|
write SetShowComponentCaptions;
|
|
property ShowNonVisualComponents: boolean read GetShowNonVisualComponents write SetShowNonVisualComponents;
|
|
property SnapToGrid: boolean read GetSnapToGrid write SetSnapToGrid;
|
|
property TheFormEditor: TCustomFormEditor read FTheFormEditor write FTheFormEditor;
|
|
property DefaultFormBounds: TRect read FDefaultFormBounds write SetDefaultFormBounds;
|
|
end;
|
|
|
|
const
|
|
DesignerMenuRootName = 'Designer';
|
|
var
|
|
DesignerMenuAlign: TIDEMenuCommand;
|
|
DesignerMenuMirrorHorizontal: TIDEMenuCommand;
|
|
DesignerMenuMirrorVertical: TIDEMenuCommand;
|
|
DesignerMenuScale: TIDEMenuCommand;
|
|
DesignerMenuSize: TIDEMenuCommand;
|
|
DesignerMenuReset: TIDEMenuCommand;
|
|
|
|
DesignerMenuAnchorEditor: TIDEMenuCommand;
|
|
DesignerMenuTabOrder: TIDEMenuCommand;
|
|
DesignerMenuOrderMoveToFront: TIDEMenuCommand;
|
|
DesignerMenuOrderMoveToBack: TIDEMenuCommand;
|
|
DesignerMenuOrderForwardOne: TIDEMenuCommand;
|
|
DesignerMenuOrderBackOne: TIDEMenuCommand;
|
|
|
|
DesignerMenuCut: TIDEMenuCommand;
|
|
DesignerMenuCopy: TIDEMenuCommand;
|
|
DesignerMenuPaste: TIDEMenuCommand;
|
|
DesignerMenuDeleteSelection: TIDEMenuCommand;
|
|
DesignerMenuSelectAll: TIDEMenuCommand;
|
|
|
|
DesignerMenuChangeClass: TIDEMenuCommand;
|
|
DesignerMenuChangeParent: TIDEMenuCommand;
|
|
DesignerMenuViewLFM: TIDEMenuCommand;
|
|
DesignerMenuSaveAsXML: TIDEMenuCommand;
|
|
DesignerMenuCenterForm: TIDEMenuCommand;
|
|
|
|
DesignerMenuShowNonVisualComponents: TIDEMenuCommand;
|
|
DesignerMenuSnapToGridOption: TIDEMenuCommand;
|
|
DesignerMenuSnapToGuideLinesOption: TIDEMenuCommand;
|
|
DesignerMenuShowOptions: TIDEMenuCommand;
|
|
|
|
|
|
procedure RegisterStandardDesignerMenuItems;
|
|
|
|
|
|
implementation
|
|
|
|
type
|
|
TCustomFormAccess = class(TCustomForm);
|
|
TControlAccess = class(TControl);
|
|
TWinControlAccess = class(TWinControl);
|
|
TComponentAccess = class(TComponent);
|
|
|
|
{ TComponentSearch }
|
|
|
|
TComponentSearch = class(TComponent)
|
|
public
|
|
Best: TComponent;
|
|
BestLevel: integer;
|
|
BestIsNonVisual: boolean;
|
|
Level: integer;
|
|
AtPos: TPoint;
|
|
MinClass: TComponentClass;
|
|
IgnoreHidden: boolean;
|
|
OnlyNonVisual: boolean;
|
|
IgnoreNonVisual: boolean;
|
|
Mediator: TDesignerMediator;
|
|
Root: TComponent;
|
|
procedure Gather(Child: TComponent);
|
|
procedure Search(ARoot: TComponent);
|
|
end;
|
|
|
|
{ TComponentSearch }
|
|
|
|
procedure TComponentSearch.Gather(Child: TComponent);
|
|
var
|
|
Control: TControl;
|
|
ChildBounds: TRect;
|
|
OldRoot: TComponent;
|
|
IsNonVisual: Boolean;
|
|
begin
|
|
if Assigned(Best) and BestIsNonVisual and (BestLevel < Level) then exit;
|
|
{$IFDEF VerboseDesignerSelect}
|
|
DebugLn(['TComponentSearch.Gather ',DbgSName(Child),' ',dbgs(AtPos),' MinClass=',DbgSName(MinClass)]);
|
|
{$ENDIF}
|
|
// check if child is at position
|
|
if Child is TControl then
|
|
begin
|
|
Control := TControl(Child);
|
|
if IgnoreHidden and (csNoDesignVisible in Control.ControlStyle) then
|
|
exit;
|
|
if csNoDesignSelectable in Control.ControlStyle then
|
|
exit;
|
|
if Control.Perform(CM_MASKHITTEST,0,Longint(SmallPoint(AtPos.X, AtPos.Y)))>0 then
|
|
exit;
|
|
end
|
|
else
|
|
Control := nil;
|
|
ChildBounds := GetParentFormRelativeBounds(Child);
|
|
{$IFDEF VerboseDesignerSelect}
|
|
DebugLn(['TComponentSearch.Gather PtInRect=',PtInRect(ChildBounds, AtPos),' ChildBounds=',dbgs(ChildBounds)]);
|
|
{$ENDIF}
|
|
if not PtInRect(ChildBounds, AtPos) then Exit;
|
|
|
|
if Assigned(Mediator) then
|
|
IsNonVisual := Mediator.ComponentIsIcon(Child)
|
|
else
|
|
IsNonVisual := DesignerProcs.ComponentIsNonVisual(Child);
|
|
|
|
if IsNonVisual then begin
|
|
if IgnoreNonVisual then exit;
|
|
if Assigned(IDEComponentsMaster)
|
|
and not IDEComponentsMaster.DrawNonVisualComponents(Root) then
|
|
Exit;
|
|
end;
|
|
|
|
if Child.InheritsFrom(MinClass) and (IsNonVisual or not OnlyNonVisual) then
|
|
begin
|
|
Best := Child;
|
|
BestIsNonVisual := IsNonVisual;
|
|
BestLevel := Level;
|
|
{$IFDEF VerboseDesignerSelect}
|
|
DebugLn(['TComponentSearch.Gather Best=',DbgSName(Best)]);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
// search in children
|
|
if (csInline in Child.ComponentState) or
|
|
(Assigned(Control) and not (csOwnedChildrenNotSelectable in Control.ControlStyle)) then
|
|
begin
|
|
{$IFDEF VerboseDesignerSelect}
|
|
DebugLn(['TComponentSearch.Gather search in children of ',DbgSName(Child)]);
|
|
{$ENDIF}
|
|
OldRoot := Root;
|
|
try
|
|
inc(Level);
|
|
if csInline in Child.ComponentState then
|
|
Root := Child;
|
|
{$IFDEF VerboseDesignerSelect}
|
|
DebugLn(['TComponentSearch.Gather Root=',DbgSName(Root)]);
|
|
{$ENDIF}
|
|
TComponentAccess(Child).GetChildren(@Gather, Root);
|
|
finally
|
|
dec(Level);
|
|
Root := OldRoot;
|
|
end;
|
|
{$IFDEF VerboseDesignerSelect}
|
|
DebugLn(['TComponentSearch.Gather searched in children of ',DbgSName(Child)]);
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
procedure TComponentSearch.Search(ARoot: TComponent);
|
|
begin
|
|
Root := ARoot;
|
|
Level := 1;
|
|
TComponentAccess(Root).GetChildren(@Gather, Root);
|
|
Level := 0;
|
|
end;
|
|
|
|
const
|
|
mk_lbutton = 1;
|
|
mk_rbutton = 2;
|
|
mk_shift = 4;
|
|
mk_control = 8;
|
|
mk_mbutton = $10;
|
|
|
|
procedure RegisterStandardDesignerMenuItems;
|
|
begin
|
|
DesignerMenuRoot:=RegisterIDEMenuRoot(DesignerMenuRootName);
|
|
|
|
// register the dynamic section for the component editor
|
|
DesignerMenuSectionComponentEditor:=RegisterIDEMenuSection(DesignerMenuRoot,
|
|
'Component editor section');
|
|
|
|
// register the custom dynamic section
|
|
DesignerMenuSectionCustomDynamic:=RegisterIDEMenuSection(DesignerMenuRoot,
|
|
'Custom dynamic section');
|
|
|
|
// register align section
|
|
DesignerMenuSectionAlign:=RegisterIDEMenuSection(DesignerMenuRoot,'Align section');
|
|
DesignerMenuAlign:=RegisterIDEMenuCommand(DesignerMenuSectionAlign,
|
|
'Align',fdmAlignMenu, nil, nil, nil, 'align');
|
|
DesignerMenuMirrorHorizontal:=RegisterIDEMenuCommand(DesignerMenuSectionAlign,
|
|
'Mirror horizontal',fdmMirrorHorizontal, nil, nil, nil, 'mirror_horizontal');
|
|
DesignerMenuMirrorVertical:=RegisterIDEMenuCommand(DesignerMenuSectionAlign,
|
|
'Mirror vertical',fdmMirrorVertical, nil, nil, nil, 'mirror_vertical');
|
|
DesignerMenuScale:=RegisterIDEMenuCommand(DesignerMenuSectionAlign,
|
|
'Scale',fdmScaleMenu, nil, nil, nil, 'scale');
|
|
DesignerMenuSize:=RegisterIDEMenuCommand(DesignerMenuSectionAlign,
|
|
'Size',fdmSizeMenu, nil, nil, nil, 'size');
|
|
DesignerMenuReset:=RegisterIDEMenuCommand(DesignerMenuSectionAlign,
|
|
'Reset', fdmResetMenu, nil, nil, nil, '');
|
|
|
|
// register tab and z-order section
|
|
DesignerMenuSectionOrder:=RegisterIDEMenuSection(DesignerMenuRoot,'Order section');
|
|
DesignerMenuAnchorEditor:=RegisterIDEMenuCommand(DesignerMenuSectionOrder,
|
|
'Anchor Editor',lisMenuViewAnchorEditor, nil, nil, nil, 'menu_view_anchor_editor');
|
|
DesignerMenuTabOrder:=RegisterIDEMenuCommand(DesignerMenuSectionOrder,
|
|
'Tab order',lisMenuViewTabOrder, nil, nil, nil, 'tab_order');
|
|
DesignerMenuSectionZOrder:=RegisterIDESubMenu(DesignerMenuSectionOrder,
|
|
'ZOrder section', fdmZOrder);
|
|
DesignerMenuOrderMoveToFront:=RegisterIDEMenuCommand(DesignerMenuSectionZOrder,
|
|
'Move to z order front',fdmOrderMoveTofront, nil, nil, nil, 'Order_move_front');
|
|
DesignerMenuOrderMoveToBack:=RegisterIDEMenuCommand(DesignerMenuSectionZOrder,
|
|
'Move to z order back',fdmOrderMoveToBack, nil, nil, nil, 'Order_move_back');
|
|
DesignerMenuOrderForwardOne:=RegisterIDEMenuCommand(DesignerMenuSectionZOrder,
|
|
'Move z order forward one',fdmOrderForwardOne, nil, nil, nil, 'Order_forward_one');
|
|
DesignerMenuOrderBackOne:=RegisterIDEMenuCommand(DesignerMenuSectionZOrder,
|
|
'Move z order backwards one',fdmOrderBackOne, nil, nil, nil, 'Order_back_one');
|
|
|
|
// register clipboard section
|
|
DesignerMenuSectionClipboard:=RegisterIDEMenuSection(DesignerMenuRoot,'Clipboard section');
|
|
DesignerMenuCut:=RegisterIDEMenuCommand(DesignerMenuSectionClipboard,
|
|
'Cut',lisCut, nil, nil, nil, 'laz_cut');
|
|
DesignerMenuCopy:=RegisterIDEMenuCommand(DesignerMenuSectionClipboard,
|
|
'Copy',lisCopy, nil, nil, nil, 'laz_copy');
|
|
DesignerMenuPaste:=RegisterIDEMenuCommand(DesignerMenuSectionClipboard,
|
|
'Paste',lisPaste, nil, nil, nil, 'laz_paste');
|
|
DesignerMenuDeleteSelection:=RegisterIDEMenuCommand(DesignerMenuSectionClipboard,
|
|
'Delete Selection',fdmDeleteSelection, nil, nil, nil, 'delete_selection');
|
|
DesignerMenuSelectAll:=RegisterIDEMenuCommand(DesignerMenuSectionClipboard,
|
|
'Select All',fdmSelectAll, nil, nil, nil, 'menu_select_all');
|
|
|
|
// register miscellaneous section
|
|
DesignerMenuSectionMisc:=RegisterIDEMenuSection(DesignerMenuRoot,'Miscellaneous section');
|
|
DesignerMenuChangeClass:=RegisterIDEMenuCommand(DesignerMenuSectionMisc,
|
|
'Change class',lisDlgChangeClass);
|
|
DesignerMenuChangeParent:=RegisterIDEMenuCommand(DesignerMenuSectionMisc,
|
|
'Change parent',lisChangeParent+' ...');
|
|
DesignerMenuViewLFM:=RegisterIDEMenuCommand(DesignerMenuSectionMisc,
|
|
'View LFM',lisViewSourceLfm);
|
|
DesignerMenuSaveAsXML:=RegisterIDEMenuCommand(DesignerMenuSectionMisc,
|
|
'Save as XML',fdmSaveFormAsXML);
|
|
DesignerMenuCenterForm:=RegisterIDEMenuCommand(DesignerMenuSectionMisc,
|
|
'Center form', lisCenterForm);
|
|
|
|
// register options section
|
|
DesignerMenuSectionOptions:=RegisterIDEMenuSection(DesignerMenuRoot,'Options section');
|
|
DesignerMenuShowNonVisualComponents:=RegisterIDEMenuCommand(DesignerMenuSectionMisc,
|
|
'Show non visual components',
|
|
lisDsgShowNonVisualComponents);
|
|
DesignerMenuShowNonVisualComponents.ShowAlwaysCheckable:=true;
|
|
DesignerMenuSnapToGridOption:=RegisterIDEMenuCommand(DesignerMenuSectionOptions,
|
|
'Snap to grid',fdmSnapToGridOption);
|
|
DesignerMenuSnapToGuideLinesOption:=RegisterIDEMenuCommand(DesignerMenuSectionOptions,
|
|
'Snap to guide lines',fdmSnapToGuideLinesOption);
|
|
DesignerMenuShowOptions:=RegisterIDEMenuCommand(DesignerMenuSectionOptions,
|
|
'Show options',lisOptions, nil, nil, nil, 'menu_environment_options');
|
|
end;
|
|
|
|
// inline
|
|
procedure TDesigner.SetNextUndoGroupId;
|
|
begin
|
|
LUIncreaseChangeStamp64(FUndoGroupId);
|
|
end;
|
|
|
|
constructor TDesigner.Create(TheDesignerForm: TCustomForm;
|
|
AControlSelection: TControlSelection);
|
|
var
|
|
LNonControlDesigner: INonControlDesigner;
|
|
i: integer;
|
|
begin
|
|
inherited Create;
|
|
//debugln(['TDesigner.Create Self=',dbgs(Pointer(Self)),' TheDesignerForm=',DbgSName(TheDesignerForm)]);
|
|
FForm := TheDesignerForm;
|
|
if FForm is INonControlDesigner then begin
|
|
LNonControlDesigner := FForm as INonControlDesigner;
|
|
FLookupRoot := LNonControlDesigner.LookupRoot;
|
|
Mediator := LNonControlDesigner.Mediator;
|
|
end
|
|
else if FForm is IFrameDesigner then
|
|
FLookupRoot := (FForm as IFrameDesigner).LookupRoot
|
|
else
|
|
FLookupRoot := FForm;
|
|
|
|
Selection := AControlSelection;
|
|
FFlags := [dfShowNonVisualComponents];
|
|
FGridColor := clGray;
|
|
|
|
FHintTimer := TTimer.Create(nil);
|
|
FHintTimer.Interval := 500;
|
|
FHintTimer.Enabled := False;
|
|
FHintTimer.OnTimer := @HintTimer;
|
|
|
|
FHintWindow := THintWindow.Create(nil);
|
|
|
|
FHIntWindow.Visible := False;
|
|
FHintWindow.HideInterval := 4000;
|
|
FHintWindow.AutoHide := True;
|
|
|
|
DDC:=TDesignerDeviceContext.Create;
|
|
LastFormCursor := crDefault;
|
|
DeletingPersistent:=TList.Create;
|
|
FPopupMenuComponentEditor := nil;
|
|
|
|
SetLength(FUndoList, 64);
|
|
for i := Low(FUndoList) to High(FUndoList) do
|
|
ClearUndoItem(i);
|
|
FUndoCurr := Low(FUndoList);
|
|
FUndoLock := 0;
|
|
FUndoState := ucsNone;
|
|
FUndoGroupId := 1;
|
|
end;
|
|
|
|
procedure TDesigner.AddComponent(
|
|
const NewRegisteredComponent: TRegisteredComponent;
|
|
const NewComponentClass: TComponentClass; const NewParent: TComponent;
|
|
const NewLeft, NewTop, NewWidth, NewHeight: Integer);
|
|
var
|
|
NewComponent: TComponent;
|
|
DisableAutoSize: Boolean;
|
|
NewControl: TControl;
|
|
begin
|
|
if NewParent=nil then exit;
|
|
if NewComponentClass = nil then exit;
|
|
|
|
// add a new component
|
|
Selection.RubberbandActive:=false;
|
|
Selection.Clear;
|
|
|
|
if not PropertyEditorHook.BeforeAddPersistent(Self, NewComponentClass, NewParent)
|
|
then begin
|
|
DebugLn('Note: TDesigner.AddComponent BeforeAddPersistent failed: ComponentClass=',
|
|
NewComponentClass.ClassName,' NewParent=',DbgSName(NewParent));
|
|
exit;
|
|
end;
|
|
|
|
// check cycles
|
|
if TheFormEditor.ClassDependsOnComponent(NewComponentClass, LookupRoot) then
|
|
begin
|
|
IDEMessageDialog(lisA2PInvalidCircularDependency,
|
|
Format(lisIsAThisCircularDependencyIsNotAllowed, [dbgsName(LookupRoot),
|
|
dbgsName(NewComponentClass), LineEnding]),
|
|
mtError,[mbOk],'');
|
|
exit;
|
|
end;
|
|
|
|
// create component and component interface
|
|
if ConsoleVerbosity>0 then
|
|
DebugLn(['AddComponent ',DbgSName(NewComponentClass),' Parent=',DbgSName(NewParent),' ',NewLeft,',',NewTop,',',NewWidth,',',NewHeight]);
|
|
DisableAutoSize:=true;
|
|
NewComponent := TheFormEditor.CreateComponent(
|
|
NewParent,NewComponentClass,'',
|
|
NewLeft,NewTop,NewWidth,NewHeight,DisableAutoSize);
|
|
if NewComponent=nil then exit;
|
|
if DisableAutoSize and (NewComponent is TControl) then
|
|
TControl(NewComponent).EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TDesigner.AddComponent'){$ENDIF};
|
|
TheFormEditor.FixupReferences(NewComponent); // e.g. frame references a datamodule
|
|
|
|
// modified
|
|
Modified;
|
|
|
|
// set initial properties
|
|
if NewComponent is TControl then begin
|
|
NewControl:=TControl(NewComponent);
|
|
//debugln(['AddComponent ',DbgSName(Self),' Bounds=',dbgs(NewControl.BoundsRect),' BaseBounds=',dbgs(NewControl.BaseBounds),' BaseParentClientSize=',dbgs(NewControl.BaseParentClientSize)]);
|
|
NewControl.Visible:=true;
|
|
if csSetCaption in NewControl.ControlStyle then
|
|
NewControl.Caption:=NewComponent.Name;
|
|
end;
|
|
if Assigned(FOnSetDesigning) then
|
|
FOnSetDesigning(Self,NewComponent,True);
|
|
|
|
if EnvironmentGuiOpts.CreateComponentFocusNameProperty then
|
|
ShowComponentNameDialog(LookupRoot,NewComponent); // ask user for name
|
|
|
|
// tell IDE about the new component (e.g. add it to the source)
|
|
NotifyComponentAdded(NewComponent);
|
|
|
|
// creation completed
|
|
// -> select new component
|
|
SelectOnlyThisComponent(NewComponent);
|
|
if Assigned(FOnComponentAdded) then // this resets the component palette to the selection tool
|
|
FOnComponentAdded(Self, NewComponent, NewRegisteredComponent);
|
|
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('NEW COMPONENT ADDED: Form.ComponentCount=',DbgS(Form.ComponentCount),
|
|
' NewComponent.Owner.Name=',NewComponent.Owner.Name);
|
|
{$ENDIF}
|
|
AddUndoAction(NewComponent, uopAdd, true, 'Name', '', NewComponent.Name);
|
|
end;
|
|
|
|
procedure TDesigner.AddComponentCheckParent(var NewParent: TComponent;
|
|
const OriginComponent: TComponent; const OriginWinControl: TWinControl;
|
|
const NewComponentClass: TComponentClass);
|
|
var
|
|
NewParentControl: TWinControl;
|
|
begin
|
|
//debugln(['TDesigner.AddComponentCheckParent Mediator=',DbgSName(Mediator),' NewParent=',DbgSName(NewParent),' NewComponentClass=',NewComponentClass.ClassName]);
|
|
if Mediator<>nil then begin
|
|
// mediator, non LCL components
|
|
if NewParent=nil then
|
|
NewParent:=OriginComponent;
|
|
while (NewParent<>nil)
|
|
and (not Mediator.ParentAcceptsChild(NewParent,NewComponentClass)) do
|
|
NewParent:=NewParent.GetParentComponent;
|
|
if NewParent=nil then
|
|
NewParent:=FLookupRoot;
|
|
end else if (FLookupRoot is TCustomForm) or (FLookupRoot is TCustomFrame)
|
|
then begin
|
|
// LCL controls
|
|
if NewParent<>nil then begin
|
|
if not (NewParent is TWinControl) then begin
|
|
debugln(['ERROR: AddComponent failed: AddClicked returned not a TWinControl: ',DbgSName(NewParent)]);
|
|
exit;
|
|
end;
|
|
NewParentControl := TWinControl(NewParent);
|
|
end else if OriginComponent is TWinControl then
|
|
NewParentControl := TWinControl(OriginComponent)
|
|
else
|
|
NewParentControl := OriginWinControl;
|
|
|
|
while (NewParentControl <> nil)
|
|
and not ControlAcceptsStreamableChildComponent(NewParentControl,
|
|
NewComponentClass,FLookupRoot)
|
|
do
|
|
NewParentControl := NewParentControl.Parent;
|
|
NewParent := NewParentControl;
|
|
//debugln(['AddComponent NewParent=',DbgSName(NewParent)]);
|
|
end else begin
|
|
// TDataModule
|
|
NewParent := FLookupRoot;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.PrepareFreeDesigner(AFreeComponent: boolean);
|
|
begin
|
|
// was FinalizeFreeDesigner
|
|
Include(FFlags, dfDestroyingForm);
|
|
// free or hide the form
|
|
TheFormEditor.DeleteComponent(FLookupRoot,AFreeComponent);
|
|
DisconnectComponent;
|
|
Free;
|
|
end;
|
|
|
|
procedure TDesigner.DisconnectComponent;
|
|
begin
|
|
//debugln(['TDesigner.DisconnectComponent Self=',dbgs(Pointer(Self))]);
|
|
inherited DisconnectComponent;
|
|
if Mediator<>nil then begin
|
|
Mediator.Designer:=nil;
|
|
FMediator:=nil;
|
|
end;
|
|
FLookupRoot:=nil;
|
|
end;
|
|
|
|
destructor TDesigner.Destroy;
|
|
begin
|
|
//debugln(['TDesigner.Destroy Self=',dbgs(Pointer(Self))]);
|
|
Application.RemoveAsyncCalls(Self);
|
|
PopupMenuComponentEditor := nil;
|
|
FreeAndNil(FDesignerPopupMenu);
|
|
FreeAndNil(FHintWIndow);
|
|
FreeAndNil(FHintTimer);
|
|
FreeAndNil(DDC);
|
|
FreeAndNil(DeletingPersistent);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TDesigner.NudgePosition(DiffX, DiffY : Integer);
|
|
begin
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('[TDesigner.NudgePosition]');
|
|
{$ENDIF}
|
|
if (Selection.SelectionForm<>Form)
|
|
or Selection.LookupRootSelected then exit;
|
|
Selection.MoveSelection(DiffX, DiffY, False);
|
|
Modified;
|
|
Form.Invalidate;
|
|
end;
|
|
|
|
procedure TDesigner.NudgeSize(DiffX, DiffY: Integer);
|
|
begin
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('[TDesigner.NudgeSize]');
|
|
{$ENDIF}
|
|
if (Selection.SelectionForm<>Form)
|
|
or Selection.LookupRootSelected then exit;
|
|
Selection.SizeSelection(DiffX, DiffY);
|
|
Modified;
|
|
end;
|
|
|
|
function ComponentsSortByLeft(Item1, Item2: Pointer): Integer;
|
|
var
|
|
Comp1: TComponent absolute Item1;
|
|
Comp2: TComponent absolute Item2;
|
|
L1, L2: Integer;
|
|
begin
|
|
L1 := GetComponentLeft(Comp1);
|
|
L2 := GetComponentLeft(Comp2);
|
|
if L1 < L2 then
|
|
Result := -1
|
|
else
|
|
if L1 > L2 then
|
|
Result := 1
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function ComponentsSortByTop(Item1, Item2: Pointer): Integer;
|
|
var
|
|
Comp1: TComponent absolute Item1;
|
|
Comp2: TComponent absolute Item2;
|
|
T1, T2: Integer;
|
|
begin
|
|
T1 := GetComponentTop(Comp1);
|
|
T2 := GetComponentTop(Comp2);
|
|
if T1 < T2 then
|
|
Result := -1
|
|
else
|
|
if T1 > T2 then
|
|
Result := 1
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
procedure TDesigner.NudgeSelection(DiffX, DiffY: Integer);
|
|
const
|
|
Delta = 50; // radius for searching components
|
|
var
|
|
List: TFPList;
|
|
Coord, Test: TPoint;
|
|
Current, AComponent: TComponent;
|
|
i: integer;
|
|
begin
|
|
if (Selection.SelectionForm <> Form) or
|
|
(Selection.SelectionForm.ComponentCount = 0) or
|
|
Selection.LookupRootSelected or
|
|
(Selection.Count <> 1) then Exit;
|
|
if not Selection[0].IsTComponent then Exit;
|
|
|
|
// create a list of components at the similar top/left
|
|
Current := TComponent(Selection[0].Persistent);
|
|
AComponent := nil;
|
|
List := TFPList.Create;
|
|
try
|
|
Coord := GetParentFormRelativeClientOrigin(Current);
|
|
if DiffX <> 0 then
|
|
begin
|
|
for i := 0 to Selection.SelectionForm.ComponentCount - 1 do
|
|
begin
|
|
AComponent := Selection.SelectionForm.Components[i];
|
|
if (AComponent = Current) or ComponentIsInvisible(AComponent) then
|
|
Continue;
|
|
Test := GetParentFormRelativeClientOrigin(AComponent);
|
|
if (Abs(Test.Y - Coord.Y) <= Delta) and
|
|
(Sign(Test.X - Coord.X) = Sign(DiffX)) then
|
|
List.Add(AComponent);
|
|
end;
|
|
if List.Count > 0 then
|
|
begin
|
|
List.Sort(@ComponentsSortByLeft);
|
|
if DiffX > 0 then
|
|
AComponent := TComponent(List[0])
|
|
else
|
|
AComponent := TComponent(List[List.Count - 1]);
|
|
end
|
|
else
|
|
AComponent := nil;
|
|
end
|
|
else
|
|
if DiffY <> 0 then
|
|
begin
|
|
for i := 0 to Selection.SelectionForm.ComponentCount - 1 do
|
|
begin
|
|
AComponent := Selection.SelectionForm.Components[i];
|
|
if (AComponent = Current) or ComponentIsInvisible(AComponent) then
|
|
Continue;
|
|
Test := GetParentFormRelativeClientOrigin(AComponent);
|
|
if (Abs(Test.X - Coord.X) <= Delta) and
|
|
(Sign(Test.Y - Coord.Y) = Sign(DiffY)) then
|
|
List.Add(AComponent);
|
|
end;
|
|
if List.Count > 0 then
|
|
begin
|
|
List.Sort(@ComponentsSortByTop);
|
|
if DiffY > 0 then
|
|
AComponent := TComponent(List[0])
|
|
else
|
|
AComponent := TComponent(List[List.Count - 1]);
|
|
end
|
|
else
|
|
AComponent := nil;
|
|
end;
|
|
finally
|
|
List.Free;
|
|
end;
|
|
if AComponent <> nil then
|
|
begin
|
|
Selection.AssignPersistent(AComponent);
|
|
Modified;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.NudgeSelection(SelectNext: Boolean);
|
|
|
|
function StepIndex(Index: Integer): Integer;
|
|
begin
|
|
Result := Index;
|
|
if SelectNext then
|
|
Inc(Result)
|
|
else
|
|
Dec(Result);
|
|
|
|
if Result >= Selection.SelectionForm.ComponentCount then
|
|
Result := 0
|
|
else
|
|
if Result < 0 then
|
|
Result := Selection.SelectionForm.ComponentCount - 1;
|
|
end;
|
|
|
|
var
|
|
Index, StartIndex: Integer;
|
|
AComponent: TComponent;
|
|
begin
|
|
if (Selection.SelectionForm <> Form) or
|
|
(Selection.SelectionForm.ComponentCount = 0) then Exit;
|
|
if (Selection.Count = 1) and Selection[0].IsTComponent then
|
|
Index := TComponent(Selection[0].Persistent).ComponentIndex
|
|
else
|
|
Index := -1;
|
|
|
|
Index := StepIndex(Index);
|
|
StartIndex := Index;
|
|
|
|
AComponent := nil;
|
|
while AComponent = nil do
|
|
begin
|
|
AComponent := Selection.SelectionForm.Components[Index];
|
|
if ComponentIsInvisible(AComponent) then
|
|
begin
|
|
AComponent := nil;
|
|
Index := StepIndex(Index);
|
|
if Index = StartIndex then
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
if AComponent <> nil then
|
|
begin
|
|
Selection.AssignPersistent(AComponent);
|
|
Modified;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.SelectParentOfSelection;
|
|
|
|
function ParentComponent(AComponent: TComponent): TComponent;
|
|
begin
|
|
Result := AComponent.GetParentComponent;
|
|
if (Result = nil) and ComponentIsIcon(AComponent) then
|
|
Result := AComponent.Owner;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
begin
|
|
// resizing or moving
|
|
if dfHasSized in FFlags then
|
|
begin
|
|
Selection.RestoreBounds;
|
|
Selection.ActiveGrabber := nil;
|
|
Selection.RubberbandActive := False;
|
|
LastMouseMovePos.X := -1;
|
|
Exclude(FFlags, dfHasSized);
|
|
MouseDownComponent := nil;
|
|
MouseDownSender := nil;
|
|
Exit;
|
|
end;
|
|
|
|
if Selection.OnlyInvisiblePersistentsSelected then
|
|
Exit;
|
|
|
|
if Selection.LookupRootSelected then
|
|
begin
|
|
SelectOnlyThisComponent(FLookupRoot);
|
|
Exit;
|
|
end;
|
|
|
|
// if not component moving then select parent
|
|
i := Selection.Count - 1;
|
|
while (i >= 0) and
|
|
(
|
|
Selection[i].ParentInSelection or
|
|
not Selection[i].IsTComponent or
|
|
(ParentComponent(TComponent(Selection[i].Persistent)) = nil)
|
|
)
|
|
do
|
|
Dec(i);
|
|
|
|
if i >= 0 then
|
|
SelectOnlyThisComponent(ParentComponent(TComponent(Selection[i].Persistent)));
|
|
end;
|
|
|
|
function TDesigner.CopySelectionToStream(AllComponentsStream: TStream): boolean;
|
|
|
|
function UnselectDistinctControls: boolean;
|
|
var
|
|
i: Integer;
|
|
AParent, CurParent: TWinControl;
|
|
begin
|
|
Result:=false;
|
|
AParent:=nil;
|
|
i:=0;
|
|
while i<Selection.Count do begin
|
|
if Selection[i].IsTControl then begin
|
|
// unselect controls from which the parent is selected too
|
|
if Selection[i].ParentInSelection then begin
|
|
Selection.Delete(i);
|
|
continue;
|
|
end;
|
|
|
|
// check if not the top level component is selected
|
|
CurParent:=TControl(Selection[i].Persistent).Parent;
|
|
if CurParent=nil then begin
|
|
IDEMessageDialog(lisCanNotCopyTopLevelComponent,
|
|
lisCopyingAWholeFormIsNotImplemented,
|
|
mtError,[mbOk]);
|
|
exit;
|
|
end;
|
|
|
|
// unselect all controls, that do not have the same parent
|
|
if (AParent=nil) then
|
|
AParent:=CurParent
|
|
else if (AParent<>CurParent) then begin
|
|
Selection.Delete(i);
|
|
continue;
|
|
end;
|
|
end;
|
|
inc(i);
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
BinCompStream: TMemoryStream;
|
|
TxtCompStream: TMemoryStream;
|
|
CurComponent: TComponent;
|
|
DestroyDriver: Boolean;
|
|
Writer: TWriter;
|
|
begin
|
|
Result:=false;
|
|
if (Selection.Count=0) then exit;
|
|
|
|
// Because controls will be pasted on a single parent,
|
|
// unselect all controls, that do not have the same parent
|
|
if not UnselectDistinctControls then exit;
|
|
|
|
for i:=0 to Selection.Count-1 do begin
|
|
if not Selection[i].IsTComponent then continue;
|
|
|
|
BinCompStream:=TMemoryStream.Create;
|
|
TxtCompStream:=TMemoryStream.Create;
|
|
try
|
|
// write component binary stream
|
|
try
|
|
CurComponent:=TComponent(Selection[i].Persistent);
|
|
|
|
DestroyDriver:=false;
|
|
Writer := CreateLRSWriter(BinCompStream,DestroyDriver);
|
|
try
|
|
Writer.OnWriteMethodProperty:=@BaseFormEditor1.WriteMethodPropertyEvent;
|
|
Writer.Root:=FLookupRoot;
|
|
Writer.WriteComponent(CurComponent);
|
|
finally
|
|
if DestroyDriver then Writer.Driver.Free;
|
|
Writer.Destroy;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
IDEMessageDialog(lisUnableToStreamSelectedComponents,
|
|
Format(lisThereWasAnErrorDuringWritingTheSelectedComponent, [
|
|
CurComponent.Name, CurComponent.ClassName, LineEnding, E.Message]),
|
|
mtError,[mbOk]);
|
|
exit;
|
|
end;
|
|
end;
|
|
BinCompStream.Position:=0;
|
|
// convert binary to text stream
|
|
try
|
|
LRSObjectBinaryToText(BinCompStream,TxtCompStream);
|
|
except
|
|
on E: Exception do begin
|
|
IDEMessageDialog(lisUnableConvertBinaryStreamToText,
|
|
Format(lisThereWasAnErrorWhileConvertingTheBinaryStreamOfThe, [
|
|
CurComponent.Name, CurComponent.ClassName, LineEnding, E.Message]),
|
|
mtError,[mbOk]);
|
|
exit;
|
|
end;
|
|
end;
|
|
// add text stream to the all stream
|
|
TxtCompStream.Position:=0;
|
|
AllComponentsStream.CopyFrom(TxtCompStream,TxtCompStream.Size);
|
|
finally
|
|
BinCompStream.Free;
|
|
TxtCompStream.Free;
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TDesigner.InsertFromStream(s: TStream; Parent: TWinControl;
|
|
PasteFlags: TComponentPasteSelectionFlags): Boolean;
|
|
begin
|
|
Result:=DoInsertFromStream(s,Parent,PasteFlags);
|
|
end;
|
|
|
|
function TDesigner.DoCopySelectionToClipboard: boolean;
|
|
var
|
|
AllComponentsStream: TMemoryStream;
|
|
AllComponentText: string;
|
|
begin
|
|
Result := false;
|
|
if Selection.Count = 0 then exit;
|
|
if Selection.OnlyInvisiblePersistentsSelected then exit;
|
|
|
|
AllComponentsStream:=TMemoryStream.Create;
|
|
try
|
|
// copy components to stream
|
|
if not CopySelectionToStream(AllComponentsStream) then exit;
|
|
SetLength(AllComponentText{%H-},AllComponentsStream.Size);
|
|
if AllComponentText<>'' then begin
|
|
AllComponentsStream.Position:=0;
|
|
AllComponentsStream.Read(AllComponentText[1],length(AllComponentText));
|
|
end;
|
|
|
|
// copy to clipboard
|
|
try
|
|
ClipBoard.AsText:=AllComponentText;
|
|
except
|
|
on E: Exception do begin
|
|
IDEMessageDialog(lisUnableCopyComponentsToClipboard,
|
|
Format(lisThereWasAnErrorWhileCopyingTheComponentStreamToCli,
|
|
[LineEnding, E.Message]),
|
|
mtError,[mbOk]);
|
|
exit;
|
|
end;
|
|
end;
|
|
finally
|
|
AllComponentsStream.Free;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TDesigner.GetPasteParent: TWinControl;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=nil;
|
|
for i:=0 to Selection.Count-1 do begin
|
|
if (Selection[i].IsTWinControl)
|
|
and (csAcceptsControls in
|
|
TWinControl(Selection[i].Persistent).ControlStyle)
|
|
and (not Selection[i].ParentInSelection) then begin
|
|
Result:=TWinControl(Selection[i].Persistent);
|
|
if GetLookupRootForComponent(Result)<>FLookupRoot then
|
|
Result:=nil;
|
|
break;
|
|
end;
|
|
end;
|
|
if (Result=nil) and (FLookupRoot is TWinControl) then
|
|
Result:=TWinControl(FLookupRoot);
|
|
end;
|
|
|
|
procedure TDesigner.DoModified;
|
|
begin
|
|
if Assigned(OnModified) then
|
|
OnModified(Self)
|
|
end;
|
|
|
|
function TDesigner.DoPasteSelectionFromClipboard(
|
|
PasteFlags: TComponentPasteSelectionFlags): boolean;
|
|
var
|
|
AllComponentText: string;
|
|
CurTextCompStream: TMemoryStream;
|
|
begin
|
|
Result:=false;
|
|
if not CanPaste then exit;
|
|
// read component stream from clipboard
|
|
AllComponentText:=ClipBoard.AsText;
|
|
if AllComponentText='' then exit;
|
|
CurTextCompStream:=TMemoryStream.Create;
|
|
try
|
|
CurTextCompStream.Write(AllComponentText[1],length(AllComponentText));
|
|
CurTextCompStream.Position:=0;
|
|
if not DoInsertFromStream(CurTextCompStream,nil,PasteFlags) then
|
|
exit;
|
|
finally
|
|
CurTextCompStream.Free;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TDesigner.DoInsertFromStream(s: TStream;
|
|
PasteParent: TWinControl; PasteFlags: TComponentPasteSelectionFlags): Boolean;
|
|
var
|
|
NewSelection: TPersistentSelectionList;
|
|
NewComps: TFPList;
|
|
|
|
procedure FindUniquePosition(AComponent: TComponent);
|
|
var
|
|
OverlappedComponent: TComponent;
|
|
P: TPoint;
|
|
AControl: TControl;
|
|
AParent: TWinControl;
|
|
i: Integer;
|
|
OverlappedControl: TControl;
|
|
begin
|
|
if AComponent is TControl then begin
|
|
AControl:=TControl(AComponent);
|
|
AParent:=AControl.Parent;
|
|
if AParent=nil then exit;
|
|
P:=Point(AControl.Left,AControl.Top);
|
|
i:=AParent.ControlCount-1;
|
|
while i>=0 do begin
|
|
OverlappedControl:=AParent.Controls[i];
|
|
if (NewComps.IndexOf(OverlappedControl)<0)
|
|
and (OverlappedControl.Left=P.X)
|
|
and (OverlappedControl.Top=P.Y) then begin
|
|
inc(P.X,NonVisualCompWidth);
|
|
inc(P.Y,NonVisualCompWidth);
|
|
if (P.X>AParent.ClientWidth-AControl.Width)
|
|
or (P.Y>AParent.ClientHeight-AControl.Height) then
|
|
break;
|
|
i:=AParent.ControlCount-1;
|
|
end else
|
|
dec(i);
|
|
end;
|
|
P.x:=Max(0,Min(P.x,AParent.ClientWidth-AControl.Width));
|
|
P.y:=Max(0,Min(P.y,AParent.ClientHeight-AControl.Height));
|
|
AControl.SetBounds(P.x,P.y,AControl.Width,AControl.Height);
|
|
end else begin
|
|
P:=GetParentFormRelativeTopLeft(AComponent);
|
|
repeat
|
|
OverlappedComponent:=NonVisualComponentAtPos(P.x,P.y);
|
|
if (OverlappedComponent=nil) then break;
|
|
inc(P.X,NonVisualCompWidth);
|
|
inc(P.Y,NonVisualCompWidth);
|
|
if (P.X+NonVisualCompWidth>Form.ClientWidth)
|
|
or (P.Y+NonVisualCompWidth>Form.ClientHeight) then
|
|
break;
|
|
until false;
|
|
AComponent.DesignInfo := LeftTopToDesignInfo(
|
|
SmallInt(Max(0, Min(P.x, Form.ClientWidth - NonVisualCompWidth))),
|
|
SmallInt(Max(0, Min(P.y, Form.ClientHeight - NonVisualCompWidth))));
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
NewComponent: TComponent;
|
|
begin
|
|
Result:=false;
|
|
//debugln('TDesigner.DoInsertFromStream A');
|
|
if (cpsfReplace in PasteFlags) and (not DeleteSelection) then exit;
|
|
|
|
//debugln('TDesigner.DoInsertFromStream B s.Size=',dbgs(s.Size),' S.Position=',dbgs(S.Position));
|
|
if PasteParent=nil then PasteParent:=GetPasteParent;
|
|
NewSelection:=TPersistentSelectionList.Create;
|
|
NewComps:=TFPList.Create;
|
|
try
|
|
Form.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TDesigner.DoInsertFromStream'){$ENDIF};
|
|
try
|
|
|
|
// read component stream from clipboard
|
|
if (s.Size<=S.Position) then begin
|
|
debugln('TDesigner.DoInsertFromStream Stream Empty s.Size=',dbgs(s.Size),' S.Position=',dbgs(S.Position));
|
|
exit;
|
|
end;
|
|
|
|
// create components and add to LookupRoot
|
|
FOnPasteComponent(Self,FLookupRoot,s,PasteParent,NewComps);
|
|
// add new component to new selection
|
|
for i:=0 to NewComps.Count-1 do begin
|
|
NewComponent:=TComponent(NewComps[i]);
|
|
NewSelection.Add(NewComponent);
|
|
// set new nice bounds
|
|
if cpsfFindUniquePositions in PasteFlags then
|
|
FindUniquePosition(NewComponent);
|
|
// finish adding component
|
|
NotifyComponentAdded(NewComponent);
|
|
Modified;
|
|
// add action in undo list
|
|
AddUndoAction(NewComponent, uopAdd, i = 0, 'Name', '', NewComponent.Name);
|
|
end;
|
|
|
|
if NewSelection.Count>0 then
|
|
FOnPastedComponents(Self,FLookupRoot);
|
|
|
|
finally
|
|
Form.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TDesigner.DoInsertFromStream'){$ENDIF};
|
|
end;
|
|
finally
|
|
NewComps.Free;
|
|
if NewSelection.Count>0 then
|
|
Selection.AssignSelection(NewSelection);
|
|
NewSelection.Free;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TDesigner.DoUndo: Boolean;
|
|
var GroupId: int64;
|
|
begin
|
|
repeat
|
|
Result := CanUndo;
|
|
if not Result then Exit;
|
|
Dec(FUndoCurr);
|
|
GroupId := FUndoList[FUndoCurr].GroupId;
|
|
ExecuteUndoItem(true);
|
|
until (FUndoCurr=Low(FUndoList)) or (GroupId <> FUndoList[FUndoCurr - 1].GroupId);
|
|
end;
|
|
|
|
function TDesigner.DoRedo: Boolean;
|
|
var GroupId: int64;
|
|
begin
|
|
repeat
|
|
Result := CanRedo;
|
|
if not Result then Exit;
|
|
ExecuteUndoItem(false);
|
|
GroupId := FUndoList[FUndoCurr].GroupId;
|
|
Inc(FUndoCurr);
|
|
until (FUndoCurr>High(FUndoList)) or (GroupId <> FUndoList[FUndoCurr].GroupId);
|
|
end;
|
|
|
|
procedure TDesigner.ExecuteUndoItem(IsActUndo: boolean);
|
|
var
|
|
AValue: Variant;
|
|
ValueStr, tmpFieldN: string;
|
|
tmpObj: TComponent;
|
|
|
|
procedure SetPublished; // published field
|
|
var
|
|
aPropType: PTypeInfo;
|
|
s: string;
|
|
i: integer;
|
|
begin
|
|
aPropType:=FUndoList[FUndoCurr].propInfo^.propType;
|
|
case aPropType^.Kind of
|
|
tkInteger, tkInt64:
|
|
begin
|
|
if (aPropType^.Name = 'TColor') or
|
|
(aPropType^.Name = 'TGraphicsColor') then
|
|
SetOrdProp(tmpObj, tmpFieldN, StringToColor(ValueStr))
|
|
else if aPropType^.Name = 'TCursor' then
|
|
SetOrdProp(tmpObj, tmpFieldN, StringToCursor(ValueStr))
|
|
else
|
|
SetOrdProp(tmpObj, tmpFieldN, StrToInt(ValueStr));
|
|
end;
|
|
tkChar, tkWChar, tkUChar:
|
|
begin
|
|
if Length(ValueStr) = 1 then
|
|
SetOrdProp(tmpObj, tmpFieldN, Ord(ValueStr[1]))
|
|
else if (ValueStr[1] = '#') then
|
|
begin
|
|
s := Copy(ValueStr, 2, Length(ValueStr) - 1);
|
|
if TryStrToInt(s, i) and (i >= 0) and (i <= High(Byte)) then
|
|
SetOrdProp(tmpObj, tmpFieldN, i);
|
|
end;
|
|
end;
|
|
tkEnumeration:
|
|
SetEnumProp(tmpObj, tmpFieldN, ValueStr);
|
|
tkFloat:
|
|
SetFloatProp(tmpObj, tmpFieldN, StrToFloat(ValueStr));
|
|
tkBool:
|
|
SetOrdProp(tmpObj, tmpFieldN, Integer(StrToBoolOI(ValueStr)));
|
|
tkString, tkLString, tkAString, tkUString, tkWString:
|
|
SetStrProp(tmpObj, tmpFieldN, ValueStr);
|
|
tkSet:
|
|
SetSetProp(tmpObj, tmpFieldN, ValueStr);
|
|
tkVariant:
|
|
SetVariantProp(tmpObj, tmpFieldN, AValue);
|
|
else
|
|
ShowMessage(Format('error: unknown TTypeKind(%d)', [Integer(aPropType^.Kind)]));
|
|
end;
|
|
end;
|
|
|
|
procedure SetUnPublished; // field is not published
|
|
var
|
|
NewParent: TComponent;
|
|
begin
|
|
if tmpObj is TControl then
|
|
begin
|
|
if CompareText(tmpFieldN,'Parent')=0 then // Special treatment for Parent.
|
|
begin
|
|
if FForm.Name <> ValueStr then // Find the parent by name.
|
|
NewParent := FForm.FindComponent(ValueStr)
|
|
else
|
|
NewParent := FForm;
|
|
Assert(NewParent is TWinControl, 'TDesigner.ExecuteUndoItem: New Parent "'
|
|
+ DbgS(NewParent) + '" is not a TWinControl.');
|
|
TControl(tmpObj).Parent := TWinControl(NewParent);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
CurTextCompStream: TMemoryStream;
|
|
SaveControlSelection: TControlSelection;
|
|
CompN: TComponentName;
|
|
begin
|
|
// Undo Add component
|
|
if (IsActUndo and (FUndoList[FUndoCurr].opType = uopAdd)) or
|
|
(not IsActUndo and (FUndoList[FUndoCurr].opType = uopDelete)) then
|
|
begin
|
|
Selection.BeginUpdate;
|
|
try
|
|
SaveControlSelection := TControlSelection.Create;
|
|
try
|
|
Inc(FUndoLock);
|
|
SaveControlSelection.Assign(Selection);
|
|
Selection.AssignPersistent(FForm.FindComponent(FUndoList[FUndoCurr].compName));
|
|
DeleteSelection;
|
|
finally
|
|
Dec(FUndoLock);
|
|
Selection.Assign(SaveControlSelection);
|
|
SaveControlSelection.Free;
|
|
end;
|
|
finally
|
|
Selection.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
// Undo Delete component
|
|
if (IsActUndo and (FUndoList[FUndoCurr].opType = uopDelete)) or
|
|
(not IsActUndo and (FUndoList[FUndoCurr].opType = uopAdd)) then
|
|
begin
|
|
CurTextCompStream := TMemoryStream.Create;
|
|
try
|
|
Inc(FUndoLock);
|
|
CurTextCompStream.Write(FUndoList[FUndoCurr].obj[1], Length(FUndoList[FUndoCurr].obj));
|
|
CurTextCompStream.Position := 0;
|
|
DoInsertFromStream(CurTextCompStream,
|
|
TWinControl(FForm.FindChildControl(FUndoList[FUndoCurr].parentName)), []);
|
|
finally
|
|
Dec(FUndoLock);
|
|
CurTextCompStream.Free;
|
|
end;
|
|
end;
|
|
|
|
// Undo Change properties of component
|
|
if FUndoList[FUndoCurr].opType = uopChange then
|
|
begin
|
|
Inc(FUndoLock);
|
|
try
|
|
tmpFieldN := FUndoList[FUndoCurr].fieldName;
|
|
if tmpFieldN = 'Name' then
|
|
begin
|
|
if IsActUndo then
|
|
CompN := String(FUndoList[FUndoCurr].newVal)
|
|
else
|
|
CompN := String(FUndoList[FUndoCurr].oldVal);
|
|
end
|
|
else
|
|
CompN := FUndoList[FUndoCurr].compName;
|
|
|
|
if FForm.Name <> CompN then
|
|
tmpObj := FForm.FindSubComponent(CompN)
|
|
else
|
|
tmpObj := FForm;
|
|
|
|
if IsActUndo then
|
|
AValue := FUndoList[FUndoCurr].oldVal
|
|
else
|
|
AValue := FUndoList[FUndoCurr].newVal;
|
|
if VarIsError(AValue) or VarIsEmpty(AValue) or VarIsNull(AValue) then
|
|
ShowMessage('error: invalid var type');
|
|
ValueStr := VarToStr(AValue);
|
|
//DebugLn(['TDesigner.ExecuteUndoItem: FForm=', FForm.Name, ', CompName=', CompN,
|
|
// ', FieldName=', tmpFieldN, ', tmpObj=', tmpObj, ', tmpStr=', ValueStr, ', IsActUndo=', IsActUndo]);
|
|
|
|
if FUndoList[FUndoCurr].propInfo<>nil then
|
|
SetPublished
|
|
else
|
|
SetUnPublished;
|
|
PropertyEditorHook.Modified(tmpObj);
|
|
finally
|
|
Dec(FUndoLock);
|
|
end;
|
|
end;
|
|
|
|
PropertyEditorHook.RefreshPropertyValues;
|
|
end;
|
|
|
|
procedure TDesigner.DoShowAnchorEditor;
|
|
begin
|
|
if Assigned(FOnShowAnchorEditor) then
|
|
FOnShowAnchorEditor(Self);
|
|
end;
|
|
|
|
procedure TDesigner.DoShowTabOrderEditor;
|
|
begin
|
|
if Assigned(FOnShowTabOrderEditor) then
|
|
FOnShowTabOrderEditor(Self);
|
|
end;
|
|
|
|
procedure TDesigner.DoShowObjectInspector;
|
|
begin
|
|
if Assigned(FOnShowObjectInspector) then
|
|
OnShowObjectInspector(Self);
|
|
end;
|
|
|
|
procedure TDesigner.DoChangeZOrder(TheAction: TChangeOrderAction);
|
|
var
|
|
Control: TControl;
|
|
Parent: TWinControl;
|
|
OI: TObjectInspectorDlg;
|
|
begin
|
|
if Selection.Count <> 1 then Exit;
|
|
if not Selection[0].IsTControl then Exit;
|
|
|
|
Control := TControl(Selection[0].Persistent);
|
|
Parent := Control.Parent;
|
|
if (Parent = nil) and (TheAction in [coaForwardOne, coaBackOne]) then Exit;
|
|
|
|
case TheAction of
|
|
coaMoveToFront: Control.BringToFront;
|
|
coaMoveToBack : Control.SendToBack;
|
|
coaForwardOne : Parent.SetControlIndex(Control, Parent.GetControlIndex(Control) + 1);
|
|
coaBackOne : Parent.SetControlIndex(Control, Parent.GetControlIndex(Control) - 1);
|
|
end;
|
|
|
|
// Ensure the order of controls in the OI now reflects the new ZOrder
|
|
// Unfortunately, if there is no parent, this code doesn't achieve a refresh
|
|
// of ComponentTree in the OI
|
|
if assigned(Parent) then
|
|
begin
|
|
Parent.ReAlign;
|
|
SelectOnlyThisComponent(Parent);
|
|
end;
|
|
SelectOnlyThisComponent(Control);
|
|
|
|
Modified;
|
|
OI := FormEditingHook.GetCurrentObjectInspector;
|
|
if Assigned(OI) then
|
|
OI.ComponentTree.BuildComponentNodes(True);
|
|
end;
|
|
|
|
procedure TDesigner.NotifyComponentAdded(AComponent: TComponent);
|
|
var
|
|
i: Integer;
|
|
SubContrl: TControl;
|
|
begin
|
|
try
|
|
if AComponent.Name='' then
|
|
AComponent.Name:=UniqueName(AComponent.ClassName);
|
|
// Iterating Controls is needed at least for Side1 and Side2 of TPairSplitter.
|
|
if AComponent is TWinControl then
|
|
for i:=0 to TWinControl(AComponent).ControlCount-1 do
|
|
begin
|
|
SubContrl:=TWinControl(AComponent).Controls[i];
|
|
if (SubContrl.Owner<>AComponent) and (SubContrl.Name='') then
|
|
SubContrl.Name:=UniqueName(SubContrl.ClassName);
|
|
end;
|
|
GlobalDesignHook.PersistentAdded(AComponent,false);
|
|
except
|
|
on E: Exception do
|
|
IDEMessageDialog('Error:',E.Message,mtError,[mbOk]);
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.SelectOnlyThisComponent(AComponent: TComponent);
|
|
begin
|
|
Selection.AssignPersistent(AComponent);
|
|
end;
|
|
|
|
function TDesigner.CopySelection: boolean;
|
|
begin
|
|
Result := DoCopySelectionToClipboard;
|
|
end;
|
|
|
|
function TDesigner.CutSelection: boolean;
|
|
begin
|
|
Result := DoCopySelectionToClipboard and DoDeleteSelectedPersistents;
|
|
end;
|
|
|
|
procedure TDesigner.CutSelectionAsync(Data: PtrInt);
|
|
begin
|
|
CutSelection;
|
|
end;
|
|
|
|
function TDesigner.CanCopy: Boolean;
|
|
begin
|
|
Result := (Selection.Count > 0) and
|
|
(Selection.SelectionForm = Form) and
|
|
Selection.OkToCopy and
|
|
not Selection.OnlyInvisiblePersistentsSelected and
|
|
not Selection.LookupRootSelected;
|
|
end;
|
|
|
|
function TDesigner.CanPaste: Boolean;
|
|
begin
|
|
Result:= Assigned(Form) and
|
|
Assigned(FLookupRoot) and
|
|
ClipBoard.HasFormat(CF_Text) and
|
|
not (csDestroying in FLookupRoot.ComponentState);
|
|
end;
|
|
|
|
function TDesigner.PasteSelection(
|
|
PasteFlags: TComponentPasteSelectionFlags): boolean;
|
|
begin
|
|
Result:=DoPasteSelectionFromClipboard(PasteFlags);
|
|
end;
|
|
|
|
function TDesigner.ClearSelection: boolean;
|
|
begin
|
|
Selection.Clear;
|
|
Result:=Selection.Count=0;
|
|
end;
|
|
|
|
function TDesigner.DeleteSelection: boolean;
|
|
begin
|
|
Result:=DoDeleteSelectedPersistents;
|
|
end;
|
|
|
|
function TDesigner.InvokeComponentEditor(AComponent: TComponent): boolean;
|
|
begin
|
|
Result:=false;
|
|
DebugLn('TDesigner.InvokeComponentEditor A ',AComponent.Name,':',AComponent.ClassName);
|
|
PopupMenuComponentEditor:=TheFormEditor.GetComponentEditor(AComponent);
|
|
if PopupMenuComponentEditor=nil then begin
|
|
DebugLn('TDesigner.InvokeComponentEditor WARNING: no component editor found for ',
|
|
AComponent.Name,':',AComponent.ClassName);
|
|
exit;
|
|
end;
|
|
DebugLn('TDesigner.InvokeComponentEditor B ',PopupMenuComponentEditor.ClassName);
|
|
try
|
|
PopupMenuComponentEditor.Edit;
|
|
Result:=true;
|
|
except
|
|
on E: Exception do begin
|
|
DebugLn('TDesigner.InvokeComponentEditor ERROR: ',E.Message);
|
|
IDEMessageDialog(Format(lisErrorIn, [PopupMenuComponentEditor.ClassName]),
|
|
Format(lisTheComponentEditorOfClassHasCreatedTheError,
|
|
[PopupMenuComponentEditor.ClassName, LineEnding, E.Message]),
|
|
mtError,[mbOk]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.ChangeClass: boolean;
|
|
begin
|
|
if (Selection.Count=1) and (not Selection.LookupRootSelected) then
|
|
Result:=ShowChangeClassDialog(Self,Selection[0].Persistent)=mrOK
|
|
else
|
|
Result:=false;
|
|
end;
|
|
|
|
procedure TDesigner.DoProcessCommand(Sender: TObject; var Command: word;
|
|
var Handled: boolean);
|
|
begin
|
|
if Assigned(OnProcessCommand) and (Command <> ecNone)
|
|
then begin
|
|
OnProcessCommand(Self,Command,Handled);
|
|
Handled := Handled or (Command = ecNone);
|
|
end;
|
|
|
|
if Handled then Exit;
|
|
|
|
case Command of
|
|
ecDesignerSelectParent : SelectParentOfSelection;
|
|
ecDesignerCopy : CopySelection;
|
|
ecDesignerCut : CutSelection;
|
|
ecDesignerPaste : PasteSelection([cpsfFindUniquePositions]);
|
|
ecDesignerMoveToFront : DoChangeZOrder(coaMoveToFront);
|
|
ecDesignerMoveToBack : DoChangeZOrder(coaMoveToBack);
|
|
ecDesignerForwardOne : DoChangeZOrder(coaForwardOne);
|
|
ecDesignerBackOne : DoChangeZOrder(coaBackOne );
|
|
ecDesignerToggleNonVisComps: ShowNonVisualComponents:=not ShowNonVisualComponents;
|
|
else
|
|
Exit;
|
|
end;
|
|
|
|
Handled := True;
|
|
end;
|
|
|
|
function TDesigner.CanUndo: Boolean;
|
|
begin
|
|
Result := Assigned(Form) and (FUndoCurr > Low(FUndoList)) and
|
|
(FUndoList[FUndoCurr - 1].isValid) and (FUndoList[FUndoCurr - 1].opType <> uopNone);
|
|
end;
|
|
|
|
function TDesigner.CanRedo: Boolean;
|
|
begin
|
|
Result := Assigned(Form) and (FUndoCurr <= High(FUndoList)) and
|
|
(FUndoList[FUndoCurr].isValid) and (FUndoList[FUndoCurr].opType <> uopNone);
|
|
end;
|
|
|
|
function TDesigner.Undo: Boolean;
|
|
begin
|
|
Result := DoUndo;
|
|
end;
|
|
|
|
function TDesigner.Redo: Boolean;
|
|
begin
|
|
Result := DoRedo;
|
|
end;
|
|
|
|
function TDesigner.AddUndoAction(const aPersistent: TPersistent;
|
|
aOpType: TUndoOpType; StartNewGroup: boolean; aFieldName: string;
|
|
const aOldVal, aNewVal: Variant): boolean;
|
|
|
|
procedure ShiftUndoList;
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := Low(FUndoList) + 1 to High(FUndoList) do
|
|
FUndoList[i - 1] := FUndoList[i];
|
|
ClearUndoItem(High(FUndoList));
|
|
Dec(FUndoCurr);
|
|
end;
|
|
|
|
var
|
|
i: integer;
|
|
SaveControlSelection: TControlSelection;
|
|
AStream: TStringStream;
|
|
APropInfo: PPropInfo;
|
|
Comp: TComponent;
|
|
begin
|
|
Result := (FUndoLock = 0);
|
|
if not Result then Exit;
|
|
|
|
APropInfo := GetPropInfo(aPersistent, aFieldName);
|
|
|
|
Inc(FUndoLock);
|
|
try
|
|
if FUndoCurr > High(FUndoList) then
|
|
ShiftUndoList;
|
|
|
|
// clear Redo items
|
|
i := FUndoCurr;
|
|
while (i <= High(FUndoList)) do
|
|
begin
|
|
ClearUndoItem(i);
|
|
Inc(i);
|
|
end;
|
|
|
|
if StartNewGroup then
|
|
SetNextUndoGroupId;
|
|
|
|
if (aOpType in [uopAdd, uopDelete]) and (FForm <> aPersistent) then
|
|
begin
|
|
Selection.BeginUpdate;
|
|
try
|
|
SaveControlSelection := TControlSelection.Create;
|
|
try
|
|
SaveControlSelection.Assign(Selection);
|
|
AStream := TStringStream.Create('');
|
|
try
|
|
Selection.AssignPersistent(aPersistent);
|
|
CopySelectionToStream(AStream);
|
|
FUndoList[FUndoCurr].obj := AStream.DataString;
|
|
finally
|
|
AStream.Free;
|
|
end;
|
|
finally
|
|
Selection.Assign(SaveControlSelection);
|
|
SaveControlSelection.Free;
|
|
end;
|
|
finally
|
|
Selection.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
// add to FUndoList
|
|
with FUndoList[FUndoCurr] do
|
|
begin
|
|
oldVal := aOldVal;
|
|
newVal := aNewVal;
|
|
fieldName := aFieldName;
|
|
compName := '';
|
|
parentName := '';
|
|
if aPersistent is TComponent then begin
|
|
Comp := TComponent(aPersistent);
|
|
compName := Comp.Name;
|
|
// Add owner to the name of a subcomponent.
|
|
if Assigned(Comp.Owner) and (Comp.Owner <> LookupRoot) then
|
|
compName := Comp.Owner.Name + '.' + compName;
|
|
if Comp.HasParent then
|
|
parentName := Comp.GetParentComponent.Name;
|
|
end;
|
|
opType := aOpType;
|
|
isValid := true;
|
|
GroupId := FUndoGroupId;
|
|
propInfo := APropInfo;
|
|
end;
|
|
Inc(FUndoCurr);
|
|
finally
|
|
Dec(FUndoLock);
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.IsUndoLocked: boolean;
|
|
begin
|
|
Result := FUndoLock > 0;
|
|
end;
|
|
|
|
procedure TDesigner.ClearUndoItem(AIndex: Integer);
|
|
begin
|
|
if (AIndex < 0) or (AIndex >= Length(FUndoList)) then Exit;
|
|
with FUndoList[AIndex] do
|
|
begin
|
|
obj := '';
|
|
fieldName := '';
|
|
VarClear(oldVal);
|
|
VarClear(newVal);
|
|
compName := '';
|
|
parentName := '';
|
|
opType := uopNone;
|
|
isValid := false;
|
|
GroupId := 0;
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.NonVisualComponentLeftTop(AComponent: TComponent): TPoint;
|
|
var
|
|
ParentForm: TPoint;
|
|
begin
|
|
Result.X := LeftFromDesignInfo(AComponent.DesignInfo);
|
|
Result.Y := TopFromDesignInfo(AComponent.DesignInfo);
|
|
// convert to lookuproot coords
|
|
if (AComponent.Owner <> FLookupRoot) then
|
|
begin
|
|
ParentForm:=GetParentFormRelativeClientOrigin(AComponent.Owner);
|
|
inc(Result.X,ParentForm.X);
|
|
inc(Result.Y,ParentForm.Y);
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.SetDefaultFormBounds(const AValue: TRect);
|
|
begin
|
|
FDefaultFormBounds:=AValue;
|
|
end;
|
|
|
|
procedure TDesigner.SetGridColor(const AValue: TColor);
|
|
begin
|
|
if GridColor=AValue then exit;
|
|
EnvironmentGuiOpts.GridColor:=AValue;
|
|
Form.Invalidate;
|
|
end;
|
|
|
|
procedure TDesigner.SetShowBorderSpacing(const AValue: boolean);
|
|
begin
|
|
if ShowBorderSpacing=AValue then exit;
|
|
EnvironmentGuiOpts.ShowBorderSpacing:=AValue;
|
|
Form.Invalidate;
|
|
end;
|
|
|
|
procedure TDesigner.SetShowComponentCaptions(const AValue: boolean);
|
|
begin
|
|
if AValue=ShowComponentCaptions then exit;
|
|
if AValue then
|
|
Include(FFlags, dfShowComponentCaptions)
|
|
else
|
|
Exclude(FFlags, dfShowComponentCaptions);
|
|
Form.Invalidate;
|
|
end;
|
|
|
|
function TDesigner.PaintControl(Sender: TControl; TheMessage: TLMPaint): Boolean;
|
|
var
|
|
OldDuringPaintControl: boolean;
|
|
begin
|
|
Result:=true;
|
|
|
|
{$IFDEF VerboseDsgnPaintMsg}
|
|
writeln('*** TDesigner.PaintControl A ',Sender.Name,':',Sender.ClassName,
|
|
' DC=',DbgS(TheMessage.DC));
|
|
{$ENDIF}
|
|
// Set flag
|
|
OldDuringPaintControl:=dfDuringPaintControl in FFlags;
|
|
Include(FFlags,dfDuringPaintControl);
|
|
|
|
// send the Paint message to the control, so that it paints itself
|
|
//writeln('TDesigner.PaintControl B ',Sender.Name);
|
|
Sender.Dispatch(TheMessage);
|
|
//writeln('TDesigner.PaintControl C ',Sender.Name,' DC=',DbgS(TheMessage.DC));
|
|
|
|
// paint the Designer stuff
|
|
if TheMessage.DC <> 0 then begin
|
|
Include(FFlags,dfNeedPainting);
|
|
|
|
if Sender is TControl then
|
|
DDC.SetDC(Form, TControl(Sender), TheMessage.DC)
|
|
else
|
|
if Sender <> nil then
|
|
DDC.SetDC(Form, Sender.Parent, TheMessage.DC)
|
|
else
|
|
DDC.SetDC(Form, nil, TheMessage.DC);
|
|
{$IFDEF VerboseDesignerDraw}
|
|
writeln('TDesigner.PaintControl D ',dbgsname(Sender),
|
|
' DC=',DbgS(DDC.DC,8),
|
|
{' FormOrigin=',DDC.FormOrigin.X,',',DDC.FormOrigin.Y,}
|
|
' DCOrigin=',DDC.DCOrigin.X,',',DDC.DCOrigin.Y,
|
|
' FormClientOrigin=',DDC.FormClientOrigin.X,',',DDC.FormClientOrigin.Y
|
|
);
|
|
{$ENDIF}
|
|
if LastPaintSender=Sender then begin
|
|
//writeln('NOTE: TDesigner.PaintControl E control painted twice: ',
|
|
// Sender.Name,':',Sender.ClassName,' DC=',DbgS(TheMessage.DC));
|
|
//RaiseGDBException('');
|
|
end;
|
|
LastPaintSender:=Sender;
|
|
|
|
if IsDesignerDC(Form.Handle, TheMessage.DC) then
|
|
DoPaintDesignerItems
|
|
else
|
|
begin
|
|
// client grid
|
|
if (Sender is TWinControl) and (csAcceptsControls in Sender.ControlStyle) then
|
|
PaintClientGrid(TWinControl(Sender),DDC);
|
|
end;
|
|
|
|
// clean up
|
|
DDC.Clear;
|
|
end;
|
|
//writeln('TDesigner.PaintControl END ',Sender.Name);
|
|
|
|
if not OldDuringPaintControl then
|
|
Exclude(FFlags,dfDuringPaintControl);
|
|
end;
|
|
|
|
function TDesigner.HandleSetCursor(var TheMessage: TLMessage): boolean;
|
|
begin
|
|
Result := Lo(DWord(TheMessage.LParam)) = HTCLIENT;
|
|
if Result then
|
|
begin
|
|
SetTempCursor(Form, LastFormCursor);
|
|
TheMessage.Result := 1;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.HandlePopupMenu(Sender: TControl; var Message: TLMContextMenu);
|
|
begin
|
|
//debugln(['TDesigner.HandlePopupMenu ',DbgSName(Sender),' ',Message.XPos,',',Message.YPos]);
|
|
{$IFDEF EnableDesignerPopupRightClick}
|
|
if Message.XPos = -1 then
|
|
{$ENDIF}
|
|
begin
|
|
PopupMenuComponentEditor := GetComponentEditorForSelection;
|
|
BuildPopupMenu;
|
|
if (Message.XPos = -1) and (Message.YPos = -1) then
|
|
// called from keyboard (VK_APPS or Shift+F10)
|
|
with Form.ClientToScreen(Point(Selection.Left, Selection.Top)) do
|
|
FDesignerPopupMenu.Popup(X, Y)
|
|
else
|
|
// coordinates can be negative with multiple monitors
|
|
FDesignerPopupMenu.Popup(Message.XPos, Message.YPos);
|
|
end;
|
|
Message.Result := 1;
|
|
end;
|
|
|
|
procedure TDesigner.GetMouseMsgShift(TheMessage: TLMMouse; out
|
|
Shift: TShiftState; out Button: TMouseButton);
|
|
begin
|
|
Shift := [];
|
|
Button := mbLeft;
|
|
if (TheMessage.Keys and MK_Shift) = MK_Shift then
|
|
Include(Shift, ssShift);
|
|
if (TheMessage.Keys and MK_Control) = MK_Control then
|
|
Include(Shift, ssCtrl);
|
|
|
|
if GetKeyState(VK_MENU) < 0 then Include(Shift, ssAlt);
|
|
if (GetKeyState(VK_LWIN) < 0) or (GetKeyState(VK_RWIN) < 0) then Include(Shift, ssMeta);
|
|
|
|
case TheMessage.Msg of
|
|
LM_LBUTTONUP,LM_LBUTTONDBLCLK,LM_LBUTTONTRIPLECLK,LM_LBUTTONQUADCLK:
|
|
begin
|
|
Include(Shift, ssLeft);
|
|
Button := mbLeft;
|
|
end;
|
|
LM_MBUTTONUP,LM_MBUTTONDBLCLK,LM_MBUTTONTRIPLECLK,LM_MBUTTONQUADCLK:
|
|
begin
|
|
Include(Shift, ssMiddle);
|
|
Button := mbMiddle;
|
|
end;
|
|
LM_RBUTTONUP,LM_RBUTTONDBLCLK,LM_RBUTTONTRIPLECLK,LM_RBUTTONQUADCLK:
|
|
begin
|
|
Include(Shift, ssRight);
|
|
Button := mbRight;
|
|
end;
|
|
else
|
|
if (TheMessage.Keys and MK_MButton) <> 0 then
|
|
begin
|
|
Include(Shift, ssMiddle);
|
|
Button := mbMiddle;
|
|
end;
|
|
if (TheMessage.Keys and MK_RButton) <> 0 then
|
|
begin
|
|
Include(Shift, ssRight);
|
|
Button := mbRight;
|
|
end;
|
|
if (TheMessage.Keys and MK_LButton) <> 0 then
|
|
begin
|
|
Include(Shift, ssLeft);
|
|
Button := mbLeft;
|
|
end;
|
|
if (TheMessage.Keys and MK_XBUTTON1) <> 0 then
|
|
begin
|
|
Include(Shift, ssExtra1);
|
|
Button := mbExtra1;
|
|
end;
|
|
if (TheMessage.Keys and MK_XBUTTON2) <> 0 then
|
|
begin
|
|
Include(Shift, ssExtra2);
|
|
Button := mbExtra2;
|
|
end;
|
|
end;
|
|
|
|
case TheMessage.Msg of
|
|
LM_LBUTTONDBLCLK,LM_MBUTTONDBLCLK,LM_RBUTTONDBLCLK,LM_XBUTTONDBLCLK:
|
|
Include(Shift, ssDouble);
|
|
LM_LBUTTONTRIPLECLK,LM_MBUTTONTRIPLECLK,LM_RBUTTONTRIPLECLK,LM_XBUTTONTRIPLECLK:
|
|
Include(Shift, ssTriple);
|
|
LM_LBUTTONQUADCLK,LM_MBUTTONQUADCLK,LM_RBUTTONQUADCLK,LM_XBUTTONQUADCLK:
|
|
Include(Shift, ssQuad);
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.GetDesignControl(AControl: TControl): TControl;
|
|
// checks if AControl is designable.
|
|
// if not check Owner.
|
|
// AControl can be a TNonControlDesignerForm
|
|
var
|
|
OwnerControl: TControl;
|
|
AComponent: TComponent;
|
|
begin
|
|
Result:=AControl;
|
|
if (Result=nil) or (Result=LookupRoot) or (Result.Owner=LookupRoot) then exit;
|
|
if Result=Form then exit;
|
|
if (Result.Owner is TControl) then begin
|
|
OwnerControl:=TControl(Result.Owner);
|
|
if (not (csOwnedChildrenNotSelectable in OwnerControl.ControlStyle)) then
|
|
exit;
|
|
Result:=GetDesignControl(OwnerControl);
|
|
end else begin
|
|
AComponent:=GetDesignedComponent(AControl);
|
|
if AComponent is TControl then
|
|
Result:=TControl(AComponent)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.SizeControl(Sender: TControl; TheMessage: TLMSize): Boolean;
|
|
begin
|
|
Result := True;
|
|
Sender.Dispatch(TheMessage);
|
|
if Selection.SelectionForm = Form then
|
|
begin
|
|
Selection.CheckForLCLChanges(True);
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.MoveControl(Sender: TControl; TheMessage: TLMMove): Boolean;
|
|
begin
|
|
Result := True;
|
|
Sender.Dispatch(TheMessage);
|
|
//debugln('*** TDesigner.MoveControl A ',Sender.Name,':',Sender.ClassName,' ',Selection.SelectionForm=Form,' ',not Selection.IsResizing,' ',Selection.IsSelected(Sender));
|
|
if Selection.SelectionForm = Form then
|
|
begin
|
|
if not Selection.CheckForLCLChanges(True) and (Sender = Form) and
|
|
Selection.LookupRootSelected then
|
|
begin
|
|
// the selected form was moved (nothing else has changed)
|
|
// Selection does not need an update, but properties like
|
|
// Form.Left/Top have to be updated in the OI
|
|
OnPropertiesChanged(Self);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.MouseDownOnControl(Sender: TControl; var TheMessage: TLMMouse);
|
|
var
|
|
CompIndex:integer;
|
|
SelectedCompClass: TRegisteredComponent;
|
|
ParentForm: TCustomForm;
|
|
Shift: TShiftState;
|
|
DesignSender: TControl;
|
|
Button: TMouseButton;
|
|
Handled: Boolean;
|
|
MouseDownControl: TControl;
|
|
p: types.TPoint;
|
|
begin
|
|
FHintTimer.Enabled := False;
|
|
FHintWindow.Visible := False;
|
|
|
|
Exclude(FFLags, dfHasSized);
|
|
SetCaptureControl(nil);
|
|
DesignSender := GetDesignControl(Sender);
|
|
ParentForm := GetDesignerForm(DesignSender);
|
|
//DebugLn(['TDesigner.MouseDownOnControl DesignSender=',dbgsName(DesignSender),' ParentForm=',dbgsName(ParentForm)]);
|
|
if (ParentForm = nil) then exit;
|
|
|
|
MouseDownPos := GetFormRelativeMousePosition(Form);
|
|
LastMouseMovePos := MouseDownPos;
|
|
MouseDownSender := nil;
|
|
MouseDownComponent := ComponentAtPos(MouseDownPos.X, MouseDownPos.Y, True, True);
|
|
if (MouseDownComponent = nil) then exit;
|
|
|
|
if ComponentIsIcon(MouseDownComponent) then
|
|
begin
|
|
if Assigned(IDEComponentsMaster) then
|
|
if not IDEComponentsMaster.DrawNonVisualComponents(FLookupRoot) then
|
|
begin
|
|
MouseDownComponent := nil;
|
|
Exit;
|
|
end;
|
|
MoveNonVisualComponentIntoForm(MouseDownComponent);
|
|
end;
|
|
|
|
MouseDownSender := DesignSender;
|
|
|
|
GetMouseMsgShift(TheMessage,Shift,Button);
|
|
MouseDownShift:=Shift;
|
|
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('************************************************************');
|
|
DbgOut('MouseDownOnControl');
|
|
DbgOut(' Sender=',dbgsName(Sender),' DesignSender=',dbgsName(DesignSender));
|
|
//write(' Msg=',TheMessage.Pos.X,',',TheMessage.Pos.Y);
|
|
//write(' Mouse=',MouseDownPos.X,',',MouseDownPos.Y);
|
|
//writeln('');
|
|
|
|
if (TheMessage.Keys and MK_Shift) = MK_Shift then
|
|
DbgOut(' Shift down')
|
|
else
|
|
DbgOut(' No Shift down');
|
|
|
|
if (TheMessage.Keys and MK_Control) = MK_Control then
|
|
DebugLn(', CTRL down')
|
|
else
|
|
DebugLn(', No CTRL down');
|
|
{$ENDIF}
|
|
|
|
if MouseDownComponent is TControl then
|
|
begin
|
|
MouseDownControl:=TControl(MouseDownComponent);
|
|
p:=MouseDownControl.ScreenToClient(Form.ClientToScreen(MouseDownPos));
|
|
if (csDesignInteractive in MouseDownControl.ControlStyle)
|
|
or (MouseDownControl.Perform(CM_DESIGNHITTEST, TheMessage.Keys, Longint(SmallPoint(p.X, p.Y))) > 0) then
|
|
begin
|
|
TControlAccess(MouseDownComponent).MouseDown(Button, Shift, p.X, p.Y);
|
|
Exit;
|
|
end;
|
|
end
|
|
else
|
|
p:=Point(0,0);
|
|
|
|
if Mediator<>nil then begin
|
|
Handled:=false;
|
|
Mediator.MouseDown(Button,Shift,MouseDownPos,Handled);
|
|
if Handled then exit;
|
|
end;
|
|
|
|
SelectedCompClass := GetSelectedComponentClass;
|
|
|
|
if Button=mbLeft then begin
|
|
// left button
|
|
// -> check if a grabber was activated
|
|
Selection.ActiveGrabber:=Selection.GrabberAtPos(MouseDownPos.X, MouseDownPos.Y);
|
|
SetCaptureControl(ParentForm);
|
|
|
|
if SelectedCompClass = nil then begin
|
|
// selection mode
|
|
if Selection.ActiveGrabber=nil then begin
|
|
// no grabber resizing
|
|
|
|
CompIndex:=Selection.IndexOf(MouseDownComponent);
|
|
if ssCtrl in Shift then begin
|
|
// child selection
|
|
end
|
|
else if (ssShift in Shift) then begin
|
|
// shift key pressed (multiselection)
|
|
|
|
if CompIndex<0 then begin
|
|
// not selected
|
|
// add component to selection
|
|
if (Selection.SelectionForm<>nil)
|
|
and (Selection.SelectionForm<>Form)
|
|
then begin
|
|
IDEMessageDialog(lisInvalidMultiselection,
|
|
fdInvalidMultiselectionText,
|
|
mtInformation,[mbOk]);
|
|
end else begin
|
|
Selection.Add(MouseDownComponent);
|
|
end;
|
|
end else begin
|
|
// remove from multiselection
|
|
Selection.Delete(CompIndex);
|
|
Form.Invalidate; // redraw markers
|
|
end;
|
|
end else begin
|
|
// no shift key (single selection or keeping multiselection)
|
|
|
|
if (CompIndex<0) then begin
|
|
// select only this component
|
|
Selection.AssignPersistent(MouseDownComponent);
|
|
end else
|
|
// sync with the interface
|
|
Selection.UpdateBounds;
|
|
end;
|
|
end else begin
|
|
// mouse down on grabber -> begin sizing
|
|
// grabber is already activated
|
|
// the sizing is handled in mousemove and mouseup
|
|
end;
|
|
end else begin
|
|
// add component mode -> handled in mousemove and mouseup
|
|
// but check if we pressed mouse on the form which is not selected
|
|
if (Selection.SelectionForm <> Form) then
|
|
Selection.AssignPersistent(MouseDownComponent);
|
|
end;
|
|
end else begin
|
|
// not left button
|
|
Selection.ActiveGrabber := nil;
|
|
if (Button = mbRight) and EnvironmentGuiOpts.RightClickSelects and
|
|
(Selection.SelectionForm <> Form) then
|
|
Selection.AssignPersistent(MouseDownComponent);
|
|
end;
|
|
|
|
if PropertyEditorHook<>nil then
|
|
PropertyEditorHook.DesignerMouseDown(Sender, Button, Shift, p.X, p.Y);
|
|
|
|
if not Selection.OnlyVisualComponentsSelected and ShowComponentCaptions then
|
|
Form.Invalidate;
|
|
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('[TDesigner.MouseDownOnControl] END');
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TDesigner.MouseUpOnControl(Sender : TControl; var TheMessage:TLMMouse);
|
|
var
|
|
Button: TMouseButton;
|
|
Shift: TShiftState;
|
|
SelectedCompClass: TRegisteredComponent;
|
|
|
|
procedure DoAddComponent;
|
|
var
|
|
NewParent: TComponent;
|
|
NewComponentClass: TComponentClass;
|
|
NewLeft, NewTop, NewWidth, NewHeight: Integer;
|
|
ParentClientOrigin: TPoint;
|
|
begin
|
|
if MouseDownComponent=nil then exit;
|
|
NewComponentClass := SelectedCompClass.GetCreationClass;
|
|
//debugln(['AddComponent NewComponentClass=',DbgSName(NewComponentClass)]);
|
|
|
|
// find a parent for the new component
|
|
NewParent:=nil;
|
|
if not PropertyEditorHook.AddClicked(Self,MouseDownComponent,Button,Shift,
|
|
MouseUpPos.X,MouseUpPos.Y,NewComponentClass,NewParent) then exit;
|
|
|
|
AddComponentCheckParent(NewParent, MouseDownComponent,
|
|
WinControlAtPos(MouseUpPos.X, MouseUpPos.Y, true, true), NewComponentClass);
|
|
if not Assigned(NewParent) then exit;
|
|
|
|
// calculate initial bounds
|
|
NewLeft:=Min(MouseDownPos.X,MouseUpPos.X);
|
|
NewTop:=Min(MouseDownPos.Y,MouseUpPos.Y);
|
|
if (Mediator<>nil) then begin
|
|
ParentClientOrigin:=Mediator.GetComponentOriginOnForm(NewParent);
|
|
DebugLn(['AddComponent ParentClientOrigin=',dbgs(ParentClientOrigin)]);
|
|
// adjust left,top to parent origin
|
|
dec(NewLeft,ParentClientOrigin.X);
|
|
dec(NewTop,ParentClientOrigin.Y);
|
|
end else if NewComponentClass.InheritsFrom(TControl) then
|
|
begin
|
|
ParentClientOrigin:=GetParentFormRelativeClientOrigin(NewParent);
|
|
// adjust left,top to parent origin
|
|
dec(NewLeft,ParentClientOrigin.X);
|
|
dec(NewTop,ParentClientOrigin.Y);
|
|
end;
|
|
NewWidth:=Abs(MouseUpPos.X-MouseDownPos.X);
|
|
NewHeight:=Abs(MouseUpPos.Y-MouseDownPos.Y);
|
|
if Abs(NewWidth+NewHeight)<7 then begin
|
|
// this very small component is probably only a wag, take default size
|
|
NewWidth:=0;
|
|
NewHeight:=0;
|
|
end;
|
|
//DebugLn(['AddComponent ',dbgsName(NewComponentClass)]);
|
|
if NewComponentClass = nil then exit;
|
|
AddComponent(SelectedCompClass,NewComponentClass,NewParent,NewLeft,NewTop,NewWidth,NewHeight);
|
|
end;
|
|
|
|
procedure RubberbandSelect;
|
|
var
|
|
MaxParentComponent: TComponent;
|
|
NewRubberSelection: boolean;
|
|
begin
|
|
if (ssShift in Shift)
|
|
and (Selection.SelectionForm<>nil)
|
|
and (Selection.SelectionForm<>Form)
|
|
then begin
|
|
IDEMessageDialog(lisInvalidMultiselection,
|
|
fdInvalidMultiselectionText,
|
|
mtInformation,[mbOk]);
|
|
exit;
|
|
end;
|
|
|
|
// check if start new selection or add/remove:
|
|
NewRubberSelection:= (not (ssShift in Shift)) or (Selection.SelectionForm<>Form);
|
|
// update non visual components
|
|
MoveNonVisualComponentsIntoForm;
|
|
// if user press the Control key, then component candidates are only
|
|
// children of the control, where the mouse started
|
|
if (ssCtrl in shift) then begin
|
|
if MouseDownComponent=Form then
|
|
MaxParentComponent:=FLookupRoot
|
|
else
|
|
MaxParentComponent:=MouseDownComponent;
|
|
end else
|
|
MaxParentComponent:=FLookupRoot;
|
|
|
|
Selection.SelectWithRubberBand(FLookupRoot, Mediator, NewRubberSelection,
|
|
ssShift in Shift, MaxParentComponent);
|
|
if Selection.Count=0 then begin
|
|
Selection.Add(FLookupRoot);
|
|
end;
|
|
Selection.RubberbandActive:=false;
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('RubberbandSelect ',DbgS(ControlSelection.Grabbers[0]));
|
|
{$ENDIF}
|
|
Form.Invalidate;
|
|
end;
|
|
|
|
var
|
|
SenderParentForm: TCustomForm;
|
|
SelectedPersistent: TSelectedControl;
|
|
DesignSender, MouseDownControl: TControl;
|
|
RubberBandWasActive, Handled: Boolean;
|
|
p: TPoint;
|
|
{$IFDEF EnableDesignerPopupRightClick}
|
|
PopupPos: TPoint;
|
|
{$ENDIF}
|
|
i, j: Integer;
|
|
begin
|
|
FHintTimer.Enabled := False;
|
|
FHintWindow.Visible := False;
|
|
|
|
SetCaptureControl(nil);
|
|
|
|
// check if the message is for the designed form and there was a mouse down before
|
|
DesignSender:=GetDesignControl(Sender);
|
|
SenderParentForm:=GetDesignerForm(DesignSender);
|
|
//DebugLn(['TDesigner.MouseUpOnControl DesignSender=',dbgsName(DesignSender),' SenderParentForm=',dbgsName(SenderParentForm),' ',TheMessage.XPos,',',TheMessage.YPos]);
|
|
if (MouseDownComponent=nil) or (SenderParentForm=nil) or (SenderParentForm<>Form)
|
|
or ( (Selection.SelectionForm<>nil) and (Selection.SelectionForm<>Form) ) then
|
|
begin
|
|
MouseDownComponent:=nil;
|
|
MouseDownSender:=nil;
|
|
exit;
|
|
end;
|
|
|
|
Selection.ActiveGrabber:=nil;
|
|
RubberBandWasActive:=Selection.RubberBandActive;
|
|
SelectedCompClass:=GetSelectedComponentClass;
|
|
|
|
GetMouseMsgShift(TheMessage,Shift,Button);
|
|
MouseUpPos:=GetFormRelativeMousePosition(Form);
|
|
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('************************************************************');
|
|
DbgOut('MouseUpOnControl');
|
|
DbgOut(' Sender=',dbgsName(Sender),' DesignSender=',dbgsName(DesignSender));
|
|
//write(' Msg=',TheMessage.Pos.X,',',TheMessage.Pos.Y);
|
|
DebugLn('');
|
|
{$ENDIF}
|
|
|
|
if MouseDownComponent is TControl then
|
|
begin
|
|
MouseDownControl:=TControl(MouseDownComponent);
|
|
p:=MouseDownControl.ScreenToClient(Form.ClientToScreen(MouseUpPos));
|
|
if (csDesignInteractive in MouseDownControl.ControlStyle)
|
|
or (MouseDownControl.Perform(CM_DESIGNHITTEST, TheMessage.Keys, Longint(SmallPoint(p.X, p.Y))) > 0) then
|
|
begin
|
|
TControlAccess(MouseDownComponent).MouseUp(Button, Shift, p.X, p.Y);
|
|
Exit;
|
|
end;
|
|
end
|
|
else
|
|
p:=Point(0,0);
|
|
|
|
if Mediator<>nil then
|
|
begin
|
|
Handled:=false;
|
|
Mediator.MouseUp(Button,Shift,MouseUpPos,Handled);
|
|
if Handled then exit;
|
|
end;
|
|
|
|
Selection.BeginUpdate;
|
|
if Button=mbLeft then
|
|
begin
|
|
if SelectedCompClass = nil then
|
|
begin
|
|
if (FUndoState = ucsSaveChange) then
|
|
begin
|
|
// update undo list stored component bounds (Left, Top, Width, Height)
|
|
// see TControlSelection.EndResizing
|
|
// the list of all TComponent, Left,Top,Width,Height
|
|
// Note: not every component has all four properties.
|
|
j := FUndoCurr - 1;
|
|
i := Selection.Count-1;
|
|
while i>=0 do
|
|
begin
|
|
SelectedPersistent:=Selection.Items[i];
|
|
if SelectedPersistent.IsTComponent then
|
|
begin
|
|
while (j>=0) do
|
|
begin
|
|
if (FUndoList[j].compName <> TComponent(SelectedPersistent.Persistent).Name)
|
|
then begin
|
|
// this is not a list of bounds -> stop
|
|
i:=0;
|
|
break;
|
|
end;
|
|
if (FUndoList[j].fieldName = 'Width') then
|
|
FUndoList[j].newVal := SelectedPersistent.Width
|
|
else if (FUndoList[j].fieldName = 'Height') then
|
|
FUndoList[j].newVal := SelectedPersistent.Height
|
|
else if (FUndoList[j].fieldName = 'Left') then
|
|
FUndoList[j].newVal := SelectedPersistent.Left
|
|
else if (FUndoList[j].fieldName = 'Top') then
|
|
FUndoList[j].newVal := SelectedPersistent.Top
|
|
else begin
|
|
// this is not a list of bounds -> stop
|
|
i:=0;
|
|
break;
|
|
end;
|
|
dec(j);
|
|
end;
|
|
end;
|
|
dec(i);
|
|
end;
|
|
end;
|
|
FUndoState := ucsNone;
|
|
|
|
// layout mode (selection, moving and resizing)
|
|
if not (dfHasSized in FFlags) then
|
|
begin
|
|
// new selection
|
|
if RubberBandWasActive then
|
|
RubberbandSelect // rubberband selection
|
|
else if not (ssShift in Shift) then
|
|
begin // point selection, select only the mouse down component
|
|
Selection.AssignPersistent(MouseDownComponent);
|
|
if (ssDouble in MouseDownShift) and (Selection.SelectionForm = Form) then
|
|
begin // Double Click -> invoke 'Edit' of the component editor
|
|
PopupMenuComponentEditor := GetComponentEditorForSelection;
|
|
Assert(Assigned(PopupMenuComponentEditor),
|
|
'TDesigner.MouseUpOnControl: no component editor found for '
|
|
+MouseDownComponent.Name+':'+MouseDownComponent.ClassName);
|
|
PopupMenuComponentEditor.Edit;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
Selection.UpdateBounds;
|
|
end else
|
|
DoAddComponent; // create new a component on the form
|
|
end
|
|
else if Button=mbRight then
|
|
begin
|
|
// right click -> popup menu
|
|
Selection.RubberbandActive := False;
|
|
Selection.EndUpdate;
|
|
if EnvironmentGuiOpts.RightClickSelects
|
|
and (not Selection.IsSelected(MouseDownComponent))
|
|
and (Shift = [ssRight]) then begin
|
|
// select only the mouse down component
|
|
Selection.AssignPersistent(MouseDownComponent);
|
|
end;
|
|
{$IFDEF EnableDesignerPopupRightClick}
|
|
PopupMenuComponentEditor := GetComponentEditorForSelection;
|
|
BuildPopupMenu;
|
|
PopupPos := Form.ClientToScreen(MouseUpPos);
|
|
FDesignerPopupMenu.Popup(PopupPos.X, PopupPos.Y);
|
|
{$ENDIF}
|
|
Selection.BeginUpdate;
|
|
end;
|
|
Selection.RubberbandActive := False;
|
|
LastMouseMovePos.X:=-1;
|
|
if (not Selection.OnlyVisualComponentsSelected and ShowComponentCaptions)
|
|
or (dfHasSized in FFlags) then
|
|
Form.Invalidate;
|
|
Exclude(FFlags,dfHasSized);
|
|
MouseDownComponent:=nil;
|
|
MouseDownSender:=nil;
|
|
if PropertyEditorHook<>nil then
|
|
PropertyEditorHook.DesignerMouseUp(Sender, Button, Shift, p.X, p.Y);
|
|
|
|
Selection.EndUpdate;
|
|
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('[TDesigner.MouseUpOnControl] END');
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TDesigner.MouseMoveOnControl(Sender: TControl;
|
|
var TheMessage: TLMMouse);
|
|
var
|
|
Button: TMouseButton;
|
|
Shift : TShiftState;
|
|
SenderParentForm:TCustomForm;
|
|
OldMouseMovePos: TPoint;
|
|
Grabber: TGrabber;
|
|
ACursor: TCursor;
|
|
SelectedCompClass: TRegisteredComponent;
|
|
CurSnappedMousePos, OldSnappedMousePos: TPoint;
|
|
DesignSender: TControl;
|
|
Handled: Boolean;
|
|
MouseMoveComponent: TComponent;
|
|
MouseMoveControl: TControl;
|
|
p: types.TPoint;
|
|
begin
|
|
GetMouseMsgShift(TheMessage, Shift, Button);
|
|
|
|
if [dfShowEditorHints] * FFlags <> [] then
|
|
begin
|
|
FHintTimer.Enabled := False;
|
|
// hide hint
|
|
FHintTimer.Enabled := Shift * [ssLeft, ssRight, ssMiddle] = [];
|
|
if not (dfHasSized in FFlags) then
|
|
FHintWindow.Visible := False;
|
|
end;
|
|
|
|
DesignSender := GetDesignControl(Sender);
|
|
//DebugLn('TDesigner.MouseMoveOnControl Sender=',dbgsName(Sender),' ',dbgsName(DesignSender));
|
|
SenderParentForm := GetDesignerForm(DesignSender);
|
|
if (SenderParentForm = nil) or (SenderParentForm <> Form) then Exit;
|
|
|
|
OldMouseMovePos := LastMouseMovePos;
|
|
LastMouseMovePos := GetFormRelativeMousePosition(Form);
|
|
|
|
MouseMoveComponent := MouseDownComponent;
|
|
if MouseMoveComponent = nil then
|
|
MouseMoveComponent := ComponentAtPos(LastMouseMovePos.X, LastMouseMovePos.Y, True, True);
|
|
if MouseMoveComponent is TControl then
|
|
begin
|
|
MouseMoveControl:=TControl(MouseMoveComponent);
|
|
p:=MouseMoveControl.ScreenToClient(Form.ClientToScreen(LastMouseMovePos));
|
|
if (csDesignInteractive in MouseMoveControl.ControlStyle)
|
|
or (MouseMoveControl.Perform(CM_DESIGNHITTEST, TheMessage.Keys, Longint(SmallPoint(p.X, p.Y))) > 0) then
|
|
begin
|
|
TControlAccess(MouseMoveComponent).MouseMove(Shift, p.X, p.Y);
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
if Mediator <> nil then
|
|
begin
|
|
Handled := False;
|
|
Mediator.MouseMove(Shift, LastMouseMovePos, Handled);
|
|
if Handled then Exit;
|
|
end;
|
|
|
|
if Selection.SelectionForm = Form then
|
|
Grabber := Selection.GrabberAtPos(LastMouseMovePos.X, LastMouseMovePos.Y)
|
|
else
|
|
Grabber := nil;
|
|
|
|
if MouseDownComponent = nil then
|
|
begin
|
|
if Grabber = nil then
|
|
ACursor := crDefault
|
|
else
|
|
ACursor := Grabber.Cursor;
|
|
|
|
LastFormCursor := ACursor;
|
|
SetTempCursor(Form, ACursor);
|
|
Exit;
|
|
end;
|
|
|
|
if (OldMouseMovePos.X = LastMouseMovePos.X) and (OldMouseMovePos.Y = LastMouseMovePos.Y) then
|
|
Exit;
|
|
|
|
if (Selection.SelectionForm = nil) or (Selection.SelectionForm = Form) then
|
|
begin
|
|
if Button = mbLeft then // left button pressed
|
|
begin
|
|
if (Selection.ActiveGrabber <> nil) then // grabber active => resizing
|
|
begin
|
|
// grabber moving -> size selection
|
|
if not Selection.LookupRootSelected then // if not current form is selected then resize selection
|
|
begin
|
|
if not (dfHasSized in FFlags) then
|
|
begin
|
|
Selection.SaveBounds(false);
|
|
Include(FFlags, dfHasSized);
|
|
end;
|
|
// skip snapping when Alt is pressed
|
|
if not (ssAlt in Shift) then
|
|
begin
|
|
OldSnappedMousePos := Selection.SnapGrabberMousePos(OldMouseMovePos);
|
|
CurSnappedMousePos := Selection.SnapGrabberMousePos(LastMouseMovePos);
|
|
end
|
|
else
|
|
begin
|
|
OldSnappedMousePos := OldMouseMovePos;
|
|
CurSnappedMousePos := LastMouseMovePos;
|
|
end;
|
|
Selection.SizeSelection(
|
|
CurSnappedMousePos.X - OldSnappedMousePos.X,
|
|
CurSnappedMousePos.Y - OldSnappedMousePos.Y);
|
|
DoModified;
|
|
end;
|
|
end
|
|
else
|
|
begin // no grabber active => moving
|
|
SelectedCompClass := GetSelectedComponentClass;
|
|
if (not Selection.RubberBandActive) and
|
|
(SelectedCompClass=nil) and
|
|
((Shift=[ssLeft]) or (Shift=[ssAlt, ssLeft])) and
|
|
(Selection.Count>=1) and
|
|
(not Selection.LookupRootSelected) then
|
|
begin // move selection
|
|
if not (dfHasSized in FFlags) then
|
|
begin
|
|
Selection.SaveBounds(false);
|
|
Include(FFlags, dfHasSized);
|
|
end;
|
|
//debugln('TDesigner.MouseMoveOnControl Move MouseDownComponent=',dbgsName(MouseDownComponent),' OldMouseMovePos=',dbgs(OldMouseMovePos),' MouseMovePos',dbgs(LastMouseMovePos),' MouseDownPos=',dbgs(MouseDownPos));
|
|
if (ssAlt in Shift) then begin
|
|
if Selection.MoveSelection(LastMouseMovePos.X - MouseDownPos.X, LastMouseMovePos.Y - MouseDownPos.Y, True) then
|
|
DoModified;
|
|
end else begin
|
|
if Selection.MoveSelectionWithSnapping(LastMouseMovePos.X - MouseDownPos.X, LastMouseMovePos.Y - MouseDownPos.Y) then
|
|
DoModified;
|
|
end;
|
|
end
|
|
else
|
|
begin // rubberband sizing (selection or creation)
|
|
Selection.RubberBandBounds := Rect(MouseDownPos.X, MouseDownPos.Y,
|
|
LastMouseMovePos.X, LastMouseMovePos.Y);
|
|
if SelectedCompClass = nil then
|
|
Selection.RubberbandType := rbtSelection
|
|
else
|
|
Selection.RubberbandType := rbtCreating;
|
|
Selection.RubberBandActive := True;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
Selection.ActiveGrabber:=nil;
|
|
end;
|
|
if [dfShowEditorHints, dfHasSized] * FFlags = [dfShowEditorHints, dfHasSized] then
|
|
HintTimer(Self);
|
|
end;
|
|
|
|
|
|
{
|
|
-----------------------------K E Y D O W N -------------------------------
|
|
}
|
|
{
|
|
Handles the keydown messages. DEL deletes the selected controls, CTRL-ARROR
|
|
moves the selection up one, SHIFT-ARROW resizes, etc.
|
|
}
|
|
procedure TDesigner.KeyDown(Sender: TControl; var TheMessage: TLMKEY);
|
|
var
|
|
Shift: TShiftState;
|
|
Command: word;
|
|
Handled: boolean;
|
|
Current: TComponent;
|
|
NameRes: TAskCompNameDialogResult;
|
|
UTF8Char: TUTF8Char;
|
|
|
|
procedure Nudge(x, y: integer);
|
|
begin
|
|
if (ssCtrl in Shift) then
|
|
begin
|
|
if ssShift in Shift then
|
|
begin
|
|
x := x * GetGridSizeX;
|
|
y := y * GetGridSizeY;
|
|
end;
|
|
NudgePosition(x, y)
|
|
end
|
|
else
|
|
if (ssShift in Shift) then
|
|
NudgeSize(x, y)
|
|
else
|
|
if (Shift = []) then
|
|
NudgeSelection(x, y);
|
|
end;
|
|
|
|
begin
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn(['TDesigner.KEYDOWN ',TheMessage.CharCode,' ',TheMessage.KeyData]);
|
|
{$ENDIF}
|
|
Shift := KeyDataToShiftState(TheMessage.KeyData);
|
|
Handled := False;
|
|
if Mediator<>nil then
|
|
Mediator.KeyDown(Sender,TheMessage.CharCode,Shift);
|
|
|
|
Command := FTheFormEditor.TranslateKeyToDesignerCommand(TheMessage.CharCode, Shift);
|
|
//DebugLn(['TDesigner.KEYDOWN Command=',dbgs(Command),' ',TheMessage.CharCode,' ',dbgs(Shift)]);
|
|
DoProcessCommand(Self, Command, Handled);
|
|
//DebugLn(['TDesigner.KeyDown Command=',Command,' Handled=',Handled,' TheMessage.CharCode=',TheMessage.CharCode]);
|
|
|
|
if not Handled and (SourceEditorManager.ActiveSourceWindow<>nil)
|
|
and (GetParentForm(SourceEditorManager.ActiveSourceWindow) = GetParentForm(Sender)) then
|
|
begin
|
|
// send special commands to current editor if they have same parent (designer is docked to the editor)
|
|
case Command of
|
|
ecNextEditor, ecPrevEditor, ecNextEditorInHistory, ecPrevEditorInHistory:
|
|
begin
|
|
FillChar(UTF8Char{%H-}, SizeOf(UTF8Char), 0);
|
|
SourceEditorManager.ActiveSourceWindow.ProcessParentCommand(Self, Command, UTF8Char, nil, Handled);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if not Handled then
|
|
begin
|
|
Handled := True;
|
|
case TheMessage.CharCode of
|
|
VK_DELETE:
|
|
if not Selection.OnlyInvisiblePersistentsSelected then
|
|
DoDeleteSelectedPersistents;
|
|
|
|
VK_UP:
|
|
Nudge(0,-1);
|
|
|
|
VK_DOWN:
|
|
Nudge(0,1);
|
|
|
|
VK_RIGHT:
|
|
Nudge(1,0);
|
|
|
|
VK_LEFT:
|
|
Nudge(-1,0);
|
|
|
|
VK_TAB:
|
|
if Shift = [ssShift] then
|
|
NudgeSelection(False)
|
|
else
|
|
if Shift = [] then
|
|
NudgeSelection(True)
|
|
else
|
|
Handled := False;
|
|
|
|
VK_RETURN:
|
|
if Shift = [] then
|
|
DoShowObjectInspector
|
|
else if Shift = [ssCtrl] then
|
|
begin
|
|
// ToDo: create an event for each selected control (currently
|
|
// GetComponentEditorForSelection returns nil if more than one is selected)
|
|
PopupMenuComponentEditor := GetComponentEditorForSelection;
|
|
if assigned(PopupMenuComponentEditor) then
|
|
PopupMenuComponentEditor.Edit;
|
|
end else
|
|
Handled := False;
|
|
|
|
VK_A:
|
|
if Shift = [ssCtrl] then
|
|
DoSelectAll
|
|
else
|
|
Handled := False;
|
|
|
|
VK_F2:
|
|
if (Selection.Count=1) and Selection[0].IsTComponent then begin
|
|
Current := TComponent(Selection[0].Persistent);
|
|
NameRes := ShowComponentNameDialog(LookupRoot, Current);
|
|
if NameRes.NameChanged then
|
|
GlobalDesignHook.ComponentRenamed(Current);
|
|
if NameRes.TextChanged then
|
|
GlobalDesignHook.Modified(Current, NameRes.TextPropertyName);
|
|
if NameRes.Changed then
|
|
Modified;
|
|
end; // don't forget the semicolon before else !!!
|
|
|
|
else
|
|
Handled := False;
|
|
end;
|
|
end;
|
|
|
|
if Handled then
|
|
begin
|
|
TheMessage.CharCode := 0;
|
|
TheMessage.Result := 1;
|
|
end;
|
|
end;
|
|
|
|
|
|
{------------------------------------K E Y U P --------------------------------}
|
|
procedure TDesigner.KeyUp(Sender: TControl; var TheMessage: TLMKEY);
|
|
var
|
|
Shift: TShiftState;
|
|
Begin
|
|
{$IFDEF VerboseDesigner}
|
|
//Writeln('TDesigner.KEYUP ',TheMessage.CharCode,' ',TheMessage.KeyData);
|
|
{$ENDIF}
|
|
if Mediator<>nil then begin
|
|
Shift := KeyDataToShiftState(TheMessage.KeyData);
|
|
Mediator.KeyUp(Sender,TheMessage.CharCode,Shift);
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.DoDeleteSelectedPersistents: boolean;
|
|
var
|
|
i: integer;
|
|
APersistent: TPersistent;
|
|
AncestorRoot: TComponent;
|
|
AComponent: TComponent;
|
|
begin
|
|
Result:=true;
|
|
if (Selection.Count=0) or (Selection.SelectionForm<>Form) then
|
|
exit;
|
|
Result:=false;
|
|
// check if a component is the lookup root (can not be deleted)
|
|
if (Selection.LookupRootSelected) then begin
|
|
if Selection.Count>1 then
|
|
IDEMessageDialog(lisInvalidDelete,
|
|
lisTheRootComponentCanNotBeDeleted, mtInformation,
|
|
[mbOk]);
|
|
exit;
|
|
end;
|
|
// check if a selected component is inherited (can not be deleted)
|
|
for i:=0 to Selection.Count-1 do begin
|
|
if not Selection[i].IsTComponent then continue;
|
|
AncestorRoot:=TheFormEditor.GetAncestorLookupRoot(
|
|
TComponent(Selection[i].Persistent));
|
|
if AncestorRoot<>nil then begin
|
|
IDEMessageDialog(lisInvalidDelete,
|
|
Format(lisTheComponentIsInheritedFromToDeleteAnInheritedComp,
|
|
[dbgsName(Selection[i].Persistent), dbgsName(AncestorRoot), LineEnding]),
|
|
mtInformation, [mbOk]);
|
|
exit;
|
|
end;
|
|
end;
|
|
// check if a selected component is not owned by lookuproot (can not be deleted)
|
|
for i:=0 to Selection.Count-1 do begin
|
|
if not Selection[i].IsTComponent then continue;
|
|
AComponent:=TComponent(Selection[i].Persistent);
|
|
if AComponent.Owner<>FLookupRoot then begin
|
|
IDEMessageDialog(lisInvalidDelete,
|
|
Format(lisTheComponentCanNotBeDeletedBecauseItIsNotOwnedBy, [dbgsName(
|
|
Selection[i].Persistent), dbgsName(FLookupRoot)]),
|
|
mtInformation, [mbOk]);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
for i := 0 to Selection.Count - 1 do
|
|
begin
|
|
if not Selection[i].IsTComponent then continue;
|
|
AComponent := TComponent(Selection[i].Persistent);
|
|
AddUndoAction(AComponent, uopDelete, i = 0, 'Name', AComponent.Name, '');
|
|
end;
|
|
|
|
// mark selected components for deletion
|
|
for i:=0 to Selection.Count-1 do
|
|
begin
|
|
APersistent := Selection[i].Persistent;
|
|
if DeletingPersistent.IndexOf(APersistent) = -1 then
|
|
DeletingPersistent.Add(APersistent);
|
|
end;
|
|
// clear selection by selecting the LookupRoot
|
|
SelectOnlyThisComponent(FLookupRoot);
|
|
// delete marked components
|
|
try
|
|
if DeletingPersistent.Count=0 then exit;
|
|
while DeletingPersistent.Count>0 do begin
|
|
APersistent:=TPersistent(DeletingPersistent[DeletingPersistent.Count-1]);
|
|
//debugln(['TDesigner.DoDeleteSelectedComponents A ',dbgsName(APersistent),' ',(APersistent is TComponent) and (TheFormEditor.FindComponent(TComponent(APersistent))<>nil)]);
|
|
RemovePersistentAndChildren(APersistent);
|
|
end;
|
|
MouseDownComponent := Nil;
|
|
finally
|
|
Modified;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
procedure TDesigner.DoDeleteSelectedPersistentsAsync(Data: PtrInt);
|
|
begin
|
|
DoDeleteSelectedPersistents;
|
|
end;
|
|
|
|
procedure TDesigner.DoSelectAll;
|
|
begin
|
|
Selection.BeginUpdate;
|
|
Selection.Clear;
|
|
Selection.SelectAll(FLookupRoot);
|
|
Selection.EndUpdate;
|
|
Form.Invalidate;
|
|
end;
|
|
|
|
procedure TDesigner.DoDeletePersistent(APersistent: TPersistent; FreeIt: boolean);
|
|
var
|
|
Hook: TPropertyEditorHook;
|
|
Special: Boolean;
|
|
begin
|
|
if APersistent=nil then exit;
|
|
try
|
|
//debugln(['TDesigner.DoDeletePersistent A ',dbgsName(APersistent),' FreeIt=',FreeIt]);
|
|
// unselect component
|
|
Selection.Remove(APersistent);
|
|
if APersistent is TComponent then begin
|
|
PopupMenuComponentEditor:=nil;
|
|
if csDestroying in TComponent(APersistent).ComponentState then
|
|
FreeIt:=false;
|
|
end;
|
|
if GetDesignerForm(APersistent)=nil then begin
|
|
// has no designer
|
|
// -> do not call handlers and simply get rid of the rubbish
|
|
if FreeIt then begin
|
|
//debugln('TDesigner.DoDeletePersistent UNKNOWN in formeditor: ',dbgsName(APersistent));
|
|
APersistent.Free;
|
|
end;
|
|
exit;
|
|
end;
|
|
// call component deleting handlers
|
|
Hook:=GetPropertyEditorHook;
|
|
if Assigned(Hook) then
|
|
Hook.PersistentDeleting(APersistent);
|
|
Special := (Copy(APersistent.ClassName,1,3) = 'TPS') // A hack for PSScript plugins. ToDo...
|
|
or ((APersistent is TWinControl) and TWinControl(APersistent).IsSpecialSubControl);
|
|
// delete component
|
|
if APersistent is TComponent then
|
|
TheFormEditor.DeleteComponent(TComponent(APersistent),FreeIt)
|
|
else if FreeIt then
|
|
APersistent.Free;
|
|
// call ComponentDeleted handler
|
|
if Assigned(FOnPersistentDeleted) then begin
|
|
if Special then // Special treatment is needed for TPairSplitterSide.
|
|
FOnPersistentDeleted(Self,nil) // Will rebuild whole OI Tree.
|
|
else
|
|
FOnPersistentDeleted(Self,APersistent);
|
|
end;
|
|
if Assigned(Hook) then
|
|
Hook.PersistentDeleted(APersistent);
|
|
finally
|
|
// unmark component
|
|
DeletingPersistent.Remove(APersistent);
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.GetSelectedComponentClass: TRegisteredComponent;
|
|
begin
|
|
Result:=nil;
|
|
if Assigned(FOnGetSelectedComponentClass) then
|
|
FOnGetSelectedComponentClass(Self,Result);
|
|
end;
|
|
|
|
function TDesigner.IsDesignMsg(Sender: TControl; var TheMessage: TLMessage): Boolean;
|
|
var
|
|
Act: Word;
|
|
begin
|
|
Result := false;
|
|
if csDesigning in Sender.ComponentState then begin
|
|
Result:=true;
|
|
Inc(FProcessingDesignerEvent);
|
|
try
|
|
case TheMessage.Msg of
|
|
LM_PAINT: Result := PaintControl(Sender, TLMPaint(TheMessage));
|
|
CN_KEYDOWN,CN_SYSKEYDOWN: KeyDown(Sender,TLMKey(TheMessage));
|
|
CN_KEYUP,CN_SYSKEYUP: KeyUP(Sender,TLMKey(TheMessage));
|
|
LM_LBUTTONDOWN,
|
|
LM_RBUTTONDOWN,
|
|
LM_LBUTTONDBLCLK: MouseDownOnControl(Sender,TLMMouse(TheMessage));
|
|
LM_LBUTTONUP,
|
|
LM_RBUTTONUP: MouseUpOnControl(Sender, TLMMouse(TheMessage));
|
|
LM_MOUSEMOVE: MouseMoveOnControl(Sender, TLMMouse(TheMessage));
|
|
LM_SIZE: Result:=SizeControl(Sender, TLMSize(TheMessage));
|
|
LM_MOVE: Result:=MoveControl(Sender, TLMMove(TheMessage));
|
|
LM_ACTIVATE: begin
|
|
{$IFDEF VerboseComponentPalette}
|
|
DebugLn(['TDesigner.IsDesignMsg: Got LM_ACTIVATE message.',
|
|
' Message.Active=',TLMActivate(TheMessage).Active]);
|
|
{$ENDIF}
|
|
Act:=TLMActivate(TheMessage).Active;
|
|
Result:=DoFormActivated(Act in [WA_ACTIVE, WA_CLICKACTIVE]);
|
|
end;
|
|
LM_CLOSEQUERY: Result:=DoFormCloseQuery;
|
|
LM_SETCURSOR: Result:=HandleSetCursor(TheMessage);
|
|
LM_CONTEXTMENU: HandlePopupMenu(Sender, TLMContextMenu(TheMessage));
|
|
else
|
|
Result:=false;
|
|
end;
|
|
finally
|
|
Dec(FProcessingDesignerEvent);
|
|
end;
|
|
end else begin
|
|
if (TheMessage.Msg=LM_PAINT)
|
|
or (TheMessage.Msg=CN_KEYDOWN)
|
|
or (TheMessage.Msg=CN_KEYUP)
|
|
or (TheMessage.Msg=LM_LBUTTONDOWN)
|
|
or (TheMessage.Msg=LM_RBUTTONDOWN)
|
|
or (TheMessage.Msg=LM_LBUTTONDBLCLK)
|
|
or (TheMessage.Msg=LM_LBUTTONUP)
|
|
or (TheMessage.Msg=LM_RBUTTONUP)
|
|
or (TheMessage.Msg=LM_MOUSEMOVE)
|
|
or (TheMessage.Msg=LM_SIZE)
|
|
or (TheMessage.Msg=LM_MOVE)
|
|
or (TheMessage.Msg=LM_ACTIVATE)
|
|
or (TheMessage.Msg=LM_CLOSEQUERY)
|
|
or (TheMessage.Msg=LM_SETCURSOR)
|
|
then
|
|
DebugLn(['TDesigner.IsDesignMsg NOT DESIGNING? ',dbgsName(Sender),' TheMessage.Msg=',GetMessageName(TheMessage.Msg)]);
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.UniqueName(const BaseName: string): string;
|
|
begin
|
|
Result:=TheFormEditor.CreateUniqueComponentName(BaseName,LookupRoot);
|
|
end;
|
|
|
|
procedure TDesigner.UTF8KeyPress(var UTF8Key: TUTF8Char);
|
|
begin
|
|
if ((Length(UTF8Key) = 1) and (Ord(UTF8Key[1]) < 32))
|
|
or (UTF8Key = sLineBreak) then // pass only printable characters
|
|
Exit;
|
|
|
|
if UTF8Key<>'' then
|
|
begin
|
|
DoOnForwardKeyToObjectInspector(Self, UTF8Key);
|
|
UTF8Key := '';
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.Modified;
|
|
Begin
|
|
Selection.SaveBounds;
|
|
DoModified;
|
|
inherited Modified;
|
|
end;
|
|
|
|
procedure TDesigner.RemovePersistentAndChildren(APersistent: TPersistent);
|
|
var
|
|
i: integer;
|
|
AWinControl: TWinControl;
|
|
ChildControl: TControl;
|
|
Begin
|
|
if APersistent=nil then exit;
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('[TDesigner.RemovePersistentAndChilds] START ',dbgsName(APersistent),' ',DbgS(APersistent));
|
|
{$ENDIF}
|
|
if (APersistent=FLookupRoot) or (APersistent=Form)
|
|
then exit;
|
|
// remove all child controls owned by the LookupRoot
|
|
if (APersistent is TWinControl) then begin
|
|
AWinControl:=TWinControl(APersistent);
|
|
// Component may auto-create new components during deletion unless informed.
|
|
// ComponentState does not have csDestroying yet when removing children.
|
|
AWinControl.DesignerDeleting := True;
|
|
i:=AWinControl.ControlCount-1;
|
|
while (i>=0) do begin
|
|
ChildControl:=AWinControl.Controls[i];
|
|
if ChildControl.Owner=FLookupRoot then begin
|
|
//Debugln(['[TDesigner.RemoveComponentAndChildren] B ',dbgsName(APersistent),' Child=',dbgsName(ChildControl),' i=',i,' ',TheFormEditor.FindComponent(ChildControl)<>nil]);
|
|
RemovePersistentAndChildren(ChildControl);
|
|
// the component list of the form has changed -> restart the search
|
|
i:=AWinControl.ControlCount-1;
|
|
end else
|
|
dec(i);
|
|
end;
|
|
AWinControl.DesignerDeleting := False;
|
|
end;
|
|
// remove component
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('[TDesigner.RemovePersistentAndChildren] DoDeletePersistent ',dbgsName(APersistent));
|
|
{$ENDIF}
|
|
DoDeletePersistent(APersistent,true);
|
|
end;
|
|
|
|
procedure TDesigner.Notification(AComponent: TComponent; Operation: TOperation);
|
|
begin
|
|
if Operation = opInsert then begin
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('opInsert ',dbgsName(AComponent),' ',DbgS(AComponent));
|
|
{$ENDIF}
|
|
end
|
|
else
|
|
if Operation = opRemove then begin
|
|
{$IFDEF VerboseDesigner}
|
|
DebugLn('[TDesigner.Notification] opRemove ',dbgsName(AComponent));
|
|
{$ENDIF}
|
|
// Notification is usually triggered by TheFormEditor.DeleteComponent in DoDeletePersistent.
|
|
DoDeletePersistent(AComponent,false);
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.PaintGrid;
|
|
begin
|
|
// This is normally done in PaintControls
|
|
if FLookupRoot<>FForm then begin
|
|
// this is a special designer form -> lets draw itself
|
|
TCustomFormAccess(FForm).Paint;
|
|
end;
|
|
end;
|
|
|
|
function RoundToMultiple(AValue, ABasis: Integer): Integer; inline;
|
|
begin
|
|
Result := AValue div ABasis * ABasis;
|
|
end;
|
|
|
|
procedure TDesigner.PaintClientGrid(AWinControl: TWinControl;
|
|
aDDC: TDesignerDeviceContext);
|
|
var
|
|
Clip: integer;
|
|
Count: integer;
|
|
i: integer;
|
|
CurControl: TControl;
|
|
R: TRect;
|
|
P: TPoint;
|
|
begin
|
|
if (AWinControl=nil)
|
|
or (not (csAcceptsControls in AWinControl.ControlStyle))
|
|
or ((not ShowGrid) and (not ShowBorderSpacing)) then exit;
|
|
|
|
aDDC.BeginPainting;
|
|
try
|
|
// exclude all child control areas
|
|
Count:=AWinControl.ControlCount;
|
|
for i := 0 to Count - 1 do begin
|
|
with AWinControl.Controls[I] do begin
|
|
if (Visible or ((csDesigning in ComponentState)
|
|
and not (csNoDesignVisible in ControlStyle)))
|
|
then begin
|
|
Clip := ExcludeClipRect(aDDC.DC, Left, Top, Left + Width, Top + Height);
|
|
if Clip = NullRegion then exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// paint points
|
|
if ShowGrid then
|
|
begin
|
|
ADDC.Canvas.Pen.Color := GridColor;
|
|
ADDC.Canvas.Pen.Width := 1;
|
|
ADDC.Canvas.Pen.Style := psSolid;
|
|
P := TWinControlAccess(AWinControl).GetClientScrollOffset;
|
|
R := AWinControl.ClientRect;
|
|
R.BottomRight := R.BottomRight + Point(GridSizeX, GridSizeY);
|
|
Types.OffsetRect(R, RoundToMultiple(P.X, GridSizeX), RoundToMultiple(P.Y, GridSizeY));
|
|
DrawGrid(ADDC.Canvas.Handle, R, GridSizeX, GridSizeY);
|
|
end;
|
|
|
|
if ShowBorderSpacing then
|
|
begin
|
|
aDDC.Canvas.Brush.Color := EnvironmentGuiOpts.BorderSpacingColor;
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
CurControl := AWinControl.Controls[i];
|
|
if csNoDesignSelectable in CurControl.ControlStyle then
|
|
Continue;
|
|
aDDC.Canvas.FrameRect(
|
|
CurControl.Left-CurControl.BorderSpacing.GetSideSpace(akLeft),
|
|
CurControl.Top-CurControl.BorderSpacing.GetSideSpace(akTop),
|
|
CurControl.Left+CurControl.Width+CurControl.BorderSpacing.GetSideSpace(akRight),
|
|
CurControl.Top+CurControl.Height+CurControl.BorderSpacing.GetSideSpace(akBottom)
|
|
);
|
|
end;
|
|
end;
|
|
finally
|
|
aDDC.EndPainting;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.ValidateRename(AComponent: TComponent;
|
|
const CurName, NewName: string);
|
|
begin
|
|
// check if component is initialized
|
|
if (CurName='') or (NewName='')
|
|
or ((AComponent<>nil) and (csDestroying in AComponent.ComponentState)) then
|
|
exit;
|
|
|
|
// check if component is the LookupRoot
|
|
if AComponent=nil then AComponent:=FLookupRoot;
|
|
|
|
// consistency check
|
|
if CurName<>AComponent.Name then
|
|
DebugLn('WARNING: TDesigner.ValidateRename: OldComponentName="',CurName,'" <> AComponent=',dbgsName(AComponent));
|
|
if Assigned(OnRenameComponent) then
|
|
OnRenameComponent(Self,AComponent,NewName);
|
|
end;
|
|
|
|
function TDesigner.GetShiftState: TShiftState;
|
|
begin
|
|
Result:=FShiftState;
|
|
end;
|
|
|
|
function TDesigner.CreateUniqueComponentName(const AClassName: string): string;
|
|
begin
|
|
Result:=TheFormEditor.CreateUniqueComponentName(AClassName,FLookupRoot);
|
|
end;
|
|
|
|
procedure TDesigner.ComponentEditorVerbMenuItemClick(Sender: TObject);
|
|
var
|
|
Verb: integer;
|
|
VerbCaption: string;
|
|
AMenuItem: TMenuItem;
|
|
begin
|
|
if (PopupMenuComponentEditor=nil) or (Sender=nil) then exit;
|
|
//DebugLn(['TDesigner.OnComponentEditorVerbMenuItemClick Sender=',dbgsName(Sender)]);
|
|
if Sender is TMenuItem then
|
|
AMenuItem:=TMenuItem(Sender)
|
|
else if Sender is TIDEMenuCommand then
|
|
AMenuItem:=TIDEMenuCommand(Sender).MenuItem
|
|
else
|
|
exit;
|
|
Verb:=PopupMenuComponentEditor.GetVerbCount-1;
|
|
VerbCaption:=AMenuItem.Caption;
|
|
while (Verb>=0) and (VerbCaption<>PopupMenuComponentEditor.GetVerb(Verb)) do
|
|
dec(Verb);
|
|
if Verb<0 then exit;
|
|
try
|
|
PopupMenuComponentEditor.ExecuteVerb(Verb);
|
|
except
|
|
on E: Exception do begin
|
|
DebugLn('TDesigner.OnComponentEditorVerbMenuItemClick ERROR: ',E.Message);
|
|
IDEMessageDialog(Format(lisErrorIn, [PopupMenuComponentEditor.ClassName]),
|
|
Format(lisTheComponentEditorOfClassInvokedWithVerbHasCreated,
|
|
[PopupMenuComponentEditor.ClassName, LineEnding, IntToStr(Verb),
|
|
VerbCaption, LineEnding, LineEnding, E.Message]),
|
|
mtError,[mbOk]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.DeleteSelectionMenuClick(Sender: TObject);
|
|
begin
|
|
Application.QueueAsyncCall(@DoDeleteSelectedPersistentsAsync, 0);
|
|
end;
|
|
|
|
procedure TDesigner.SelectAllMenuClick(Sender: TObject);
|
|
begin
|
|
DoSelectAll;
|
|
end;
|
|
|
|
procedure TDesigner.ChangeClassMenuClick(Sender: TObject);
|
|
begin
|
|
ChangeClass;
|
|
end;
|
|
|
|
procedure TDesigner.ChangeParentMenuClick(Sender: TObject);
|
|
begin
|
|
if Assigned(OnChangeParent) then
|
|
OnChangeParent(Self);
|
|
end;
|
|
|
|
procedure TDesigner.ShowNonVisualComponentsMenuClick(Sender: TObject);
|
|
begin
|
|
ShowNonVisualComponents:=not ShowNonVisualComponents;
|
|
end;
|
|
|
|
procedure TDesigner.SnapToGridOptionMenuClick(Sender: TObject);
|
|
begin
|
|
EnvironmentGuiOpts.SnapToGrid := not EnvironmentGuiOpts.SnapToGrid;
|
|
end;
|
|
|
|
procedure TDesigner.ShowOptionsMenuItemClick(Sender: TObject);
|
|
begin
|
|
if Assigned(OnShowOptions) then OnShowOptions(Self);
|
|
end;
|
|
|
|
procedure TDesigner.SnapToGuideLinesOptionMenuClick(Sender: TObject);
|
|
begin
|
|
EnvironmentGuiOpts.SnapToGuideLines := not EnvironmentGuiOpts.SnapToGuideLines;
|
|
end;
|
|
|
|
procedure TDesigner.ViewLFMMenuClick(Sender: TObject);
|
|
begin
|
|
if Assigned(OnViewLFM) then OnViewLFM(Self);
|
|
end;
|
|
|
|
procedure TDesigner.SaveAsXMLMenuClick(Sender: TObject);
|
|
begin
|
|
if Assigned(OnSaveAsXML) then OnSaveAsXML(Self);
|
|
end;
|
|
|
|
procedure TDesigner.CenterFormMenuClick(Sender: TObject);
|
|
var
|
|
NewLeft: Integer;
|
|
NewTop: Integer;
|
|
begin
|
|
if Form=nil then exit;
|
|
NewLeft:=Max(30,(Screen.Width-Form.Width) div 2);
|
|
NewTop:=Max(30,(Screen.Height-Form.Height) div 2);
|
|
Form.SetBounds(NewLeft,NewTop,Form.Width,Form.Height);
|
|
if Assigned(IDETabMaster) then
|
|
IDETabMaster.ShowForm(Form);
|
|
end;
|
|
|
|
procedure TDesigner.CopyMenuClick(Sender: TObject);
|
|
begin
|
|
CopySelection;
|
|
end;
|
|
|
|
procedure TDesigner.CutMenuClick(Sender: TObject);
|
|
begin
|
|
Application.QueueAsyncCall(@CutSelectionAsync, 0);
|
|
end;
|
|
|
|
procedure TDesigner.PasteMenuClick(Sender: TObject);
|
|
begin
|
|
PasteSelection([cpsfFindUniquePositions]);
|
|
end;
|
|
|
|
procedure TDesigner.AnchorEditorMenuClick(Sender: TObject);
|
|
begin
|
|
DoShowAnchorEditor;
|
|
end;
|
|
|
|
procedure TDesigner.TabOrderMenuClick(Sender: TObject);
|
|
begin
|
|
DoShowTabOrderEditor;
|
|
end;
|
|
|
|
function TDesigner.GetGridColor: TColor;
|
|
begin
|
|
Result:=EnvironmentGuiOpts.GridColor;
|
|
end;
|
|
|
|
function TDesigner.GetShowBorderSpacing: boolean;
|
|
begin
|
|
Result:=EnvironmentGuiOpts.ShowBorderSpacing;
|
|
end;
|
|
|
|
function TDesigner.GetShowComponentCaptions: boolean;
|
|
begin
|
|
Result:=dfShowComponentCaptions in FFlags;
|
|
end;
|
|
|
|
function TDesigner.GetShowGrid: boolean;
|
|
begin
|
|
Result:=EnvironmentGuiOpts.ShowGrid;
|
|
end;
|
|
|
|
function TDesigner.GetShowNonVisualComponents: boolean;
|
|
begin
|
|
Result:=dfShowNonVisualComponents in FFlags;
|
|
end;
|
|
|
|
function TDesigner.GetGridSizeX: integer;
|
|
begin
|
|
Result:=EnvironmentGuiOpts.GridSizeX;
|
|
if Result<2 then Result:=2;
|
|
end;
|
|
|
|
function TDesigner.GetGridSizeY: integer;
|
|
begin
|
|
Result:=EnvironmentGuiOpts.GridSizeY;
|
|
if Result<2 then Result:=2;
|
|
end;
|
|
|
|
function TDesigner.GetIsControl: Boolean;
|
|
Begin
|
|
Result := True;
|
|
end;
|
|
|
|
function TDesigner.GetShowEditorHints: boolean;
|
|
begin
|
|
Result:=dfShowEditorHints in FFlags;
|
|
end;
|
|
|
|
function TDesigner.GetSnapToGrid: boolean;
|
|
begin
|
|
Result := EnvironmentGuiOpts.SnapToGrid;
|
|
end;
|
|
|
|
procedure TDesigner.SetShowGrid(const AValue: boolean);
|
|
begin
|
|
if ShowGrid=AValue then exit;
|
|
EnvironmentGuiOpts.ShowGrid:=AValue;
|
|
Form.Invalidate;
|
|
end;
|
|
|
|
procedure TDesigner.SetShowNonVisualComponents(AValue: boolean);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if ShowNonVisualComponents=AValue then exit;
|
|
|
|
if AValue then begin
|
|
Include(FFlags,dfShowNonVisualComponents);
|
|
Form.Invalidate;
|
|
end else begin
|
|
Exclude(FFlags,dfShowNonVisualComponents);
|
|
Selection.BeginUpdate;
|
|
try
|
|
for i:=Selection.Count-1 downto 0 do
|
|
if Selection[i].IsNonVisualComponent then
|
|
Selection.Delete(i);
|
|
finally
|
|
Selection.EndUpdate;
|
|
Form.Invalidate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.SetGridSizeX(const AValue: integer);
|
|
begin
|
|
if GridSizeX=AValue then exit;
|
|
EnvironmentGuiOpts.GridSizeX:=AValue;
|
|
end;
|
|
|
|
procedure TDesigner.SetGridSizeY(const AValue: integer);
|
|
begin
|
|
if GridSizeY=AValue then exit;
|
|
EnvironmentGuiOpts.GridSizeY:=AValue;
|
|
end;
|
|
|
|
procedure TDesigner.SetMediator(const AValue: TDesignerMediator);
|
|
begin
|
|
if Mediator=AValue then exit;
|
|
if Mediator<>nil then Mediator.Designer:=nil;
|
|
FMediator:=AValue;
|
|
if Mediator<>nil then Mediator.Designer:=Self;
|
|
end;
|
|
|
|
procedure TDesigner.SetPopupMenuComponentEditor(const AValue: TBaseComponentEditor);
|
|
begin
|
|
if FPopupMenuComponentEditor <> AValue then
|
|
begin
|
|
FPopupMenuComponentEditor.Free;
|
|
FPopupMenuComponentEditor := AValue;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.SetShowEditorHints(const AValue: boolean);
|
|
begin
|
|
if AValue = ShowEditorHints then Exit;
|
|
if AValue then
|
|
Include(FFlags, dfShowEditorHints)
|
|
else
|
|
Exclude(FFlags, dfShowEditorHints);
|
|
end;
|
|
|
|
procedure TDesigner.DrawNonVisualComponent(AComponent: TComponent);
|
|
var
|
|
ItemLeft, ItemTop, ItemRight, ItemBottom: integer;
|
|
Diff, ItemLeftTop: TPoint;
|
|
OwnerRect, IconRect, TextRect: TRect;
|
|
TextSize: TSize;
|
|
IsSelected: Boolean;
|
|
RGN: HRGN;
|
|
IL: TCustomImageList;
|
|
II: TImageIndex;
|
|
Res: TScaledImageListResolution;
|
|
Icon: TBitmap;
|
|
ScaleFactor: Double;
|
|
begin
|
|
if (AComponent is TControl)
|
|
and (csNoDesignVisible in TControl(AComponent).ControlStyle) then
|
|
exit;
|
|
if (csDestroying in AComponent.ComponentState) then
|
|
exit;
|
|
|
|
// draw children
|
|
if (AComponent.Owner=nil) then
|
|
begin
|
|
FDDC.BeginPainting;
|
|
TComponentAccess(AComponent).GetChildren(@DrawNonVisualComponent, AComponent);
|
|
FDDC.EndPainting;
|
|
end
|
|
else if (csInline in AComponent.ComponentState) then
|
|
begin
|
|
if AComponent is TControl then
|
|
begin
|
|
// clip to client area
|
|
FDDC.BeginPainting;
|
|
FDDC.Canvas.SaveHandleState;
|
|
OwnerRect := TControl(AComponent).ClientRect;
|
|
Diff := GetParentFormRelativeClientOrigin(AComponent);
|
|
Types.OffsetRect(OwnerRect, Diff.X, Diff.Y);
|
|
with OwnerRect do
|
|
RGN := CreateRectRGN(Left, Top, Right, Bottom);
|
|
SelectClipRGN(FDDC.DC, RGN);
|
|
DeleteObject(RGN);
|
|
end;
|
|
TComponentAccess(AComponent).GetChildren(@DrawNonVisualComponent, AComponent);
|
|
if AComponent is TControl then
|
|
begin
|
|
FDDC.Canvas.RestoreHandleState;
|
|
FDDC.EndPainting;
|
|
end;
|
|
end
|
|
else
|
|
TComponentAccess(AComponent).GetChildren(@DrawNonVisualComponent, AComponent.Owner);
|
|
|
|
if not ComponentIsIcon(AComponent) or (AComponent.Owner = nil) then
|
|
Exit;
|
|
// actual draw
|
|
Diff := FDDC.FormOrigin;
|
|
//DebugLn(['FDDC.FormOrigin - ', Diff.X, ' : ' ,Diff.Y]);
|
|
// non-visual component
|
|
if FDDC.Form<>nil then
|
|
ScaleFactor := FDDC.Form.GetCanvasScaleFactor
|
|
else
|
|
ScaleFactor := 1;
|
|
ItemLeftTop := NonVisualComponentLeftTop(AComponent);
|
|
ItemLeft := ItemLeftTop.X - Diff.X;
|
|
ItemTop := ItemLeftTop.Y - Diff.Y;
|
|
ItemRight := ItemLeft + NonVisualCompWidth;
|
|
ItemBottom := ItemTop + NonVisualCompWidth;
|
|
if not FDDC.RectVisible(ItemLeft, ItemTop, ItemRight, ItemBottom) then
|
|
Exit;
|
|
|
|
IsSelected := Selection.IsSelected(AComponent);
|
|
|
|
if FSurface = nil then
|
|
begin
|
|
FSurface := TBitmap.Create;
|
|
FSurface.SetSize(Round(NonVisualCompWidth*ScaleFactor),
|
|
Round(NonVisualCompWidth*ScaleFactor));
|
|
FSurface.Canvas.Brush.Color := clBtnFace;
|
|
FSurface.Canvas.Pen.Width := 1;
|
|
end;
|
|
|
|
IconRect := Rect(0, 0, Round(NonVisualCompWidth*ScaleFactor),
|
|
Round(NonVisualCompWidth*ScaleFactor));
|
|
FSurface.Canvas.Frame3D(IconRect, 1, bvRaised);
|
|
FSurface.Canvas.FillRect(IconRect);
|
|
|
|
// draw component Name
|
|
if ShowComponentCaptions
|
|
and (((GetKeyState(VK_LBUTTON) and $80) = 0) or not IsSelected) then
|
|
begin
|
|
// workarounds gtk2 problem with DrawText on gc with GDK_INCLUDE_INFERIORS
|
|
// it uses pango drawing and this for some reason does not take subwindow_mode
|
|
// into account
|
|
Icon := TBitmap.Create;
|
|
try
|
|
TextSize := Icon.Canvas.TextExtent(AComponent.Name);
|
|
Icon.SetSize(Round(TextSize.cx * ScaleFactor), Round(TextSize.cy * ScaleFactor));
|
|
LCLIntf.SetCanvasScaleFactor(Icon.Canvas.Handle, ScaleFactor);
|
|
Icon.Canvas.Font.Assign(FDDC.Canvas.Font);
|
|
Icon.Canvas.Font.PixelsPerInch := FDDC.Canvas.Font.PixelsPerInch;
|
|
TextRect := Rect(0, 0, TextSize.cx, TextSize.cy);
|
|
if FDDC.Form <> nil then
|
|
Icon.Canvas.Brush.Color := FDDC.Form.Brush.Color
|
|
else
|
|
Icon.Canvas.Brush.Color := clBtnFace;
|
|
Icon.Canvas.FillRect(TextRect);
|
|
Icon.Canvas.Font.Color := clWindowText;
|
|
DrawText(Icon.Canvas.Handle, PChar(AComponent.Name), -1, TextRect,
|
|
DT_CENTER or DT_VCENTER or DT_SINGLELINE or DT_NOCLIP);
|
|
TextRect.Left := (ItemLeft + ItemRight - TextSize.cx) div 2;
|
|
TextRect.Top := (ItemBottom + NonVisualCompBorder + 2);
|
|
TextRect.Right := TextRect.Left + Icon.Width;
|
|
TextRect.Bottom := TextRect.Top + Icon.Height;
|
|
FDDC.Canvas.StretchDraw(TextRect, Icon);
|
|
finally
|
|
Icon.Free;
|
|
end;
|
|
end;
|
|
// draw component icon
|
|
if Assigned(FOnGetNonVisualCompIcon) then
|
|
begin
|
|
Icon := nil;
|
|
FOnGetNonVisualCompIcon(Self, AComponent, IL{%H-}, II{%H-});
|
|
if (IL<>nil) and (II>=0) then
|
|
begin
|
|
Res := IL.ResolutionForPPI[0, FDDC.Canvas.Font.PixelsPerInch, ScaleFactor];
|
|
InflateRect(IconRect,
|
|
- (IconRect.Right-IconRect.Left-Res.Resolution.Width) div 2,
|
|
- (IconRect.Bottom-IconRect.Top-Res.Resolution.Height) div 2);
|
|
Res.StretchDraw(FSurface.Canvas, II, IconRect);
|
|
end;
|
|
end;
|
|
FDDC.Canvas.StretchDraw(Rect(ItemLeft, ItemTop, ItemRight, ItemBottom), FSurface);
|
|
if (Selection.Count > 1) and IsSelected then
|
|
Selection.DrawMarkerAt(FDDC,
|
|
ItemLeft, ItemTop, NonVisualCompWidth, NonVisualCompWidth);
|
|
end;
|
|
|
|
procedure TDesigner.DrawNonVisualComponents(aDDC: TDesignerDeviceContext);
|
|
begin
|
|
if not ShowNonVisualComponents then exit;
|
|
FSurface := nil;
|
|
FDDC := aDDC;
|
|
DrawNonVisualComponent(FLookupRoot);
|
|
FDDC := nil;
|
|
FreeAndNil(FSurface);
|
|
end;
|
|
|
|
procedure TDesigner.DrawDesignerItems(OnlyIfNeeded: boolean);
|
|
var
|
|
DesignerDC: HDC;
|
|
begin
|
|
if WidgetSet.GetLCLCapability(lcCanDrawOutsideOnPaint) = 0 then Exit;
|
|
if OnlyIfNeeded and (not (dfNeedPainting in FFlags)) then exit;
|
|
Exclude(FFlags,dfNeedPainting);
|
|
|
|
if (Form=nil) or (not Form.HandleAllocated) then exit;
|
|
|
|
//writeln('TDesigner.DrawDesignerItems B painting');
|
|
DesignerDC := GetDesignerDC(Form.Handle);
|
|
DDC.SetDC(Form, Form, DesignerDC);
|
|
DDC.BeginPainting;
|
|
DoPaintDesignerItems;
|
|
DDC.EndPainting;
|
|
DDC.Clear;
|
|
ReleaseDesignerDC(Form.Handle, DesignerDC);
|
|
end;
|
|
|
|
procedure TDesigner.CheckFormBounds;
|
|
// check if the Form was moved or resized
|
|
// Note: During form loading the window manager can resize and position
|
|
// the Form. Such initial changes are ignored, by waiting and comparing
|
|
// not before the IDE becomes idle. When the IDE becomes the first time
|
|
// idle, the form bounds are stored and used as default.
|
|
// After that any change of the Form Bounds is treated as a user move
|
|
// and thus calls Modified.
|
|
var
|
|
NewFormBounds: TRect;
|
|
begin
|
|
NewFormBounds:=Form.BoundsRect;
|
|
if FDefaultFormBoundsValid then begin
|
|
if (not SameRect(@NewFormBounds,@FLastFormBounds))
|
|
and (not SameRect(@NewFormBounds,@FDefaultFormBounds)) then begin
|
|
//debugln('TDesigner.CheckFormBounds');
|
|
Modified;
|
|
if Selection.SelectionForm=Form then begin
|
|
Selection.CheckForLCLChanges(true);
|
|
end;
|
|
end;
|
|
end else begin
|
|
FDefaultFormBoundsValid:=true;
|
|
FDefaultFormBounds:=NewFormBounds;
|
|
end;
|
|
FLastFormBounds:=NewFormBounds;
|
|
end;
|
|
|
|
procedure TDesigner.DoPaintDesignerItems;
|
|
begin
|
|
// marker (multi selection markers)
|
|
if (Selection.SelectionForm = Form) and (Selection.Count > 1) then
|
|
begin
|
|
Selection.DrawMarkers(DDC);
|
|
end;
|
|
// non visual component icons
|
|
if not Assigned(IDEComponentsMaster)
|
|
or IDEComponentsMaster.DrawNonVisualComponents(FLookupRoot) then
|
|
DrawNonVisualComponents(DDC);
|
|
|
|
// guidelines and grabbers
|
|
if (Selection.SelectionForm=Form) then
|
|
begin
|
|
if EnvironmentGuiOpts.ShowGuideLines then
|
|
Selection.DrawGuideLines(DDC);
|
|
Selection.DrawGrabbers(DDC);
|
|
end;
|
|
// rubberband
|
|
if Selection.RubberBandActive and
|
|
((Selection.SelectionForm = Form) or (Selection.SelectionForm = nil)) then
|
|
begin
|
|
Selection.DrawRubberBand(DDC);
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.ComponentIsIcon(AComponent: TComponent): boolean;
|
|
begin
|
|
Result:=ComponentIsNonVisual(AComponent);
|
|
if Result and (Mediator<>nil) then
|
|
Result:=Mediator.ComponentIsIcon(AComponent);
|
|
end;
|
|
|
|
function TDesigner.GetParentFormRelativeClientOrigin(AComponent: TComponent): TPoint;
|
|
begin
|
|
if Mediator<>nil then begin
|
|
Result:=Mediator.GetComponentOriginOnForm(AComponent);
|
|
end else begin
|
|
Result:=DesignerProcs.GetParentFormRelativeClientOrigin(AComponent);
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.GetDesignedComponent(AComponent: TComponent): TComponent;
|
|
begin
|
|
Result:=AComponent;
|
|
if AComponent=Form then begin
|
|
Result:=FLookupRoot;
|
|
end else begin
|
|
while (Result<>nil)
|
|
and (Result<>FLookupRoot)
|
|
and (Result.Owner<>FLookupRoot)
|
|
and (Result is TControl) do
|
|
Result:=TControl(Result).Parent;
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.GetComponentEditorForSelection: TBaseComponentEditor;
|
|
begin
|
|
Result := nil;
|
|
if (Selection.Count <> 1) or
|
|
(Selection.SelectionForm <> Form) or
|
|
(not Selection[0].IsTComponent) then Exit;
|
|
Result := TheFormEditor.GetComponentEditor(TComponent(Selection[0].Persistent));
|
|
end;
|
|
|
|
procedure TDesigner.AddComponentEditorMenuItems(
|
|
AComponentEditor: TBaseComponentEditor; ClearOldOnes: boolean);
|
|
var
|
|
VerbCount, i: integer;
|
|
NewMenuCmd: TIDEMenuCommand;
|
|
begin
|
|
if ClearOldOnes then
|
|
DesignerMenuSectionComponentEditor.Clear;
|
|
|
|
if (AComponentEditor = nil) or (DesignerMenuSectionComponentEditor = nil) then
|
|
Exit;
|
|
|
|
VerbCount := AComponentEditor.GetVerbCount;
|
|
for i := 0 to VerbCount - 1 do
|
|
begin
|
|
NewMenuCmd:=RegisterIDEMenuCommand(DesignerMenuSectionComponentEditor,
|
|
'ComponentEditorVerMenuItem' + IntToStr(i),
|
|
AComponentEditor.GetVerb(i),
|
|
@ComponentEditorVerbMenuItemClick);
|
|
if NewMenuCmd.MenuItem<>nil then
|
|
AComponentEditor.PrepareItem(i, NewMenuCmd.MenuItem);
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.NonVisualComponentAtPos(X, Y: integer): TComponent;
|
|
var
|
|
s: TComponentSearch;
|
|
begin
|
|
// Note: Do not check ShowNonVisualComponents
|
|
s := TComponentSearch.Create(nil);
|
|
try
|
|
s.MinClass := TComponent;
|
|
s.AtPos := Point(X,Y);
|
|
s.IgnoreHidden := true;
|
|
s.OnlyNonVisual := true;
|
|
s.Search(FLookupRoot);
|
|
s.Mediator := Mediator;
|
|
Result := s.Best;
|
|
finally
|
|
s.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.MoveNonVisualComponentIntoForm(AComponent: TComponent);
|
|
var
|
|
X, Y: SmallInt;
|
|
begin
|
|
DesignInfoToLeftTop(AComponent.DesignInfo, X, Y);
|
|
AComponent.DesignInfo := LeftTopToDesignInfo(X, Y);
|
|
end;
|
|
|
|
procedure TDesigner.MoveNonVisualComponentsIntoForm;
|
|
var
|
|
i: Integer;
|
|
AComponent: TComponent;
|
|
begin
|
|
for i:=0 to FLookupRoot.ComponentCount-1 do begin
|
|
AComponent:=FLookupRoot.Components[i];
|
|
if ComponentIsIcon(AComponent) then begin
|
|
MoveNonVisualComponentIntoForm(AComponent);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TDesigner.ComponentClassAtPos(const AClass: TComponentClass;
|
|
const APos: TPoint; const UseRootAsDefault, IgnoreHidden: boolean): TComponent;
|
|
var
|
|
s: TComponentSearch;
|
|
MediatorFlags: TDMCompAtPosFlags;
|
|
begin
|
|
if Mediator <> nil then
|
|
begin
|
|
MediatorFlags := [];
|
|
if IgnoreHidden then
|
|
Include(MediatorFlags, dmcapfOnlyVisible);
|
|
Result := Mediator.ComponentAtPos(APos,AClass,MediatorFlags);
|
|
end
|
|
else
|
|
begin
|
|
s := TComponentSearch.Create(nil);
|
|
try
|
|
s.AtPos := APos;
|
|
s.MinClass := AClass;
|
|
s.IgnoreHidden := IgnoreHidden;
|
|
s.IgnoreNonVisual := not ShowNonVisualComponents;
|
|
s.Search(FLookupRoot);
|
|
s.Mediator := Mediator;
|
|
Result := s.Best;
|
|
finally
|
|
s.Free;
|
|
end;
|
|
end;
|
|
if (Result = nil) and UseRootAsDefault and (FLookupRoot.InheritsFrom(AClass)) then
|
|
Result := LookupRoot;
|
|
end;
|
|
|
|
procedure TDesigner.SetTempCursor(ARoot: TWinControl; ACursor: TCursor);
|
|
|
|
procedure Traverse(ARoot: TWinControl);
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := 0 to ARoot.ControlCount - 1 do
|
|
begin
|
|
ARoot.Controls[i].SetTempCursor(ACursor);
|
|
if ARoot.Controls[i] is TWinControl then
|
|
Traverse(TWinControl(ARoot.Controls[i]));
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Traverse(ARoot);
|
|
ARoot.SetTempCursor(ACursor);
|
|
end;
|
|
|
|
function TDesigner.WinControlAtPos(x, y: integer; UseRootAsDefault,
|
|
IgnoreHidden: boolean): TWinControl;
|
|
begin
|
|
Result := TWinControl(ComponentClassAtPos(TWinControl, Point(x,y),
|
|
UseRootAsDefault, IgnoreHidden));
|
|
end;
|
|
|
|
function TDesigner.ControlAtPos(x, y: integer; UseRootAsDefault,
|
|
IgnoreHidden: boolean): TControl;
|
|
begin
|
|
Result := TControl(ComponentClassAtPos(TControl, Point(x,y), UseRootAsDefault,
|
|
IgnoreHidden));
|
|
end;
|
|
|
|
function TDesigner.ComponentAtPos(x, y: integer; UseRootAsDefault,
|
|
IgnoreHidden: boolean): TComponent;
|
|
begin
|
|
Result := ComponentClassAtPos(TComponent, Point(x,y), UseRootAsDefault,
|
|
IgnoreHidden);
|
|
end;
|
|
|
|
procedure TDesigner.BuildPopupMenu;
|
|
begin
|
|
if FDesignerPopupMenu = nil then
|
|
begin
|
|
FDesignerPopupMenu:=TPopupMenu.Create(nil);
|
|
with FDesignerPopupMenu do
|
|
begin
|
|
Name := 'DesignerPopupmenu';
|
|
OnPopup := @DesignerPopupMenuPopup;
|
|
Images := IDEImages.Images_16;
|
|
end;
|
|
end;
|
|
// assign the root TMenuItem to the registered menu root.
|
|
// This will automatically create all registered items
|
|
{$IFDEF VerboseMenuIntf}
|
|
FDesignerPopupMenu.Items.WriteDebugReport('TSourceNotebook.BuildPopupMenu ');
|
|
DesignerMenuRoot.ConsistencyCheck;
|
|
{$ENDIF}
|
|
DesignerMenuRoot.MenuItem := FDesignerPopupMenu.Items;
|
|
|
|
DesignerMenuAlign.OnClick := @AlignPopupMenuClick;
|
|
DesignerMenuMirrorHorizontal.OnClick := @MirrorHorizontalPopupMenuClick;
|
|
DesignerMenuMirrorVertical.OnClick := @MirrorVerticalPopupMenuClick;
|
|
DesignerMenuScale.OnClick := @ScalePopupMenuClick;
|
|
DesignerMenuSize.OnClick := @SizePopupMenuClick;
|
|
DesignerMenuReset.OnClick := @ResetPopupMenuClick;
|
|
|
|
DesignerMenuAnchorEditor.OnClick:=@AnchorEditorMenuClick;
|
|
DesignerMenuTabOrder.OnClick:=@TabOrderMenuClick;
|
|
DesignerMenuOrderMoveToFront.OnClick := @OrderMoveToFrontMenuClick;
|
|
DesignerMenuOrderMoveToFront.MenuItem.ShortCut :=
|
|
EditorOpts.KeyMap.CommandToShortCut(ecDesignerMoveToFront);
|
|
DesignerMenuOrderMoveToBack.OnClick := @OrderMoveToBackMenuClick;
|
|
DesignerMenuOrderMoveToBack.MenuItem.ShortCut :=
|
|
EditorOpts.KeyMap.CommandToShortCut(ecDesignerMoveToBack);
|
|
DesignerMenuOrderForwardOne.OnClick := @OrderForwardOneMenuClick;
|
|
DesignerMenuOrderForwardOne.MenuItem.ShortCut :=
|
|
EditorOpts.KeyMap.CommandToShortCut(ecDesignerForwardOne);
|
|
DesignerMenuOrderBackOne.OnClick := @OrderBackOneMenuClick;
|
|
DesignerMenuOrderBackOne.MenuItem.ShortCut :=
|
|
EditorOpts.KeyMap.CommandToShortCut(ecDesignerBackOne);
|
|
|
|
DesignerMenuCut.OnClick:=@CutMenuClick;
|
|
DesignerMenuCopy.OnClick:=@CopyMenuClick;
|
|
DesignerMenuPaste.OnClick:=@PasteMenuClick;
|
|
DesignerMenuDeleteSelection.OnClick:=@DeleteSelectionMenuClick;
|
|
DesignerMenuSelectAll.OnClick:=@SelectAllMenuClick;
|
|
|
|
DesignerMenuChangeClass.OnClick:=@ChangeClassMenuClick;
|
|
DesignerMenuChangeParent.OnClick:=@ChangeParentMenuClick;
|
|
DesignerMenuViewLFM.OnClick:=@ViewLFMMenuClick;
|
|
DesignerMenuSaveAsXML.OnClick:=@SaveAsXMLMenuClick;
|
|
DesignerMenuCenterForm.OnClick:=@CenterFormMenuClick;
|
|
|
|
DesignerMenuShowNonVisualComponents.OnClick:=@ShowNonVisualComponentsMenuClick;
|
|
DesignerMenuShowNonVisualComponents.ShowAlwaysCheckable:=true;
|
|
DesignerMenuSnapToGridOption.OnClick:=@SnapToGridOptionMenuClick;
|
|
DesignerMenuSnapToGridOption.ShowAlwaysCheckable:=true;
|
|
DesignerMenuSnapToGuideLinesOption.OnClick:=@SnapToGuideLinesOptionMenuClick;
|
|
DesignerMenuSnapToGuideLinesOption.ShowAlwaysCheckable:=true;
|
|
DesignerMenuShowOptions.OnClick:=@ShowOptionsMenuItemClick;
|
|
end;
|
|
|
|
procedure TDesigner.DesignerPopupMenuPopup(Sender: TObject);
|
|
var
|
|
ControlSelIsNotEmpty,
|
|
LookupRootIsSelected,
|
|
OnlyNonVisualsAreSelected,
|
|
CompsAreSelected: boolean;
|
|
MultiCompsAreSelected: boolean;
|
|
OneControlSelected: Boolean;
|
|
SelectionVisible: Boolean;
|
|
SrcFile: TLazProjectFile;
|
|
UnitIsVirtual, DesignerCanCopy, HasChangeParentCandidates,
|
|
HasAncestorComponent: Boolean;
|
|
PersistentSelection: TPersistentSelectionList;
|
|
ChangeParentCandidates: TFPList;
|
|
i: Integer;
|
|
Item: TSelectedControl;
|
|
AComponent, AncestorComponent: TComponent;
|
|
begin
|
|
SrcFile:=LazarusIDE.GetProjectFileWithDesigner(Self);
|
|
ControlSelIsNotEmpty:=(Selection.Count>0)
|
|
and (Selection.SelectionForm=Form);
|
|
LookupRootIsSelected:=Selection.LookupRootSelected;
|
|
OnlyNonVisualsAreSelected := Selection.OnlyNonVisualPersistentsSelected;
|
|
SelectionVisible:=not Selection.OnlyInvisiblePersistentsSelected;
|
|
CompsAreSelected:=ControlSelIsNotEmpty and SelectionVisible
|
|
and not LookupRootIsSelected;
|
|
OneControlSelected := ControlSelIsNotEmpty and not Selection[0].IsNonVisualComponent;
|
|
MultiCompsAreSelected := CompsAreSelected and (Selection.Count>1);
|
|
UnitIsVirtual:=(SrcFile=nil) or not FilenameIsAbsolute(SrcFile.Filename);
|
|
PersistentSelection:=TPersistentSelectionList.Create;
|
|
try
|
|
Selection.GetSelection(PersistentSelection);
|
|
|
|
ChangeParentCandidates:=GetChangeParentCandidates(GlobalDesignHook,PersistentSelection);
|
|
HasChangeParentCandidates:=ChangeParentCandidates.Count>0;
|
|
FreeAndNil(ChangeParentCandidates);
|
|
|
|
HasAncestorComponent:=false;
|
|
for i:=0 to Selection.Count-1 do
|
|
begin
|
|
Item:=Selection[i];
|
|
AComponent:=TComponent(Item.Persistent);
|
|
AncestorComponent:=TheFormEditor.GetAncestorInstance(AComponent);
|
|
if AncestorComponent<>nil then
|
|
begin
|
|
HasAncestorComponent:=true;
|
|
break;
|
|
end;
|
|
end;
|
|
finally
|
|
FreeAndNil(PersistentSelection);
|
|
end;
|
|
|
|
AddComponentEditorMenuItems(PopupMenuComponentEditor,true);
|
|
|
|
DesignerMenuAlign.Enabled := CompsAreSelected and not OnlyNonVisualsAreSelected;
|
|
DesignerMenuMirrorHorizontal.Enabled := MultiCompsAreSelected and not OnlyNonVisualsAreSelected;
|
|
DesignerMenuMirrorVertical.Enabled := MultiCompsAreSelected and not OnlyNonVisualsAreSelected;
|
|
DesignerMenuScale.Enabled := CompsAreSelected and not OnlyNonVisualsAreSelected;
|
|
DesignerMenuSize.Enabled := CompsAreSelected and not OnlyNonVisualsAreSelected;
|
|
DesignerMenuReset.Enabled := HasAncestorComponent;
|
|
|
|
DesignerMenuSectionZOrder.Enabled := CompsAreSelected and not OnlyNonVisualsAreSelected;
|
|
DesignerMenuOrderMoveToFront.Enabled := OneControlSelected and not OnlyNonVisualsAreSelected;
|
|
DesignerMenuOrderMoveToBack.Enabled := OneControlSelected and not OnlyNonVisualsAreSelected;
|
|
DesignerMenuOrderForwardOne.Enabled := OneControlSelected and not OnlyNonVisualsAreSelected;
|
|
DesignerMenuOrderBackOne.Enabled := OneControlSelected and not OnlyNonVisualsAreSelected;
|
|
|
|
DesignerCanCopy := CanCopy;
|
|
DesignerMenuCut.Enabled := DesignerCanCopy;
|
|
DesignerMenuCopy.Enabled := DesignerCanCopy;
|
|
DesignerMenuPaste.Enabled := CanPaste;
|
|
DesignerMenuDeleteSelection.Enabled := CompsAreSelected;
|
|
|
|
DesignerMenuChangeClass.Enabled := CompsAreSelected and (Selection.Count = 1);
|
|
// Disable ViewLFM menu item for virtual units. There is no form file yet.
|
|
DesignerMenuViewLFM.Enabled := not UnitIsVirtual;
|
|
DesignerMenuChangeParent.Enabled := HasChangeParentCandidates;
|
|
DesignerMenuSnapToGridOption.Checked := EnvironmentGuiOpts.SnapToGrid;
|
|
DesignerMenuShowNonVisualComponents.Checked := ShowNonVisualComponents;
|
|
DesignerMenuSnapToGuideLinesOption.Checked := EnvironmentGuiOpts.SnapToGuideLines;
|
|
end;
|
|
|
|
procedure TDesigner.AlignPopupMenuClick(Sender: TObject);
|
|
var
|
|
HorizAlignment, VertAlignment: TComponentAlignment;
|
|
HorizAlignID, VertAlignID: integer;
|
|
begin
|
|
if ShowAlignComponentsDialog(HorizAlignID,VertAlignID)=mrOk then
|
|
begin
|
|
case HorizAlignID of
|
|
1: HorizAlignment:=csaSides1;
|
|
2: HorizAlignment:=csaCenters;
|
|
3: HorizAlignment:=csaSides2;
|
|
4: HorizAlignment:=csaCenterInWindow;
|
|
5: HorizAlignment:=csaSpaceEqually;
|
|
6: HorizAlignment:=csaSide1SpaceEqually;
|
|
7: HorizAlignment:=csaSide2SpaceEqually;
|
|
else HorizAlignment:=csaNone; // value=0, this prevents compiler warning.
|
|
end;
|
|
case VertAlignID of
|
|
1: VertAlignment:=csaSides1;
|
|
2: VertAlignment:=csaCenters;
|
|
3: VertAlignment:=csaSides2;
|
|
4: VertAlignment:=csaCenterInWindow;
|
|
5: VertAlignment:=csaSpaceEqually;
|
|
6: VertAlignment:=csaSide1SpaceEqually;
|
|
7: VertAlignment:=csaSide2SpaceEqually;
|
|
else VertAlignment:=csaNone; // value=0, this prevents compiler warning.
|
|
end;
|
|
Selection.AlignComponents(HorizAlignment,VertAlignment);
|
|
Modified;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.MirrorHorizontalPopupMenuClick(Sender: TObject);
|
|
begin
|
|
Selection.MirrorHorizontal;
|
|
Modified;
|
|
end;
|
|
|
|
procedure TDesigner.MirrorVerticalPopupMenuClick(Sender: TObject);
|
|
begin
|
|
Selection.MirrorVertical;
|
|
Modified;
|
|
end;
|
|
|
|
procedure TDesigner.ScalePopupMenuClick(Sender: TObject);
|
|
var
|
|
ScaleInPercent: integer;
|
|
begin
|
|
if ShowScaleComponentsDialog(ScaleInPercent)=mrOk then
|
|
begin
|
|
Selection.ScaleComponents(ScaleInPercent);
|
|
Modified;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.SizePopupMenuClick(Sender: TObject);
|
|
var
|
|
HorizSizing, VertSizing: TComponentSizing;
|
|
HorizSizingID, VertSizingID: integer;
|
|
AWidth, AHeight: integer;
|
|
begin
|
|
if ShowSizeComponentsDialog(HorizSizingID,AWidth,VertSizingID,AHeight) = mrOk then
|
|
begin
|
|
case HorizSizingID of
|
|
1: HorizSizing:=cssShrinkToSmallest;
|
|
2: HorizSizing:=cssGrowToLargest;
|
|
3: HorizSizing:=cssFixed;
|
|
else HorizSizing:=cssNone; // value=0, this prevents compiler warning.
|
|
end;
|
|
case VertSizingID of
|
|
1: VertSizing:=cssShrinkToSmallest;
|
|
2: VertSizing:=cssGrowToLargest;
|
|
3: VertSizing:=cssFixed;
|
|
else VertSizing:=cssNone; // value=0, this prevents compiler warning.
|
|
end;
|
|
Selection.SizeComponents(HorizSizing,AWidth,VertSizing,AHeight);
|
|
Modified;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.ResetPopupMenuClick(Sender: TObject);
|
|
var
|
|
ResetComps: TFPList;
|
|
HasChanged: Boolean;
|
|
|
|
procedure ResetControl(AControl: TControl; Recursive: boolean);
|
|
var
|
|
Ancestor: TControl;
|
|
i: Integer;
|
|
OldBounds: TRect;
|
|
NewBounds: TRect;
|
|
begin
|
|
if ResetComps.IndexOf(AControl)>=0 then exit;
|
|
ResetComps.Add(AControl);
|
|
Ancestor:=TControl(TheFormEditor.GetAncestorInstance(AControl));
|
|
if not (Ancestor is TControl) then exit;
|
|
OldBounds:=AControl.BoundsRect;
|
|
NewBounds:=Ancestor.BoundsRect;
|
|
if not SameRect(@OldBounds,@NewBounds) then begin
|
|
AControl.BoundsRect:=NewBounds;
|
|
HasChanged:=true;
|
|
end;
|
|
if Recursive and (AControl is TWinControl) then begin
|
|
for i:=0 to TWinControl(AControl).ControlCount-1 do
|
|
ResetControl(TWinControl(AControl).Controls[i],true);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
MsgResult: TModalResult;
|
|
i: Integer;
|
|
Item: TSelectedControl;
|
|
AComponent: TComponent;
|
|
AncestorComponent: TComponent;
|
|
begin
|
|
MsgResult:=IDEQuestionDialog(lisReset,
|
|
lisResetLeftTopWidthHeightOfSelectedComponentsToTheir,
|
|
mtConfirmation, [mrYes, lisSelected,
|
|
mrYesToAll, lisSelectedAndChildControls,
|
|
mrCancel]);
|
|
if not (MsgResult in [mrYes,mrYesToAll]) then exit;
|
|
HasChanged:=false;
|
|
Form.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TDesigner.OnResetPopupMenuClick'){$ENDIF};
|
|
ResetComps:=TFPList.Create;
|
|
try
|
|
for i:=0 to Selection.Count-1 do begin
|
|
Item:=Selection[i];
|
|
if Item.IsTControl then begin
|
|
ResetControl(TControl(Item.Persistent),MsgResult=mrYesToAll);
|
|
end else if Item.IsTComponent then begin
|
|
AComponent:=TComponent(Item.Persistent);
|
|
if ResetComps.IndexOf(AComponent)>=0 then continue;
|
|
ResetComps.Add(AComponent);
|
|
if Item.IsNonVisualComponent then begin
|
|
AncestorComponent:=TheFormEditor.GetAncestorInstance(AComponent);
|
|
if AncestorComponent=nil then continue;
|
|
if AComponent.DesignInfo=AncestorComponent.DesignInfo then continue;
|
|
AComponent.DesignInfo:=AncestorComponent.DesignInfo;
|
|
HasChanged:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
ResetComps.Free;
|
|
Form.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TDesigner.OnResetPopupMenuClick'){$ENDIF};
|
|
if HasChanged then
|
|
Modified;
|
|
end;
|
|
end;
|
|
|
|
procedure TDesigner.OrderMoveToFrontMenuClick(Sender: TObject);
|
|
begin
|
|
DoChangeZOrder(coaMoveToFront);
|
|
end;
|
|
|
|
procedure TDesigner.OrderMoveToBackMenuClick(Sender: TObject);
|
|
begin
|
|
DoChangeZOrder(coaMoveToBack);
|
|
end;
|
|
|
|
procedure TDesigner.OrderForwardOneMenuClick(Sender: TObject);
|
|
begin
|
|
DoChangeZOrder(coaForwardOne);
|
|
end;
|
|
|
|
procedure TDesigner.OrderBackOneMenuClick(Sender: TObject);
|
|
begin
|
|
DoChangeZOrder(coaBackOne);
|
|
end;
|
|
|
|
procedure TDesigner.HintTimer(Sender: TObject);
|
|
|
|
function GetComponentHintText(AComponent: TComponent): String;
|
|
const
|
|
HintNameStr = '%s: %s';
|
|
HintPositionStr = 'Position: %d, %d';
|
|
HintSizeStr = 'Size: %d x %d';
|
|
HintTabStr = 'TabStop: %s; TabOrder: %d';
|
|
var
|
|
AControl: TControl absolute AComponent;
|
|
AWinControl: TWinControl absolute AComponent;
|
|
AComponentEditor: TBaseComponentEditor;
|
|
S: String;
|
|
begin
|
|
// component name and classname
|
|
Result := Format(HintNameStr, [AComponent.Name, AComponent.ClassName]);
|
|
// component position
|
|
Result := Result + LineEnding + Format(HintPositionStr, [GetComponentLeft(AComponent), GetComponentTop(AComponent)]);
|
|
if AComponent is TControl then // more info for controls
|
|
begin
|
|
// size
|
|
Result := Result + '; ' + Format(HintSizeStr, [AControl.Width, AControl.Height]);
|
|
// and TabStop, TabOrder for TWinControl
|
|
if (AComponent is TWinControl) and not (AComponent = Form) then
|
|
Result := Result + LineEnding + Format(HintTabStr, [BoolToStr(AWinControl.TabStop, True), AWinControl.TabOrder]);
|
|
end;
|
|
AComponentEditor := TheFormEditor.GetComponentEditor(AComponent);
|
|
if Assigned(AComponentEditor) then
|
|
begin
|
|
S := AComponentEditor.GetCustomHint;
|
|
if S <> '' then
|
|
Result := Result + LineEnding + S;
|
|
AComponentEditor.Free;
|
|
end;
|
|
end;
|
|
|
|
function ParentComponent(AComponent: TComponent): TComponent;
|
|
begin
|
|
Result := AComponent.GetParentComponent;
|
|
if (Result = nil) and ComponentIsIcon(AComponent) then
|
|
Result := AComponent.Owner;
|
|
end;
|
|
|
|
function GetSelectionSizeHintText: String;
|
|
begin
|
|
Result := Format('%d x %d', [Selection.Width, Selection.Height]);
|
|
end;
|
|
|
|
function GetSelectionPosHintText: String;
|
|
var
|
|
BaseParent, TestParent: TComponent;
|
|
BaseFound: Boolean;
|
|
i: integer;
|
|
P: TPoint;
|
|
begin
|
|
BaseFound := Selection[0].IsTComponent;
|
|
// search for one parent of our selection
|
|
if BaseFound then
|
|
begin
|
|
BaseParent := ParentComponent(TComponent(Selection[0].Persistent));
|
|
BaseFound := BaseParent is TWinControl;
|
|
if BaseFound then
|
|
begin
|
|
for i := 1 to Selection.Count - 1 do
|
|
begin
|
|
if Selection[0].IsTComponent then
|
|
TestParent := ParentComponent(TComponent(Selection[0].Persistent))
|
|
else
|
|
TestParent := nil;
|
|
if TestParent <> BaseParent then
|
|
begin
|
|
BaseFound := False;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
P := Point(Selection.Left, Selection.Top);
|
|
if BaseFound then
|
|
P := TWinControl(BaseParent).ScreenToClient(Form.ClientToScreen(P));
|
|
Result := Format('%d, %d', [P.X, P.Y]);
|
|
end;
|
|
|
|
var
|
|
Rect: TRect;
|
|
AHint: String;
|
|
Position, ClientPos: TPoint;
|
|
AWinControl: TWinControl;
|
|
AComponent: TComponent;
|
|
begin
|
|
FHintTimer.Enabled := False;
|
|
if ([dfShowEditorHints]*FFlags=[]) or (Form=nil) then exit;
|
|
|
|
Position := Mouse.CursorPos;
|
|
if not (dfHasSized in FFlags) then
|
|
begin
|
|
AWinControl := FindLCLWindow(Position);
|
|
if not (Assigned(AWinControl)) then Exit;
|
|
if GetDesignerForm(AWinControl) <> Form then exit;
|
|
|
|
// search a component at the position
|
|
ClientPos := Form.ScreenToClient(Position);
|
|
AComponent := ComponentAtPos(ClientPos.X,ClientPos.Y,true,true);
|
|
if not Assigned(AComponent) then
|
|
AComponent := AWinControl;
|
|
AComponent := GetDesignedComponent(AComponent);
|
|
if AComponent = nil then exit;
|
|
AHint := GetComponentHintText(AComponent);
|
|
end
|
|
else
|
|
begin
|
|
// components are either resize or move
|
|
if (Selection.LookupRoot <> Form) or (Selection.Count = 0) then
|
|
Exit;
|
|
if Selection.ActiveGrabber <> nil then
|
|
AHint := GetSelectionSizeHintText
|
|
else
|
|
AHint := GetSelectionPosHintText;
|
|
end;
|
|
|
|
Rect := FHintWindow.CalcHintRect(0, AHint, Nil); //no maxwidth
|
|
Rect.Left := Position.X + 15;
|
|
Rect.Top := Position.Y + 15;
|
|
Rect.Right := Rect.Left + Rect.Right;
|
|
Rect.Bottom := Rect.Top + Rect.Bottom;
|
|
FHintWindow.ActivateHint(Rect, AHint);
|
|
end;
|
|
|
|
procedure TDesigner.SetSnapToGrid(const AValue: boolean);
|
|
begin
|
|
if SnapToGrid=AValue then exit;
|
|
EnvironmentGuiOpts.SnapToGrid:=AValue;
|
|
end;
|
|
|
|
procedure TDesigner.DoOnForwardKeyToObjectInspector(Sender: TObject;
|
|
Key: TUTF8Char);
|
|
begin
|
|
if Assigned(FOnForwardKeyToOI) then
|
|
FOnForwardKeyToOI(Self, Key);
|
|
end;
|
|
|
|
function TDesigner.DoFormActivated(Active: boolean): boolean;
|
|
begin
|
|
if Active then begin
|
|
// designer form was activated.
|
|
if Assigned(FOnActivated) then FOnActivated(Self);
|
|
end else begin
|
|
// designer form deactivated
|
|
end;
|
|
Result:=false; // pass message to form, needed for focussing
|
|
end;
|
|
|
|
function TDesigner.DoFormCloseQuery: boolean;
|
|
begin
|
|
if Assigned(FOnCloseQuery) then FOnCloseQuery(Self);
|
|
Result:=true; // do not pass to form
|
|
end;
|
|
|
|
function TDesigner.GetPropertyEditorHook: TPropertyEditorHook;
|
|
begin
|
|
Result:=TheFormEditor.PropertyEditorHook;
|
|
end;
|
|
|
|
end.
|
|
|